Добавить штрих‑код в PDF на Java – Подпись и проверка с GroupDocs

Если вам нужен быстрый визуальный способ помечать документы для внутренних рабочих процессов, добавление штрих‑кода в PDF на Java — идеальное решение. С помощью GroupDocs.Signature вы можете внедрять, проверять, искать, обновлять и удалять подписи‑штрих‑коды без нагрузки полной PKI‑основанной цифровой подписи. Этот учебник проведёт вас через каждый шаг, от настройки окружения до готовых к продакшену рекомендаций.

Быстрые ответы

  • Какая библиотека добавляет штрих‑код в PDF на Java? GroupDocs.Signature for Java.
  • Могу ли я подписывать PDF, Word и изображения? Да — API поддерживает более 30 форматов.
  • Нужна ли лицензия для разработки? Временная 30‑дневная лицензия бесплатна; полная лицензия требуется для продакшена.
  • Сколько строк кода нужно, чтобы подписать PDF? Всего две строки: создать объект Signature и вызвать sign.
  • Чувствительна ли проверка штрих‑кода к регистру? Да — текст должен точно совпадать, включая регистр и пробелы.

Что такое add barcode pdf java?

add barcode pdf java относится к процессу использования Java‑кода для внедрения штрих‑кода (например, Code128, QR или Data Matrix) в PDF‑файл. Эта техника предоставляет машинно‑читаемую метку, которую можно сканировать или программно проверять, обеспечивая быстрый учёт документов во внутренних системах.

Почему использовать подписи‑штрих‑коды вместо полных цифровых подписей?

Подписи‑штрих‑коды на 30‑50 % быстрее генерировать и проверять, чем PKI‑основанные цифровые подписи, и они надёжно работают после цикла печать‑сканирование. Кроме того, они не требуют управления сертификатами, что делает их идеальными для высокообъёмных внутренних процессов, где криптографическое доказательство не является обязательным.

Предварительные требования

  • Java Development Kit (JDK) 8+ – рекомендуется Java 11 или 17 для лучшей производительности.
  • IDE – IntelliJ IDEA или Eclipse (в примерах используется синтаксис IntelliJ).
  • Инструмент сборки – Maven или Gradle для управления зависимостями.

Добавление GroupDocs.Signature в проект

Самый простой способ начать — добавить библиотеку через ваш инструмент сборки. Вот как:

Пользователи Maven – добавьте следующее в ваш pom.xml:

<dependency>
    <groupId>com.groupdocs</groupId>
    <artifactId>groupdocs-signature</artifactId>
    <version>23.12</version>
</dependency>

Пользователи Gradle – добавьте следующее в ваш build.gradle:

implementation 'com.groupdocs:groupdocs-signature:23.12'

Прямая загрузка JAR – если вы предпочитаете ручную настройку, скачайте JAR с GroupDocs.Signature releases и добавьте его в classpath.

Получение лицензии

GroupDocs.Signature не бесплатен для продакшена, но у вас есть гибкие варианты:

Совет: начните с временной лицензии для разработки; она удалит водяные знаки после перехода на постоянный ключ.

Быстрая проверка окружения

После добавления зависимости выполните простой smoke‑test:

import com.groupdocs.signature.Signature;

public class SignatureTest {
    public static void main(String[] args) {
        try {
            Signature signature = new Signature("test.pdf");
            System.out.println("GroupDocs.Signature initialized successfully!");
            signature.dispose();
        } catch (Exception e) {
            System.err.println("Setup issue: " + e.getMessage());
        }
    }
}

Если программа запустилась без ошибок, ваше окружение готово. Если возникли проблемы, проверьте версию JDK и точную версию библиотеки, которую вы добавили.

Когда использовать подписи‑штрих‑коды

Подписи‑штрих‑коды проявляют себя в определённых сценариях:

  • Внутренние документооборотные процессы – отслеживание счетов, заказов на покупку или служебных записок через цепочки согласования.
  • Обработка больших объёмов – подпись тысяч документов быстро; генерация штрих‑кода до 2× быстрее, чем полная цифровая подпись.
  • Переход печать‑скан – штрих‑коды выдерживают цикл печать‑скан, что делает их идеальными для гибридных бумажно‑цифровых процессов.
  • Интеграция с наследуемыми системами – существующие сканеры штрих‑кодов могут считывать метки без дополнительного ПО.
  • Аудиторские следы – внедрение идентификаторов транзакций или временных меток, которые аудиторы могут мгновенно проверить.

Избегайте штрих‑кодов для юридических контрактов, документов высокой безопасности или обмена с внешними партнёрами, где требуются PKI‑основанные цифровые подписи.

Настройка GroupDocs.Signature для Java

Определение базового класса

Класс Signature является точкой входа для всех операций подписи, проверки, поиска, обновления и удаления в GroupDocs.Signature.

import com.groupdocs.signature.Signature;

Базовая инициализация

Создайте объект Signature, указывающий на целевой файл:

