Как правильно масштабировать содержимое PDF с помощью Apache PdfBox? - PullRequest
0 голосов
/ 24 апреля 2019

У меня есть PDF с данными.Миссия - разместить изображение с некоторой информацией на всех страницах (в одном месте: вверху, слева, справа, внизу страницы).Но некоторым PDF-файлам не место на этих позициях, и я не могу вставить изображение поверх содержимого.Вот почему мне нужно масштабирование.После нескольких дней попыток и чтения stackoverflow я делаю все, что мне нужно, и "Он жив! Жив!"(c) ... но не всегда ... Начиная с pdfbox 2.0.8, в некоторых файлах pdf (с необязательными свойствами содержимого) моя функция попадает в рекурсивный ад.

Это код:

public static void putStamp(PDDocument document, X509Certificate certificate, UserInfo userInfo, TransformProperties transformProperties) throws IOException {
    final Map<StampSize, PDImageXObject> stampsUser = new HashMap<>();

    TransformProperties.Position posUserStamp = null;
    double scale = 1;

    if (transformProperties!=null){
        posUserStamp = transformProperties.getUserStampPosition();
        scale = ((double)transformProperties.getScale()) / 100;
    }

    final LayerUtility layerUtility = new LayerUtility(document);

    Iterator<PDPage> iterator = document.getPages().iterator();
    while (iterator.hasNext()) {
        PDPage page = iterator.next();
        PDPageContentStream stream;
        Boolean isRotatedPage = false;
        int rotateAngle = 0;

        final PDFormXObject obj = layerUtility.importPageAsForm(document, page);
        AffineTransform affineTransformScale1 = new AffineTransform(1,0, 0,1,0, 0);
        obj.setMatrix(affineTransformScale1);

        page.setResources(new PDResources());

        if(page.getRotation()==90 || page.getRotation()==270){
            isRotatedPage = true;
            rotateAngle = 360 - page.getRotation();
            page.setRotation(0);
            PDRectangle rectangle = new PDRectangle(page.getMediaBox().getHeight(),page.getMediaBox().getWidth());
            page.setMediaBox(rectangle);
            page.setCropBox(rectangle);
        }else if (page.getRotation()==180){
            isRotatedPage = true;
            rotateAngle = 360 - page.getRotation();
            page.setRotation(0);
        }

        float pageWidth = page.getBBox().getWidth();
        float pageHeight =  page.getBBox().getHeight();
        double newXcoordinates = (pageWidth - pageWidth*scale)  /2;
        double newYcoordinates = (pageHeight - pageHeight*scale)  /2;

        stream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.OVERWRITE, true, true);
        stream.saveGraphicsState();
        if (isRotatedPage) {
            float offsetX = 0, offsetY = 0;
            switch (rotateAngle){
                case 270:
                    offsetX = 0;
                    offsetY = pageHeight;
                    break;
                case 180:
                    offsetX = pageWidth;
                    offsetY = pageHeight;
                    break;
                case 90:
                    offsetX = pageWidth;
                    offsetY = 0;
                    break;
            }
            stream.transform(Matrix.getRotateInstance(Math.toRadians(rotateAngle), offsetX, offsetY));
        }

        AffineTransform affineTransformScale = new AffineTransform(scale, 0, 0, scale, newXcoordinates, newYcoordinates);
        stream.transform(new Matrix(affineTransformScale));
        stream.drawForm(obj);
        stream.restoreGraphicsState();

        if (!Config.getInstance().isIgnoreUserStamp() && userInfo != null) {
            int posXuser = 0;
            int posYuser = 0;
            int stampH = 0;
            int stampW = 0;
            boolean rotate = false;
            if (posUserStamp == TransformProperties.Position.LEFT) {
                stampH = (int) (pageWidth / 32 * multiply);
                stampW = (int) (pageHeight * 0.9 * multiply);
                posXuser = (int) (pageWidth * 0.015);
                posYuser = (int) ((pageHeight - stampW/multiply) / 2 );
                rotate = true;
            }else if (posUserStamp == TransformProperties.Position.RIGHT){
                stampH = (int) (pageWidth / 32 * multiply);
                stampW = (int) (pageHeight * 0.9 * multiply);
                posXuser = (int) (pageWidth * 0.985 - (stampH / multiply)) ;
                posYuser = (int) ((pageHeight - (stampW / multiply)) / 2 );
                rotate = true;
            }else if(posUserStamp == TransformProperties.Position.BOTTOM){
                stampH = (int) (pageHeight / 32 * multiply);
                stampW = (int) (pageWidth * 0.9 * multiply);
                posXuser = (int) (pageWidth * 0.05);
                posYuser = (int) (pageHeight * 0.015);
            }else if(posUserStamp == TransformProperties.Position.TOP || posUserStamp == null) {
                stampH = (int) (pageHeight / 32 * multiply);
                stampW = (int) (pageWidth * 0.9 * multiply);
                posXuser = (int) (pageWidth * 0.05);
                posYuser = (int) (pageHeight * 0.985 - (stampH / multiply));
            }

            final StampSize stampSizeUser = new StampSize(stampW, stampH);

            PDImageXObject imageUser;
            if(stampsUser.containsKey(stampSizeUser)){
                imageUser = stampsUser.get(stampSizeUser);
            }else {
                BufferedImage img = createStampUser(stampSizeUser.width, stampSizeUser.height, userInfo);
                if (rotate) {
                    img = rotateImageByDegrees(img, 90);
                }
                imageUser = LosslessFactory.createFromImage(document, img);
            }
            stampsUser.put(stampSizeUser, imageUser);
            stream.drawImage(imageUser, posXuser, posYuser, imageUser.getWidth() / multiply, imageUser.getHeight() / multiply);
        }

        stream.close();

    }
}

