У меня есть 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;
}
Итак, что я делаю:
- Я сохраняю текущую страницу как
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 ... личные данные ...