Signature signature = new Signature("path/to/your/document.pdf");

Важные замечания

  • Пути могут быть абсолютными или относительными; используйте System.getProperty("user.home") для кросс‑платформенной совместимости.
  • GroupDocs поддерживает более 30 входных и выходных форматов, включая PDF, DOCX, XLSX, PPTX и PNG.
  • Всегда освобождайте ресурсы с помощью signature.dispose() или блока try‑with‑resources:
try (Signature signature = new Signature("path/to/your/document.pdf")) {
    // Your signing operations here
} catch (Exception e) {
    System.err.println("Error working with document: " + e.getMessage());
}

Теперь вы готовы добавлять штрих‑коды.

Как добавить штрих‑код в PDF на Java?

Загрузите исходный PDF с new Signature("input.pdf"), настройте объект BarcodeSignature (например, Code128 с текстом “John Smith”) и вызовите sign, чтобы получить подписанный файл — всё в трёх лаконичных строках кода. Такой подход позволяет внедрять машинно‑читаемую метку, сохраняя оригинальное расположение документа, и работает с любым поддерживаемым форматом, а не только с PDF.

Пошаговая реализация

1. Определите пути к файлам

Укажите расположения исходного и выходного файлов:

String filePath = "YOUR_DOCUMENT_DIRECTORY/sample.docx";
String outputFilePath = "YOUR_OUTPUT_DIRECTORY/signed_sample.docx";

2. Создайте обработчик подписи

Инициализируйте обработчик с исходным документом:

Signature signature = new Signature(filePath);

3. Настройте параметры штрих‑кода

Объект BarcodeSignature определяет визуальные и данные свойства штрих‑кода, который будет внедрён. Установите тип штрих‑кода, закодированный текст, позицию, размер, цвет и необязательный читаемый шрифт:

BarcodeSignOptions signOptions = new BarcodeSignOptions("John Smith", BarcodeTypes.Code128);
signOptions.setVerticalAlignment(VerticalAlignment.Top);
signOptions.setHorizontalAlignment(HorizontalAlignment.Center);
signOptions.setWidth(100);
signOptions.setHeight(40);
signOptions.setMargin(new java.awt.Rectangle(20, 20, 0, 0));
signOptions.setForeColor(Color.RED);

SignatureFont signatureFont = new SignatureFont();
signatureFont.setSize(12);
signatureFont.setFamilyName("Comic Sans MS");
signOptions.setFont(signatureFont);

Совет: если закодированная строка превышает 20 символов, увеличьте ширину штрих‑кода, чтобы избежать ошибок сканирования.

4. Примените подпись

Выполните операцию подписи и сгенерируйте выходной файл:

signature.sign(outputFilePath, signOptions);

5. Пример из реального мира

В системе согласования счетов вы можете внедрить идентификатор сотрудника‑утверждающего и временную метку:

String invoiceId = "INV-2025-0042";
String approver = "john.smith@company.com";
String timestamp = LocalDateTime.now().toString();

// Combine into barcode data
String barcodeData = invoiceId + "|" + approver + "|" + timestamp;

BarcodeSignOptions signOptions = new BarcodeSignOptions(barcodeData, BarcodeTypes.QR);
signOptions.setVerticalAlignment(VerticalAlignment.Bottom);
signOptions.setHorizontalAlignment(HorizontalAlignment.Right);

signature.sign(outputFilePath, signOptions);

Полученный PDF теперь содержит сканируемый штрих‑код, который кодирует всю необходимую мета‑информацию согласования.

Как проверить подпись‑штрих‑код в документе Java?

Метод Signature.verify проверяет документ на наличие совпадающих подпись‑штрих‑кодов согласно заданным параметрам, возвращая булево значение, указывающее, присутствует ли ожидаемый штрих‑код. Проверка полезна для автоматизированных процессов, где необходимо подтвердить, что документ обработан правильной стороной перед дальнейшими действиями.

Шаги проверки

1. Загрузите подписанный документ

Signature signature = new Signature("YOUR_OUTPUT_DIRECTORY/signed_sample.docx");

2. Установите критерии проверки

Определите точный текст, формат штрих‑кода и номер страницы, которые вы ожидаете:

BarcodeVerifyOptions verifyOptions = new BarcodeVerifyOptions();
verifyOptions.setAllPages(false); // Only check the first page
verifyOptions.setPageNumber(1);
verifyOptions.setEncodeType(BarcodeTypes.Code128);
verifyOptions.setText("John Smith");

3. Запустите проверку

Выполните проверку и обработайте результат:

boolean isValid = signature.verify(verifyOptions) != null;

if (isValid) {
    System.out.println("Document signature verified successfully!");
} else {
    System.out.println("Warning: Signature verification failed!");
}

Распространённый шаблон: чтобы просто подтвердить, что любой штрих‑код заданного типа существует, опустите фильтр setText:

BarcodeVerifyOptions flexibleOptions = new BarcodeVerifyOptions();
flexibleOptions.setAllPages(true);
flexibleOptions.setEncodeType(BarcodeTypes.Code128);
// No setText() call = accept any text with this barcode type

boolean hasAnySignature = signature.verify(flexibleOptions) != null;

Или проверьте только формат штрих‑кода без учёта содержимого:

BarcodeVerifyOptions formatOnly = new BarcodeVerifyOptions();
formatOnly.setEncodeType(BarcodeTypes.QR);
// Confirms a QR code exists, regardless of content

Внимание: проверка чувствительна к регистру и пробелам; всегда обрезайте и нормализуйте данные перед подписью.

Как искать подписи‑штрих‑коды в документе?

Метод Signature.search сканирует документ в поисках подписи‑штрих‑кодов, соответствующих переданным BarcodeSearchOptions, возвращая коллекцию, включающую расположение каждого штрих‑кода, номер страницы и закодированное значение. Эта возможность позволяет массово извлекать метаданные без ручного открытия каждого файла.

Рабочий процесс поиска

1. Инициализируйте поиск

Signature signature = new Signature("YOUR_OUTPUT_DIRECTORY/signed_sample.docx");

2. Настройте параметры поиска

Укажите диапазон страниц, тип штрих‑кода или текстовые фильтры:

BarcodeSearchOptions searchOptions = new BarcodeSearchOptions();
searchOptions.setAllPages(true); // Search the entire document

Опционально сузьте поиск для повышения производительности:

BarcodeSearchOptions narrowSearch = new BarcodeSearchOptions();
narrowSearch.setAllPages(false);
narrowSearch.setPageNumber(1); // Only search page 1
narrowSearch.setEncodeType(BarcodeTypes.Code128); // Only find Code128 barcodes

3. Выполните поиск

java.util.List<BarcodeSignature> signatures = signature.search(BarcodeSignature.class, searchOptions);

System.out.println("Found " + signatures.size() + " barcode signature(s)");

for (BarcodeSignature bcSignature : signatures) {
    System.out.println("Barcode Type: " + bcSignature.getEncodeType().getTypeName());
    System.out.println("Text: " + bcSignature.getText());
    System.out.println("Position: (" + bcSignature.getLeft() + ", " + bcSignature.getTop() + ")");
    System.out.println("Size: " + bcSignature.getWidth() + "x" + bcSignature.getHeight());
    System.out.println("---");
}

4. Пример извлечения данных

Получите данные согласования из каждого штрих‑кода в партии счетов:

List<BarcodeSignature> foundSignatures = signature.search(BarcodeSignature.class, searchOptions);

for (BarcodeSignature sig : foundSignatures) {
    String barcodeData = sig.getText();
    
    // Parse your custom format (remember our invoice example?)
    String[] parts = barcodeData.split("\\|");
    if (parts.length == 3) {
        String invoiceId = parts[0];
        String approver = parts[1];
        String timestamp = parts[2];
        
        System.out.println("Invoice " + invoiceId + " approved by " + approver + " at " + timestamp);
    }
}

Подсказка по производительности: для документов более 100 страниц всегда ограничивайте поиск только теми страницами, где действительно находятся штрих‑коды; это может сократить время выполнения до 70 %.

Как обновить существующую подпись‑штрих‑код?

Метод Signature.update изменяет визуальные атрибуты существующей подписи‑штрих‑кода — такие как позиция, размер или цвет — при сохранении оригинальных закодированных данных. Это удобно, когда после подписи требуется изменить макет.

Процедура обновления

1. Найдите подписи для обновления

Signature signature = new Signature("YOUR_OUTPUT_DIRECTORY/signed_sample.docx");

BarcodeSearchOptions searchOptions = new BarcodeSearchOptions();
searchOptions.setAllPages(true);

List<BarcodeSignature> signatures = signature.search(BarcodeSignature.class, searchOptions);

2. Измените нужные свойства

List<BarcodeSignature> signaturesToUpdate = new ArrayList<>();

if (!signatures.isEmpty()) {
    BarcodeSignature bcSignature = signatures.get(0); // Update the first found signature
    
    // Move it 100px right and 100px down
    bcSignature.setLeft(bcSignature.getLeft() + 100);
    bcSignature.setTop(bcSignature.getTop() + 100);
    
    // Make it bigger
    bcSignature.setWidth(200);
    bcSignature.setHeight(50);
    
    signaturesToUpdate.add(bcSignature);
}

Можно изменить сразу несколько атрибутов:

bcSignature.setLeft(50);
bcSignature.setTop(50);
bcSignature.setWidth(150);
bcSignature.setHeight(45);
// Note: You can't change the encoded text or barcode type with update
// For that, you'd need to delete and re-sign

3. Сохраните изменения

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
signature.update(outputStream, signaturesToUpdate);

// If you want to save to a file instead:
// signature.update("path/to/updated_document.docx", signaturesToUpdate);

