我遇到了一个非常棘手的问题。我们有应该填写的表单,但是有些人在Foxit中使用了自由注释形式的文本注释,而不是填写表单字段,因此注释永远不会变平。当我们的渲染软件生成最终文档时,不包括注释。
我尝试的解决方案是基本上遍历文档,获取批注文本内容并将其写入pdf,以便将其放在最终文档中,然后删除实际的批注,但是我遇到了一个我不知道的问题注释使用的字体,行距等,因此无法找到如何从pdfbox获取它的方法,因为注释在未展平的表单上看起来很精确。基本上,我想展平在foxit中创建的自由格式的注释(打字机注释功能)这是代码。它正在工作,但是我又在努力弄清楚如何使注释写入最终的pdf文档。再次压扁在acroform上不起作用,因为这些不是acroform字段!实时代码过滤掉了不是自由文本类型注释的所有内容,但是下面的代码应该显示我的问题。
public static void main(String [] args)
{
String startDoc = "C:/test2/test.pdf";
String finalFlat = "C:/test2/test_FLAT.pdf";
try {
// for testing
try {
//BasicConfigurator.configure();
File myFile = new File(startDoc);
PDDocument pdDoc = PDDocument.load( myFile );
PDDocumentCatalog pdCatalog = pdDoc.getDocumentCatalog();
PDAcroForm pdAcroForm = pdCatalog.getAcroForm();
// set the NeedApperances flag
pdAcroForm.setNeedAppearances(false);
// correct the missing page link for the annotations
for (PDPage page : pdDoc.getPages()) {
for (PDAnnotation annot : page.getAnnotations()) {
System.out.println(annot.getContents());
System.out.println(annot.isPrinted());
System.out.println(annot.isLocked());
System.out.println(annot.getAppearance().toString());
PDPageContentStream contentStream = new PDPageContentStream(pdDoc, page, PDPageContentStream.AppendMode.APPEND,true,true);
int fontHeight = 14;
contentStream.setFont(PDType1Font.TIMES_ROMAN, fontHeight);
float height = annot.getRectangle().getLowerLeftY();
String s = annot.getContents().replaceAll("\t", " ");
String ss[] = s.split("\\r");
for(String sss : ss)
{
contentStream.beginText();
contentStream.newLineAtOffset(annot.getRectangle().getLowerLeftX(),height );
contentStream.showText(sss);
height = height + fontHeight * 2 ;
contentStream.endText();
}
contentStream.close();
page.getAnnotations().remove(annot);
}
}
pdAcroForm.flatten();
pdDoc.save(finalFlat);
pdDoc.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
catch (Exception e) {
System.err.println("Exception: " + e.getLocalizedMessage());
}
}
因此,在Tilman的帮助下,我能够找到原始源代码并将其修改为包括不属于任何形式的自由文本注释。这是工作代码
public static void flattenv2(String startDoc)
{
org.apache.log4j.Logger.getRootLogger().setLevel(org.apache.log4j.Level.INFO);
String finalFlat = startDoc;
try {
// for testing
try {
//BasicConfigurator.configure();
File myFile = new File(startDoc);
PDDocument pdDoc = PDDocument.load( myFile );
PDDocumentCatalog pdCatalog = pdDoc.getDocumentCatalog();
PDAcroForm pdAcroForm = pdCatalog.getAcroForm();
// set the NeedApperances flag
pdAcroForm.setNeedAppearances(false);
boolean isContentStreamWrapped;
for (PDPage page : pdDoc.getPages())
{
PDPageContentStream contentStream;
isContentStreamWrapped = false;
List<PDAnnotation> annotations = new ArrayList<>();
for (PDAnnotation annotation: page.getAnnotations())
{
String cls = annotation.getClass().getName();
// System.out.println("HELLO");
// System.out.println(cls);
if (!(annotation instanceof PDAnnotationWidget) && !annotation.getClass().getName().equals("org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationMarkup"))
{
annotations.add(annotation);
}
else if (!annotation.isInvisible() && !annotation.isHidden() && annotation.getNormalAppearanceStream() != null)
{
if (!isContentStreamWrapped)
{
contentStream = new PDPageContentStream(pdDoc, page, AppendMode.APPEND, true, true);
isContentStreamWrapped = true;
}
else
{
contentStream = new PDPageContentStream(pdDoc, page, AppendMode.APPEND, true);
}
PDAppearanceStream appearanceStream = annotation.getNormalAppearanceStream();
PDFormXObject fieldObject = new PDFormXObject(appearanceStream.getCOSObject());
contentStream.saveGraphicsState();
// translate the appearance stream to the widget location if there is
// not already a transformation in place
boolean needsTranslation = resolveNeedsTranslation(appearanceStream);
// scale the appearance stream - mainly needed for images
// in buttons and signatures
boolean needsScaling = resolveNeedsScaling(appearanceStream);
Matrix transformationMatrix = new Matrix();
boolean transformed = false;
if (needsTranslation)
{
transformationMatrix.translate(annotation.getRectangle().getLowerLeftX(),
annotation.getRectangle().getLowerLeftY());
transformed = true;
}
if (needsScaling)
{
PDRectangle bbox = appearanceStream.getBBox();
PDRectangle fieldRect = annotation.getRectangle();
if (bbox.getWidth() - fieldRect.getWidth() != 0 && bbox.getHeight() - fieldRect.getHeight() != 0)
{
float xScale = fieldRect.getWidth() / bbox.getWidth();
float yScale = fieldRect.getHeight() / bbox.getHeight();
Matrix scalingMatrix = fieldObject.getMatrix();
transformationMatrix.concatenate(scalingMatrix);
transformed = true;
}
}
if (transformed)
{
contentStream.transform(transformationMatrix);
}
contentStream.drawForm(fieldObject);
contentStream.restoreGraphicsState();
contentStream.close();
}
}
page.setAnnotations(annotations);
}
pdAcroForm.flatten();
pdDoc.save(finalFlat);
pdDoc.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
catch (Exception e) {
System.err.println("Exception: " + e.getLocalizedMessage());
}
}
private static boolean resolveNeedsTranslation(PDAppearanceStream appearanceStream)
{
boolean needsTranslation = false;
PDResources resources = appearanceStream.getResources();
if (resources != null && resources.getXObjectNames().iterator().hasNext())
{
Iterator<COSName> xObjectNames = resources.getXObjectNames().iterator();
while (xObjectNames.hasNext())
{
try
{
// if the BBox of the PDFormXObject does not start at 0,0
// there is no need do translate as this is done by the BBox definition.
PDXObject xObject = resources.getXObject(xObjectNames.next());
if (xObject instanceof PDFormXObject)
{
PDRectangle bbox = ((PDFormXObject)xObject).getBBox();
float llX = bbox.getLowerLeftX();
float llY = bbox.getLowerLeftY();
if (llX == 0 && llY == 0)
{
needsTranslation = true;
}
}
}
catch (IOException e)
{
// we can safely ignore the exception here
// as this might only cause a misplacement
}
}
return needsTranslation;
}
return true;
}
private static boolean resolveNeedsScaling(PDAppearanceStream appearanceStream)
{
// Check if there is a transformation within the XObjects content
PDResources resources = appearanceStream.getResources();
return resources != null && resources.getXObjectNames().iterator().hasNext();
}
}