0

我需要将注释从一个 PDF 文件复制到另一个。我使用了优秀的 PDFClown 库,但无法操作颜色、旋转等。这可能吗?我可以看到基本对象信息,但也不确定如何直接操作。

我可以通过克隆外观来复制外观,但不能“编辑”它。

提前致谢。亚历克斯

PS如果Stephano作者正在上市,项目死了吗?

4

2 回答 2

0

一般注释,特别是标注注释

我调查了一下,恐怕没有多少你可以使用高级方法确定性地操作任意输入。原因是有许多替代方法可以设置标注注释的外观,而 PDF Clown 仅支持具有显式高级方法的优先级较低的方法。从高优先级向下

  • AP流中的显式外观。如果给出,则使用它,而忽略此外观是否看起来像 Callout 注释,更不用说像其他 Callout 属性定义的外观了。

    PDF Clown 还没有为其他值的标注注释创建外观,更不用说更新现有外观以跟进某些特定属性(例如Color)的更改。对于 ISO 32000-2 支持,PDF Clown 必须改进,因为外观流已成为强制性要求。

    如果它存在,您可以使用它来检索外观,getAppearance()但您只能得到一个FormXObject带有其低级绘图指令的,没有特定于标注的。

    给定的一件事你可以很容易地操作,但是,你可以通过相应地设置它的MatrixFormXObject来很容易地旋转或倾斜外观,例如

    annotation.getAppearance().getNormal().get(null).setMatrix(AffineTransform.getRotateInstance(100, 10));
    
  • RC字符串或流中的富文本字符串。除非给出外观,否则标注文本框中的文本是从此富文本数据生成的(此处的富文本使用 XHTML 1.0 子集进行格式化)。

    PDF Clown 尚未创建标注文本的富文本表示,更不用说更新现有的以跟进某些特定属性(例如Color)更改。

    如果存在,您可以使用 低级访问检索富文本getBaseDataObject().get(PdfName.RC),更改此字符串或流,然后使用 再次设置它getBaseDataObject().put(PdfName.RC, ...)。同样,您可以使用其名称检索、操作和设置富文本默认样式字符串PdfName.DS

  • 在没有外观流和(就文本内容而言)富文本字符串的情况下,用于构建标注的不同方面的许多不同设置。

    PDF Clown 支持(许多)这些属性,特别是如果您将克隆的注释转换为StaticNote,例如不透明度CA使用get/set/withAlpha,边框Border / BS使用get/set/withBorder,背景颜色C使用get/set/withColor,...

    顺便说一句,它的行尾样式LE支持有一个错误: 显然Line注释LE属性的代码是在没有检查的情况下复制的;不幸的是,那里的属性遵循不同的语法......

你的任务

因此,关于您声明要更改的属性,

  • Rotation:Callout 注释本身没有旋转属性(除了是否跟随页面旋转的标志)。因此,您不能将旋转设置为简单的注释属性。但是,如果源注释确实有外观流,则可以操纵其Matrix以在注释矩形内旋转它,见上文。

  • 边框颜色字体:如果您的 Callout 具有外观流,您可以尝试使用 a 解析其内容ContentScanner并操作颜色和字体设置操作。否则,如果设置了富文本信息,对于字体,您可以尝试使用一些 XML 解析器解析富文本并操作字体样式属性。否则,您可以解析默认外观DA字符串并操作其字体和颜色设置指令。

一些示例代码

我使用 Adob​​e Acrobat 创建了一个带有示例标注注释的文件:Callout-Yellow.pdf。它包含外观流、富文本和简单属性,因此可以使用此文件进行不同级别的操作。

我用不同的值应用了这个代码keepAppearanceStreamkeepRichText(你没有提到你是使用Java还是.Net的PDF Clown;所以我选择了Java;但是.Net的端口应该是微不足道的......):

boolean keepAppearanceStream = ...;
boolean keepRichText = ...;