4. Сценарий из реального мира

Переместите все подписи, чтобы они не перекрывали недавно добавленный логотип компании:

List<BarcodeSignature> allSignatures = signature.search(BarcodeSignature.class, searchOptions);
List<BarcodeSignature> toUpdate = new ArrayList<>();

for (BarcodeSignature sig : allSignatures) {
    // Move all signatures down by 50px to clear space for new logo
    if (sig.getTop() < 100) {
        sig.setTop(sig.getTop() + 50);
        toUpdate.add(sig);
    }
}

if (!toUpdate.isEmpty()) {
    signature.update(outputStream, toUpdate);
    System.out.println("Repositioned " + toUpdate.size() + " signature(s)");
}

Ограничение: изменение закодированного текста требует удаления и повторного вставления подписи.

Как удалить подписи‑штрих‑коды из документа?

Метод Signature.delete полностью удаляет выбранные подписи‑штрих‑коды из документа, стирая как визуальный элемент, так и закодированные данные. Используйте эту операцию при очистке тестовых подписей или когда штрих‑код больше не нужен.

Шаги удаления

1. Найдите подписи

Signature signature = new Signature("YOUR_OUTPUT_DIRECTORY/signed_sample.docx");

BarcodeSearchOptions searchOptions = new BarcodeSearchOptions();
searchOptions.setAllPages(true);

List<BarcodeSignature> signatures = signature.search(BarcodeSignature.class, searchOptions);

2. Удалите выбранные подписи

List<BarcodeSignature> signaturesToDelete = new ArrayList<>();

// Delete all Code128 signatures (for example)
for (BarcodeSignature sig : signatures) {
    if (sig.getEncodeType().equals(BarcodeTypes.Code128)) {
        signaturesToDelete.add(sig);
    }
}

if (!signaturesToDelete.isEmpty()) {
    boolean result = signature.delete(signaturesToDelete);
    
    if (result) {
        System.out.println("Successfully deleted " + signaturesToDelete.size() + " signature(s)");
    } else {
        System.err.println("Failed to delete signatures");
    }
}

3. Пример условного удаления

Удалите только штрих‑коды старше определённой временной метки (при условии, что метка включена в закодированный текст):

LocalDateTime cutoffDate = LocalDateTime.now().minusDays(30);
List<BarcodeSignature> outdatedSignatures = new ArrayList<>();

for (BarcodeSignature sig : signatures) {
    try {
        String text = sig.getText();
        // Assuming format like "data|timestamp"
        String[] parts = text.split("\\|");
        if (parts.length > 1) {
            LocalDateTime sigDate = LocalDateTime.parse(parts[1]);
            if (sigDate.isBefore(cutoffDate)) {
                outdatedSignatures.add(sig);
            }
        }
    } catch (Exception e) {
        // Skip signatures that don't match our format
        continue;
    }
}

if (!outdatedSignatures.isEmpty()) {
    signature.delete(outdatedSignatures);
    System.out.println("Removed " + outdatedSignatures.size() + " outdated signature(s)");
}

Внимание: удаление нельзя отменить; всегда работайте с копией производственных файлов во время тестирования.

4. Шаблон пакетного удаления

Очистите тестовые подписи перед релизом:

// Remove all signatures containing "TEST" in their text
List<BarcodeSignature> testSignatures = signatures.stream()
    .filter(sig -> sig.getText().toUpperCase().contains("TEST"))
    .collect(Collectors.toList());

if (!testSignatures.isEmpty()) {
    signature.delete(testSignatures);
    System.out.println("Cleaned up " + testSignatures.size() + " test signature(s)");
}

Типы штрих‑кодов: практическое руководство

Выбор правильного штрих‑кода влияет на надёжность сканирования, ёмкость данных и совместимость с принтером.

Code128 (самый популярный выбор)

  • Когда использовать: буквенно‑цифровые данные, высокая плотность, печатные документы.
  • Плюсы: компактный, работает со стандартными сканерами, чётко печатается в небольших размерах.
  • Минусы: ограничен ASCII, менее устойчив к ошибкам, чем 2‑D коды.

Пример:

BarcodeSignOptions code128 = new BarcodeSignOptions("INV-2025-042", BarcodeTypes.Code128);

QR Code (лучший для мобильных)

  • Когда использовать: мобильное сканирование, большие объёмы данных (URL, JSON и т.д.), документы, подверженные повреждениям.
  • Плюсы: до 4 000 символов, встроенная коррекция ошибок, удобен для смартфонов.
  • Минусы: больший визуальный след, требует более высокого разрешения для небольших размеров.

Пример:

String jsonData = "{\"invoice\":\"INV-042\",\"amount\":1500.00,\"approver\":\"john@company.com\"}";
BarcodeSignOptions qrCode = new BarcodeSignOptions(jsonData, BarcodeTypes.QR);