public static BufferedImage rotateImageByDegrees(BufferedImage img, double degrees) {
    double rads = Math.toRadians(degrees);
    double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads));
    int w = img.getWidth();
    int h = img.getHeight();
    int newWidth = (int) Math.floor(w * cos + h * sin);
    int newHeight = (int) Math.floor(h * cos + w * sin);

    BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = rotated.createGraphics();
    AffineTransform at = new AffineTransform();
    at.translate((newWidth - w) / 2, (newHeight - h) / 2);

    at.rotate(rads, w / 2, h / 2);
    g2d.setTransform(at);
    g2d.drawImage(img, 0, 0,null);
    g2d.dispose();

    return rotated;
}

Итак, что я делаю:

  1. Я сохраняю текущую страницу как PDFormXObject и очищаю ресурсы на странице.

final PDFormXObject obj = layerUtility.importPageAsForm(document, page); AffineTransform affineTransformScale1 = new AffineTransform(1,0, 0,1,0, 0); obj.setMatrix(affineTransformScale1); page.setResources(new PDResources());

Проверьте, поворачивается ли страница на setRotation(), и установите для нее значение 0 с изменением ширины и высоты медиа-бокса.(потому что когда он поворачивается таким образом, нижняя часть при предпросмотре становится левой ... или правой ... или верхней ... это не подходит для пользователя)

Открыть поток с OVERWRITE,при необходимости поверните, масштабируйте (по умолчанию scale = 1) и drawForm с шага 1.

AffineTransform affineTransformScale = new AffineTransform (scale, 0, 0, scale, newXcoordinates, newYcoordinates);stream.transform (новая матрица (affineTransformScale));stream.drawForm (OBJ);stream.restoreGraphicsState ();

Создать и нарисовать штамп.

Я чувствую, что я делаю - это неправильно!Потому что на шаге 1 я использую метод layerUtility.importPageAsForm(document, page);, созданный для импорта из другого PDF ... не того же самого PDF, если я правильно понял.И так как pdfbox 2.0.8 в этом методе был добавлен importOcProperties(sourceDoc); В этом месте у меня есть бесконечная рекурсия в некоторых PDF-файлах.Но как я могу преобразовать текущую страницу в PDFormXObject?Или как я могу использовать матрицу для масштабирования только контента?(я пытаюсь сделать это, но у меня не получилось ...)

Я не знаю, как сделать это правильно.Я не хочу создавать новый PDDocument и добавлять страницы к нему, потому что некоторые PDF-файлы более 500 МБ.В этом случае в памяти будет 2 файла PDF размером более 500 МБ.Я думаю, что это неправильный способ ... Но как правильно взять страницу, масштабировать содержимое (без изменения размера, например, mediaBox и cropBox) и добавить штамп?

Второй вопрос о шаге 2: какой точный способ сделать пейзаж илипортрет?По setRotation или смене медиабокса?ИМХО: меняю медиабоксы, но я новичок в pdf ...

Пожалуйста, пройдите по порядку!

PS Я не могу показать pdfs ... личные данные ...

...