try (   InputStream sourceResource = GET_STREAM_FOR("Callout-Yellow.pdf");
        InputStream targetResource = GET_STREAM_FOR("test123.pdf");
        org.pdfclown.files.File sourceFile = new org.pdfclown.files.File(sourceResource);
        org.pdfclown.files.File targetFile = new org.pdfclown.files.File(targetResource); ) {
    Document sourceDoc = sourceFile.getDocument();
    Page sourcePage = sourceDoc.getPages().get(0);
    Annotation<?> sourceAnnotation = sourcePage.getAnnotations().get(0);

    Document targetDoc = targetFile.getDocument();
    Page targetPage = targetDoc.getPages().get(0);

    StaticNote targetAnnotation = (StaticNote) sourceAnnotation.clone(targetDoc);

    if (keepAppearanceStream) {
        // changing properties of an appearance
        // rotating the appearance in the appearance rectangle
        targetAnnotation.getAppearance().getNormal().get(null).setMatrix(AffineTransform.getRotateInstance(100, 10));
    } else {
        // removing the appearance to allow lower level properties changes
        targetAnnotation.setAppearance(null);
    }

    // changing text background color
    targetAnnotation.setColor(new DeviceRGBColor(0, 0, 1));

    if (keepRichText) {
        // changing rich text properties
        PdfString richText = (PdfString) targetAnnotation.getBaseDataObject().get(PdfName.RC);
        String richTextString = richText.getStringValue();
        // replacing the font family
        richTextString = richTextString.replaceAll("font-family:Helvetica", "font-family:Courier");
        richText = new PdfString(richTextString);
        targetAnnotation.getBaseDataObject().put(PdfName.RC, richText);
    } else {
        targetAnnotation.getBaseDataObject().remove(PdfName.RC);
        targetAnnotation.getBaseDataObject().remove(PdfName.DS);
    }

    // changing default appearance properties
    PdfString defaultAppearance = (PdfString) targetAnnotation.getBaseDataObject().get(PdfName.DA);
    String defaultAppearanceString = defaultAppearance.getStringValue();
    // replacing the font
    defaultAppearanceString = defaultAppearanceString.replaceFirst("Helv", "HeBo");
    // replacing the text and line color
    defaultAppearanceString = defaultAppearanceString.replaceFirst(". . . rg", ".5 g");
    defaultAppearance = new PdfString(defaultAppearanceString);
    targetAnnotation.getBaseDataObject().put(PdfName.DA, defaultAppearance);

    // changing the text value
    PdfString contents = (PdfString) targetAnnotation.getBaseDataObject().get(PdfName.Contents);
    String contentsString = contents.getStringValue();
    contentsString = contentsString.replaceFirst("text", "text line");
    contents = new PdfString(contentsString);
    targetAnnotation.getBaseDataObject().put(PdfName.Contents, contents);

    // change the line width and style
    targetAnnotation.setBorder(new Border(0, new LineDash(new double[] {3, 2})));

    targetPage.getAnnotations().add(targetAnnotation);

    targetFile.save(new File(RESULT_FOLDER, "test123-withCalloutCopy.pdf"),  SerializationModeEnum.Standard);
}

CopyCallOut测试testCopyCallout

请注意,该代码仅具有概念验证质量:对于任意 PDF,您不能简单地期望将“font-family:Helvetica”字符串替换为“font-family:Courier”或将“Helv”替换为“HeBo”或“。 . . rg" by ".5 g" 来完成这项工作:可以使用不同的样式属性或名称来赋予字体,并且可以使用不同的着色指令。

Adobe 中的屏幕截图

  • 原始文件:

    原始注释

  • keepAppearanceStream = true

    外观流保持但旋转

  • keepAppearanceStream = falsekeepRichText = true

    外观流被丢弃,富文本被保留但被操纵

  • keepAppearanceStream = falsekeepRichText = false

    删除了外观流和富文本,并操纵了简单的属性

于 2018-05-14T14:22:46.047 回答
0

作为评论后 Mkl 您的伟大建议对于创建新注释非常有用。我确实应用了以下作为“复制”现有注释的方法,其中 note 是“克隆”注释广告 baseAnnotation 源

 foreach (PdfName t in baseAnnotation.BaseDataObject.Keys)
  {
                if (t.Equals(PdfName.DA) || t.Equals(PdfName.DS) || t.Equals(PdfName.RC) || t.Equals(PdfName.Rotate))
                {
                    note.BaseDataObject[t] = baseAnnotation.BaseDataObject[t];
                }
            }

再次感谢

于 2018-05-15T10:41:20.107 回答