Code39 (надёжный старый вариант)

  • Когда использовать: среды со старыми сканерами, необходимость в читаемом человеком тексте.
  • Плюсы: широкая поддержка наследуемых систем, простой формат, без контрольной суммы.
  • Минусы: низкая плотность данных, ограниченный набор символов, занимает больше места.

Data Matrix (компактный мощный)

  • Когда использовать: крайне ограниченное пространство, маркировка крошечных предметов, высоко‑плотные данные.
  • Плюсы: очень компактный, сильная коррекция ошибок, работает на изогнутых поверхностях.
  • Минусы: требует высококачественной печати, менее распространённая поддержка сканеров.

Быстрое сравнение

Тип штрих‑кодаЁмкость данныхЛучшее применениеТипичный размер
Code128~100 символовУниверсальная маркировкаМалый
QR Code~4 000 символовМобильное сканирование, богатые данныеСредний
Code39~43 символаНаследуемое оборудованиеБольшой
Data Matrix~3 000 символовКрошечные пространства, промышленная маркировкаОчень маленький

Рекомендация: начните с Code128 для простых идентификаторов. Перейдите на QR, когда понадобится внедрять URL или большие полезные нагрузки. Используйте Code39 только для наследуемых интеграций.

Распространённые проблемы и решения

Проблема: «Штрих‑код не найден при проверке»

Симптомы: проверка возвращает false, хотя штрих‑код виден.

Типичные причины

  1. Несоответствие регистра (например, “John Smith” vs. “john smith”).
  2. Лишние пробелы в закодированном тексте.
  3. Неправильный тип штрих‑кода, указанный в параметрах проверки.
  4. Поиск на неверном номере страницы.

Решение: нормализуйте текст перед подписью и проверкой, и убедитесь, что setEncodeType соответствует оригинальному типу.

// Always normalize your data
String signatureText = originalText.trim().toLowerCase();

// When signing:
BarcodeSignOptions signOptions = new BarcodeSignOptions(signatureText, BarcodeTypes.Code128);

// When verifying:
BarcodeVerifyOptions verifyOptions = new BarcodeVerifyOptions();
verifyOptions.setText(signatureText); // Use the same normalized text
verifyOptions.setEncodeType(BarcodeTypes.Code128);

Проблема: «Штрих‑код выглядит размытым или нечитаемым»

Симптомы: напечатанные штрих‑коды не сканируются.

Причины

  • Размеры штрих‑кода слишком малы для объёма данных.
  • Низкое разрешение принтера.
  • Недостаточный контраст между цветом штрих‑кода и фоном.

Решение: увеличьте ширину/высоту штрих‑кода, используйте высококонтрастные цвета (чёрный на белом) и установите DPI принтера не менее 300 dpi.

// Increase size for longer strings
int dataLength = barcodeText.length();
int minWidth = Math.max(100, dataLength * 10); // 10px per character minimum

BarcodeSignOptions signOptions = new BarcodeSignOptions(barcodeText, BarcodeTypes.Code128);
signOptions.setWidth(minWidth);
signOptions.setHeight(60); // Taller = easier to scan

// Use high-contrast colors
signOptions.setForeColor(Color.BLACK); // Black on white is most reliable
signOptions.setBackColor(Color.WHITE);

Проблема: «OutOfMemoryError при работе с большими документами»

Симптомы: приложение падает при обработке PDF‑файлов со сотнями страниц.

Причина: библиотека загружает весь документ в память.

Решение: включите режим потоковой передачи и обрабатывайте страницы по частям.

// Process pages in batches instead of all at once
BarcodeSearchOptions searchOptions = new BarcodeSearchOptions();
searchOptions.setAllPages(false);

List<BarcodeSignature> allSignatures = new ArrayList<>();

for (int page = 1; page <= totalPages; page++) {
    searchOptions.setPageNumber(page);
    List<BarcodeSignature> pageSignatures = signature.search(BarcodeSignature.class, searchOptions);
    allSignatures.addAll(pageSignatures);
    
    // Process and clear as you go
    if (page % 10 == 0) {
        System.gc(); // Suggest garbage collection every 10 pages
    }
}

Проблема: «Позиция подписи отличается в разных типах документов»

Симптомы: штрих‑коды появляются в разных местах при подписи PDF‑файлов и Word‑документов.

Причина: в PDF используется система координат снизу‑слева, а в Word — сверху‑слева.

Решение: определяйте тип документа и применяйте соответствующее преобразование координат.

// Use relative positioning instead of absolute
signOptions.setVerticalAlignment(VerticalAlignment.Bottom);
signOptions.setHorizontalAlignment(HorizontalAlignment.Right);

// Then use margins for fine-tuning
Padding margin = new Padding();
margin.setRight(50); // 50px from right edge
margin.setBottom(50); // 50px from bottom
signOptions.setMargin(margin);

// This works consistently across PDF, DOCX, XLSX, etc.

Проблема: «Обновлённые подписи теряют форматирование»

Симптомы: после вызова update цвет или шрифт штрих‑кода меняются неожиданно.

