Работа с PDF-операторами в Java

Введение в PDF‑операторы и их использование

Оператор — это PDF‑ключевое слово, указывающее действие, которое должно быть выполнено, например, отрисовка графической формы на странице. Ключевое слово оператора отличается от именованного объекта отсутствием начального символа косой черты (2Fh). Операторы имеют смысл только внутри контент‑потока.

Контент‑поток — это объект PDF‑потока, данные которого состоят из инструкций, описывающих графические элементы, которые необходимо отрисовать на странице. Более подробная информация о PDF‑операторах доступна в Спецификация PDF.

Используйте эту страницу, когда вам требуется прямой контроль над потоком содержимого PDF в Java, например, размещение изображения с явными матричными вычислениями, повторное использование одной и той же графики несколько раз через XForm или удаление низкоуровневых инструкций рисования со страницы.

Добавление изображения с помощью PDF-операторов

Используйте низкоуровневые операторы, когда размещение изображения должно контролироваться точно на уровне потока содержимого, а не через API более высокого уровня для макета.

  1. Откройте исходный PDF с помощью Document и получите целевой Page.
  2. Добавьте поток входного изображения к ресурсам страницы и сохраните возвращённое имя ресурса.
  3. Создайте Rectangle, который определяет целевую область, и постройте Matrix по его границам.
  4. Используйте GSave для сохранения текущего графического состояния, ConcatenateMatrix для позиционирования изображения, Do для его отрисовки и GRestore для восстановления предыдущего состояния.
  5. Сохраните обновлённый PDF‑документ.
public static void addImageUsingPdfOperators(Path inputFile, Path imageFile, Path outputFile) throws Exception {
    try (Document document = new Document(inputFile.toString());
         InputStream imageStream = Files.newInputStream(imageFile)) {
        Page page = document.getPages().get_Item(1);
        String imageName = page.getResources().getImages().add(imageStream);

        Rectangle rectangle = new Rectangle(100, 100, 200, 200, true);
        Matrix matrix = new Matrix(new double[]{
                rectangle.getURX() - rectangle.getLLX(),
                0,
                0,
                rectangle.getURY() - rectangle.getLLY(),
                rectangle.getLLX(),
                rectangle.getLLY()
        });

        page.getContents().add(new GSave());
        page.getContents().add(new ConcatenateMatrix(matrix));
        page.getContents().add(new Do(imageName));
        page.getContents().add(new GRestore());
        document.save(outputFile.toString());
    }
    System.out.println("Image added with PDF operators to " + outputFile);
}

Отрисовка переиспользуемого XForm‑контента на странице

Используйте этот подход, когда одно и то же изображение или графика должны отображаться более одного раза без дублирования ресурса в файле PDF.

  1. Откройте исходный PDF с помощью Document, выберите нужную страницу Page и получите доступ к её OperatorCollection.
  2. Заключите существующее содержимое страницы между GSave и GRestore, чтобы последующие преобразования не повлияли на исходный поток содержимого.
  3. Создайте ресурс XForm, добавьте изображение в ресурсы формы и используйте ConcatenateMatrix и Do, чтобы нарисовать изображение внутри формы.
  4. Разместите одну и ту же форму в нескольких точках страницы, добавив матрицу переноса и вызвав оператор Do с именем формы.
  5. Восстановите состояние графики и сохраните PDF‑вывод.
public static void drawXFormOnPage(Path inputFile, Path imageFile, Path outputFile) throws Exception {
    try (Document document = new Document(inputFile.toString());
         InputStream imageStream = Files.newInputStream(imageFile)) {
        Page page = document.getPages().get_Item(1);
        OperatorCollection pageContents = page.getContents();

        pageContents.insert(1, new GSave());
        pageContents.add(new GRestore());
        pageContents.add(new GSave());

        XForm form = XForm.createNewForm(page, document);
        page.getResources().getForms().add(form);

        form.getContents().add(new GSave());
        form.getContents().add(new ConcatenateMatrix(200, 0, 0, 200, 0, 0));
        String imageName = form.getResources().getImages().add(imageStream);
        form.getContents().add(new Do(imageName));
        form.getContents().add(new GRestore());

        addFormAt(pageContents, form.getName(), 100, 500);
        addFormAt(pageContents, form.getName(), 100, 300);

        pageContents.add(new GRestore());
        document.save(outputFile.toString());
    }
    System.out.println("XForm drawn on page in " + outputFile);
}

private static void addFormAt(OperatorCollection pageContents, String formName, double x, double y) {
    pageContents.add(new GSave());
    pageContents.add(new ConcatenateMatrix(1, 0, 0, 1, x, y));
    pageContents.add(new Do(formName));
    pageContents.add(new GRestore());
}

Удаление графических операторов со страницы

Используйте этот пример, когда страница содержит операторы векторного рисования, которые следует удалить непосредственно из потока содержимого.

  1. Откройте исходный PDF с помощью Document и получите целевой Page.
  2. Переберите операторы содержимого страницы и соберите экземпляры Stroke, ClosePathStroke и Fill.
  3. Удалите собранные операторы из содержимого страниц и сохраните обновлённый PDF.

Эта техника удаляет только целевые инструкции рисования. Если на странице также содержатся связанные текстовые подписи или другие не графические операторы, эти элементы остаются в потоке содержимого и могут потребовать отдельного прохода очистки.

public static void removeGraphicsObjects(Path inputFile, Path outputFile) {
    try (Document document = new Document(inputFile.toString())) {
        Page page = document.getPages().get_Item(1);
        List<Operator> operatorsToRemove = new ArrayList<>();
        for (Object item : page.getContents()) {
            Operator operator = (Operator) item;
            if (operator instanceof Stroke || operator instanceof ClosePathStroke || operator instanceof Fill) {
                operatorsToRemove.add(operator);
            }
        }
        page.getContents().delete(operatorsToRemove);
        document.save(outputFile.toString());
    }
    System.out.println("Graphics operators removed in " + outputFile);
}

Связанные темы