我用 Adobe 创建了一个 PDF,其中包含一个名为“logo”的图像字段
现在,如果我想使用 PDFBox 添加图片,它将不会显示在创建的 pdf 中。
但是,使用正确创建的 PDImageXObject 对象不会引发错误消息,并且调试看起来完全正常。
使用的代码主要改编自这个问题:
public static void setImageField(PDDocument pdfDocument, PDAcroForm acroForm, String fieldKey, byte[] imageData)
{
final PDField retrievedField = acroForm.getField(fieldKey);
if (!(retrievedField instanceof PDPushButton)) {
throw new RuntimeException("The field: " + fieldKey + " is not of the correct type");
}
LOGGER.info("Received field: " + retrievedField.getPartialName()); // correct field is being logged
final PDPushButton imageField = (PDPushButton) retrievedField;
final List<PDAnnotationWidget> fieldWidgets = imageField.getWidgets(); // it has exactly one widget, which would be the action to set the image
if (fieldWidgets == null || fieldWidgets.size() <= 0) {
throw new RuntimeException("Misconfiguration for field: " + fieldKey + ", it has no widgets(actions)");
}
final PDAnnotationWidget imageWidget = fieldWidgets.get(0);
PDImageXObject imageForm;
try {
// This test data is resized to be smaller than the bounding box of the image element in the PDF. Just for testing purposes
final String testImage = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAARUlEQVR42u3PMREAAAgEIO2fzkRvBlcPGtCVTD3QIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIXC7VGjKHva+IvAAAAAElFTkSuQmCC";
final byte[] testData = Base64.getDecoder().decode(testImage);
imageForm = PDImageXObject.createFromByteArray(pdfDocument, testData, "logo"); // imageForm is being populated with data and no error thrown
}
catch (IOException e) {
throw new RuntimeException("Error creating Image from image data", e);
}
final float imageScaleRatio = (float) (imageForm.getHeight() / imageForm.getWidth());
final PDRectangle imagePosition = getFieldArea(imageField);
LOGGER.info("Received image position: " + imagePosition.toString());
// Retrieve the height and width and position of the rectangle where the picture will be
final float imageHeight = imagePosition.getHeight();
LOGGER.info("Image height: " + imageHeight);
final float imageWidth = imageHeight / imageScaleRatio;
LOGGER.info("Image width: " + imageWidth);
final float imageXPosition = imagePosition.getLowerLeftX();
LOGGER.info("Image X position: " + imageXPosition);
final float imageYPosition = imagePosition.getLowerLeftY();
LOGGER.info("Image Y position: " + imageYPosition);
final PDAppearanceStream documentAppearance = new PDAppearanceStream(pdfDocument);
documentAppearance.setResources(new PDResources()); // not sure why this is done
// Weird "bug" in pdfbox forces to create the contentStream always after the object to add
try (PDPageContentStream documentContentStream = new PDPageContentStream(pdfDocument, documentAppearance)) {
documentContentStream.drawImage(imageForm, imageXPosition, imageYPosition, imageWidth, imageHeight);
}
catch (IOException e) {
throw new RuntimeException("Error drawing the picture in the document", e);
}
// Setting the boundary box is mandatory for the image field! Do not remove or the flatten call on the acroform will NPE
documentAppearance.setBBox(new PDRectangle(imageXPosition, imageYPosition, imageWidth, imageHeight));
// Apply the appearance settings of the document onto the image widget ( No border )
setImageAppearance(imageWidget, documentAppearance);
// This code is normally somewhere else but for SO copied in this method to show how the pdf is being created
acroForm.flatten();
AccessPermission ap = new AccessPermission();
ap.setCanModify(false);
ap.setCanExtractContent(false);
ap.setCanFillInForm(false);
ap.setCanModifyAnnotations(false);
ap.setReadOnly();
StandardProtectionPolicy spp = new StandardProtectionPolicy("", "", ap);
spp.setEncryptionKeyLength(PdfBuildConstants.ENCRYPTION_KEY_LENTGH);
pdfDocument.protect(spp);
pdfDocument.save(pdfFile);
pdfDocument.close();
}
/**
* Applies the appearance settings of the document onto the widget to ensure a consistent look of the document.
*
* @param imageWidget The {@link PDAnnotationWidget Widget} to apply the settings to
* @param documentAppearance The {@link PDAppearanceStream Appearance settings} of the document
*/
private static void setImageAppearance(final PDAnnotationWidget imageWidget,
final PDAppearanceStream documentAppearance)
{
PDAppearanceDictionary widgetAppearance = imageWidget.getAppearance();
if (widgetAppearance == null) {
widgetAppearance = new PDAppearanceDictionary();
imageWidget.setAppearance(widgetAppearance);
}
widgetAppearance.setNormalAppearance(documentAppearance);
}
/**
* Retrieves the dimensions of the given {@link PDField Field} and creates an {@link PDRectangle Rectangle} with the
* same dimensions.
*
* @param field The {@link PDField Field} to create the rectangle for
* @return The created {@link PDRectangle Rectangle} with the dimensions of the field
*/
private static PDRectangle getFieldArea(PDField field) {
final COSDictionary fieldDictionary = field.getCOSObject();
final COSBase fieldAreaValue = fieldDictionary.getDictionaryObject(COSName.RECT);
if (!(fieldAreaValue instanceof COSArray)) {
throw new RuntimeException("The field: " + field.getMappingName() + " has no position values");
}
final COSArray fieldAreaArray = (COSArray) fieldAreaValue;
return new PDRectangle(fieldAreaArray);
}
我还查看了其他问题,例如this,但我不能使用 PDJpeg,因为它在当前版本中不可用。此外,要使用的原始图像可以是从 jpeg、png 到 gif 的任何内容。
我验证了记录的位置和尺寸变量与 pdf 文件中的图像字段具有相同的值。(字段属性)