Причина: не все визуальные свойства сохраняются при обновлении.

Решение: повторно применяйте любые визуальные настройки после вызова update.

// When updating, explicitly set all visual properties
bcSignature.setLeft(newLeft);
bcSignature.setTop(newTop);
bcSignature.setWidth(originalWidth); // Don't forget these!
bcSignature.setHeight(originalHeight);
// Note: Color and font can't be updated - you'd need to delete and re-sign

Лучшие практики для продакшена

Оптимизация производительности

  1. Повторное использование объектов Signature – создавайте один объект Signature на документ и переиспользуйте его для нескольких операций.
// Bad - Creates new instance each time
public void processDocument(String path) {
    Signature sig1 = new Signature(path);
    sig1.search(/* ... */);
    
    Signature sig2 = new Signature(path); // Unnecessary reload
    sig2.verify(/* ... */);
}

// Good - Reuse the same instance
public void processDocument(String path) {
    try (Signature signature = new Signature(path)) {
        signature.search(/* ... */);
        signature.verify(/* ... */);
        signature.update(/* ... */);
    }
}
  1. Ищите только нужные страницы – ограничьте диапазон страниц до тех, где ожидаются штрих‑коды.
// Slow for 100+ page documents
BarcodeSearchOptions slowSearch = new BarcodeSearchOptions();
slowSearch.setAllPages(true);

// Fast - only check where signatures actually are
BarcodeSearchOptions fastSearch = new BarcodeSearchOptions();
fastSearch.setAllPages(false);
fastSearch.setPageNumber(1); // Only check cover page
  1. Выбирайте самый простой тип штрих‑кода – более простые штрих‑коды генерируются быстрее; используйте QR или Data Matrix только при реальной необходимости в большей ёмкости.
// Overkill for simple IDs
BarcodeSignOptions slow = new BarcodeSignOptions("12345", BarcodeTypes.QR);

// Faster and more appropriate
BarcodeSignOptions fast = new BarcodeSignOptions("12345", BarcodeTypes.Code128);

Соображения безопасности

  1. Никогда не кодируйте конфиденциальные персональные данные – штрих‑коды легко читаются; избегайте ПИД, паролей и т.п.
// Bad - exposes sensitive data
String barcodeData = "SSN:123-45-6789|Password:hunter2";

// Good - use reference IDs
String barcodeData = "USER-REF-7721X"; // Look up sensitive data server-side
  1. Проверяйте на сервере – не полагайтесь только на клиентскую проверку; всегда повторно проверяйте на надёжном бэкенде.
// Client scans barcode and extracts "APPROVED-BY-ADMIN"
// Always verify server-side:
public boolean verifyApproval(String documentId, String scannedData) {
    // Load document from secure storage
    Signature signature = new Signature(getSecureDocument(documentId));
    
    BarcodeVerifyOptions verifyOptions = new BarcodeVerifyOptions();
    verifyOptions.setText(scannedData);
    
    boolean isValid = signature.verify(verifyOptions) != null;
    
    // Also check against your database
    boolean isInDatabase = checkApprovalDatabase(documentId, scannedData);
    
    return isValid && isInDatabase;
}
  1. Добавляйте временные метки – включайте timestamp в полезную нагрузку штрих‑кода, чтобы предотвратить атаки повторного воспроизведения.
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

String timestamp = LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS).toString();
String barcodeData = "APPROVAL|" + userId + "|" + timestamp;

// When verifying, check the timestamp isn't too old
public boolean isSignatureRecent(String barcodeData, int maxAgeHours) {
    String[] parts = barcodeData.split("\\|");
    if (parts.length < 3) return false;
    
    LocalDateTime signatureTime = LocalDateTime.parse(parts[2]);
    LocalDateTime now = LocalDateTime.now();
    
    long hoursSince = ChronoUnit.HOURS.between(signatureTime, now);
    return hoursSince <= maxAgeHours;
}

Шаблоны обработки ошибок

  • Всегда используйте try‑with‑resources, чтобы гарантировать освобождение объектов Signature.
try (Signature signature = new Signature(documentPath)) {
    // Your operations here
    signature.sign(outputPath, signOptions);
} catch (Exception e) {
    logger.error("Failed to sign document: " + documentPath, e);
    throw new DocumentSigningException("Could not process document", e);
}
  • Проверяйте доступ к файлам перед попыткой подписи.
public void signDocument(String inputPath, String outputPath) {
    File inputFile = new File(inputPath);
    
    if (!inputFile.exists()) {
        throw new IllegalArgumentException("Input file not found: " + inputPath);
    }
    
    if (!inputFile.canRead()) {
        throw new SecurityException("Cannot read input file: " + inputPath);
    }
    
    File outputDir = new File(outputPath).getParentFile();
    if (outputDir != null && !outputDir.exists()) {
        outputDir.mkdirs();
    }
    
    try (Signature signature = new Signature(inputPath)) {
        signature.sign(outputPath, signOptions);
    }
}
  • Синхронизируйте доступ, если несколько потоков могут работать с одним и тем же файлом.
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ConcurrentHashMap;

public class SignatureManager {
    private final ConcurrentHashMap<String, ReentrantLock> documentLocks = new ConcurrentHashMap<>();
    
    public void signDocument(String documentPath, BarcodeSignOptions options) {
        ReentrantLock lock = documentLocks.computeIfAbsent(documentPath, k -> new ReentrantLock());
        
        lock.lock();
        try (Signature signature = new Signature(documentPath)) {
            signature.sign(documentPath + ".signed", options);
        } finally {
            lock.unlock();
        }
    }
}

Тестирование реализации

Шаблон юнит‑теста

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import static org.junit.jupiter.api.Assertions.*;

public class BarcodeSignatureTest {
    
    @TempDir
    Path tempDir;
    
    @Test
    public void testSignAndVerify() throws Exception {
        // Setup
        String testDoc = "test-document.pdf";
        String outputDoc = tempDir.resolve("signed-test.pdf").toString();
        String testData = "TEST-USER-12345";
        
        // Sign
        try (Signature signature = new Signature(testDoc)) {
            BarcodeSignOptions signOptions = new BarcodeSignOptions(testData, BarcodeTypes.Code128);
            signature.sign(outputDoc, signOptions);
        }
        
        // Verify
        try (Signature signature = new Signature(outputDoc)) {
            BarcodeVerifyOptions verifyOptions = new BarcodeVerifyOptions();
            verifyOptions.setText(testData);
            verifyOptions.setEncodeType(BarcodeTypes.Code128);
            
            boolean isValid = signature.verify(verifyOptions) != null;
            assertTrue(isValid, "Signature should be valid");
        }
    }
    
    @Test
    public void testSearchFindsSignature() throws Exception {
        String signedDoc = "signed-document.pdf";
        
        try (Signature signature = new Signature(signedDoc)) {
            BarcodeSearchOptions searchOptions = new BarcodeSearchOptions();
            searchOptions.setAllPages(true);
            
            List<BarcodeSignature> signatures = signature.search(BarcodeSignature.class, searchOptions);
            
            assertFalse(signatures.isEmpty(), "Should find at least one signature");
            assertEquals("TEST-USER-12345", signatures.get(0).getText());
        }
    }
}

Контрольный список интеграции

  • ✅ Протестировать все поддерживаемые форматы (PDF, DOCX, XLSX, PPTX, PNG).
  • ✅ Убедиться, что штрих‑коды выдерживают цикл печать‑скан.
  • ✅ Провести стресс‑тест с максимальной длиной строк данных.
  • ✅ Проверить позиционирование на A4, Letter и пользовательских размерах страниц.
  • ✅ Запустить параллельную подпись нескольких документов.
  • ✅ Мониторить использование памяти для документов > 500 страниц.
  • ✅ Убедиться, что сканеры штрих‑кодов надёжно читают полученный результат.

Логирование и мониторинг

Добавьте осмысленные логи вокруг каждой операции:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DocumentSigningService {
    private static final Logger logger = LoggerFactory.getLogger(DocumentSigningService.class);
    
    public void signDocument(String docPath, String barcodeData) {
        logger.info("Starting signature process for document: {}", docPath);
        logger.debug("Barcode data length: {} characters", barcodeData.length());
        
        try (Signature signature = new Signature(docPath)) {
            BarcodeSignOptions options = new BarcodeSignOptions(barcodeData, BarcodeTypes.Code128);
            
            long startTime = System.currentTimeMillis();
            signature.sign(docPath + ".signed", options);
            long duration = System.currentTimeMillis() - startTime;
            
            logger.info("Successfully signed document in {}ms", duration);
        } catch (Exception e) {
            logger.error("Failed to sign document: {}", docPath, e);
            throw new RuntimeException("Signature operation failed", e);
        }
    }
}

Отслеживайте ключевые метрики, такие как время обработки, потребление памяти и уровень ошибок:

// Track success/failure rates
metricsService.incrementCounter("signature.sign.success");
metricsService.incrementCounter("signature.verify.failure");

// Track performance
metricsService.recordTimer("signature.sign.duration", duration);

// Track document sizes
metricsService.recordHistogram("signature.document.pages", pageCount);

Профессиональные советы из реального использования

Стратегия пакетной обработки

При работе с сотнями файлов обрабатывайте их пакетами и выводите прогресс:

public void signBatch(List<String> documents, String barcodePrefix) {
    int total = documents.size();
    int completed = 0;
    int failed = 0;
    
    for (String docPath : documents) {
        try {
            String barcodeData = barcodePrefix + "-" + UUID.randomUUID().toString();
            signDocument(docPath, barcodeData);
            completed++;
            
            if (completed % 10 == 0) {
                logger.info("Progress: {}/{} documents signed", completed, total);
            }
        } catch (Exception e) {
            failed++;
            logger.warn("Failed to sign: {}", docPath, e);
        }
    }
    
    logger.info("Batch complete: {} succeeded, {} failed", completed, failed);
}

Вынесите конфигурацию наружу

Храните настройки штрих‑кода (тип, размер, цвет) в файле свойств для лёгкой настройки без перекомпиляции:

public class BarcodeConfig {
    private int defaultWidth = 100;
    private int defaultHeight = 40;
    private String defaultBarcodeType = "Code128";
    private String defaultColor = "BLACK";
    
    // Load from properties file or environment variables
    public BarcodeConfig() {
        this.defaultWidth = Integer.parseInt(
            System.getProperty("barcode.width", "100")
        );
        // ... load other properties
    }
    
    public BarcodeSignOptions createDefaultOptions(String text) {
        BarcodeSignOptions options = new BarcodeSignOptions(text, getBarcodeType());
        options.setWidth(defaultWidth);
        options.setHeight(defaultHeight);
        options.setForeColor(getColor());
        return options;
    }
}

Стандартизируйте полезную нагрузку штрих‑кода

Используйте разделённый формат, например EMPID|TIMESTAMP|DOCID, чтобы упростить парсинг и обеспечить согласованность:

public class BarcodeDataBuilder {
    private String userId;
    private String action;
    private LocalDateTime timestamp;
    private String documentId;
    
    public BarcodeDataBuilder setUserId(String userId) {
        this.userId = userId;
        return this;
    }
    
    public BarcodeDataBuilder setAction(String action) {
        this.action = action;
        return this;
    }
    
    public BarcodeDataBuilder setDocumentId(String documentId) {
        this.documentId = documentId;
        return this;
    }
    
    public String build() {
        this.timestamp = LocalDateTime.now();
        
        // Format: VERSION|USER|ACTION|DOC_ID|TIMESTAMP|CHECKSUM
        String data = String.join("|",
            "v1",
            userId,
            action,
            documentId,
            timestamp.toString()
        );
        
        // Add simple checksum
        int checksum = data.hashCode();
        return data + "|" + checksum;
    }
    
    public static ParsedBarcodeData parse(String barcodeData) {
        String[] parts = barcodeData.split("\\|");
        if (parts.length != 6 || !parts[0].equals("v1")) {
            throw new IllegalArgumentException("Invalid barcode format");
        }
        
        return new ParsedBarcodeData(parts[1], parts[2], parts[3], 
                                     LocalDateTime.parse(parts[4]));
    }
}

// Usage:
String barcodeData = new BarcodeDataBuilder()
    .setUserId("john.smith")
    .setAction("APPROVED")
    .setDocumentId("INV-2025-042")
    .build();

Автоматическое определение типа документа

Корректируйте размеры штрих‑кода в зависимости от того, является ли цель PDF, Word или изображением:

public BarcodeSignOptions getOptimalOptions(String documentPath, String text) {
    String extension = documentPath.substring(documentPath.lastIndexOf(".") + 1).toLowerCase();
    
    BarcodeSignOptions options = new BarcodeSignOptions(text, BarcodeTypes.Code128);
    
    switch (extension) {
        case "pdf":
            // PDFs can handle larger, detailed barcodes
            options.setWidth(150);
            options.setHeight(50);
            break;
        case "docx":
        case "doc":
            // Word docs need compact signatures that don't disrupt flow
            options.setWidth(100);
            options.setHeight(35);
            options.setVerticalAlignment(VerticalAlignment.Bottom);
            break;
        case "xlsx":
        case "xls":
            // Excel needs signatures that don't obscure data
            options.setWidth(80);
            options.setHeight(30);
            options.setHorizontalAlignment(HorizontalAlignment.Right);
            break;
        default:
            // Safe defaults
            options.setWidth(100);
            options.setHeight(40);
    }
    
    return options;
}

Дополнительные ресурсы

Часто задаваемые вопросы

В: Можно ли использовать GroupDocs.Signature с Java 17?
О: Да – библиотека полностью совместима с JDK 8, 11 и 17.

В: Выживает ли штрих‑код после цикла печать‑скан?
О: При использовании Code128 или QR с достаточным размером и контрастом штрих‑код остаётся сканируемым после печати и сканирования.

В: Сколько штрих‑кодов может содержать один документ?
О: Жёсткого ограничения нет; однако для оптимальной производительности рекомендуется держать общее количество ниже 200 на документ.

В: Требуется ли лицензия для сборок разработки?
О: Временная лицензия убирает водяные знаки оценки; полная лицензия обязательна для любого продакшн‑развёртывания.

В: Можно ли подписывать PDF, защищённые паролем?
О: Да – передайте пароль при создании объекта Signature; API раскроет файл внутренне.


Последнее обновление: 2026-07-15
Тестировано с: GroupDocs.Signature 23.9 for Java
Автор: GroupDocs

Связанные учебники