PDFClown 复制注释然后操作它们
PDFClown Copy annotations and then manipulate them
我需要将注释从一个 PDF 文件复制到另一个。我使用了优秀的 PDFClown 库,但无法操作颜色、旋转等内容。这可能吗?我可以看到基本对象信息,但不确定如何直接操作它。
我可以通过克隆外观复制外观,但不能"edit"。
提前致谢。
亚历克斯
P.S 如果作者 Stephano 正在收听,项目是否已死?
关于一般注释和特别是标注注释
我调查了一下,恐怕没有多少东西可以使用高级方法确定性地操纵任意输入。原因是有许多替代方法可以设置标注注释的外观,而 PDF Clown 仅支持具有显式高级方法的优先级较低的方法。从高优先级向下
AP 流中的显式外观。如果给出,则使用它,忽略此外观是否看起来像 Callout 注释,更不用说像其他 Callout 属性定义的那样了。
PDF Clown 还没有为其他值的标注注释创建外观,更不用说更新现有外观以跟进某些特定属性(例如 Color
)的更改。对于 ISO 32000-2 支持,这里的 PDF Clown 必须改进,因为外观流已成为强制性的。
如果它存在,您可以使用 getAppearance()
检索外观,但您只会得到一个 FormXObject
及其低级绘图指令,没有特定标注。
给定一个 FormXObject
你可以很容易地操纵一件事,但是,你可以通过相应地设置它的 矩阵 来很容易地旋转或倾斜外观,例如
annotation.getAppearance().getNormal().get(null).setMatrix(AffineTransform.getRotateInstance(100, 10));
RC 字符串或流中的富文本字符串。除非给出外观,否则 Callout 文本框中的文本是从此富文本数据生成的(此处的富文本使用 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 注释本身没有旋转属性(除了标记是否跟随页面旋转)。因此,您不能将旋转设置为简单的注释属性。但是,如果源注释确实有外观流,您可以操纵它的 矩阵 以在注释矩形内旋转它,请参见上文。
边框颜色和字体:如果您的标注有外观流,您可以尝试解析其内容使用 ContentScanner
和操纵颜色和字体设置操作。否则,如果设置了富文本信息,对于字体,您可以尝试使用一些 XML 解析器解析富文本并操纵字体样式属性。否则,您可以解析默认外观DA字符串并操作其字体和颜色设置指令。
一些示例代码
我使用 Adobe Acrobat 创建了一个带有标注注释示例的文件:Callout-Yellow.pdf。它包含外观流、富文本和简单属性,因此可以使用此文件进行不同级别的示例操作。
我将此代码应用到 keepAppearanceStream
和 keepRichText
的不同值(你没有提到你是否将 PDF Clown 用于 Java 或 .Net;所以我选择 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" 或 "..5 g" 的 ". . . rg" 来完成这项工作:字体可以使用不同的样式属性或名称,并且可以使用不同的着色说明。
Adobe 中的屏幕截图
原文件:
keepAppearanceStream = true
:
keepAppearanceStream = false
和 keepRichText = true
:
keepAppearanceStream = false
和 keepRichText = false
:
作为 post 评论 Mkl
您的好建议对创建新注释非常有帮助。我确实将以下方法应用为 "copying" 现有注释的方法,其中 note 是 "cloned" 注释 ad baseAnnotation the source
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];
}
}
再次感谢
我需要将注释从一个 PDF 文件复制到另一个。我使用了优秀的 PDFClown 库,但无法操作颜色、旋转等内容。这可能吗?我可以看到基本对象信息,但不确定如何直接操作它。
我可以通过克隆外观复制外观,但不能"edit"。
提前致谢。 亚历克斯
P.S 如果作者 Stephano 正在收听,项目是否已死?
关于一般注释和特别是标注注释
我调查了一下,恐怕没有多少东西可以使用高级方法确定性地操纵任意输入。原因是有许多替代方法可以设置标注注释的外观,而 PDF Clown 仅支持具有显式高级方法的优先级较低的方法。从高优先级向下
AP 流中的显式外观。如果给出,则使用它,忽略此外观是否看起来像 Callout 注释,更不用说像其他 Callout 属性定义的那样了。
PDF Clown 还没有为其他值的标注注释创建外观,更不用说更新现有外观以跟进某些特定属性(例如
Color
)的更改。对于 ISO 32000-2 支持,这里的 PDF Clown 必须改进,因为外观流已成为强制性的。如果它存在,您可以使用
getAppearance()
检索外观,但您只会得到一个FormXObject
及其低级绘图指令,没有特定标注。给定一个
FormXObject
你可以很容易地操纵一件事,但是,你可以通过相应地设置它的 矩阵 来很容易地旋转或倾斜外观,例如annotation.getAppearance().getNormal().get(null).setMatrix(AffineTransform.getRotateInstance(100, 10));
RC 字符串或流中的富文本字符串。除非给出外观,否则 Callout 文本框中的文本是从此富文本数据生成的(此处的富文本使用 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 注释本身没有旋转属性(除了标记是否跟随页面旋转)。因此,您不能将旋转设置为简单的注释属性。但是,如果源注释确实有外观流,您可以操纵它的 矩阵 以在注释矩形内旋转它,请参见上文。
边框颜色和字体:如果您的标注有外观流,您可以尝试解析其内容使用
ContentScanner
和操纵颜色和字体设置操作。否则,如果设置了富文本信息,对于字体,您可以尝试使用一些 XML 解析器解析富文本并操纵字体样式属性。否则,您可以解析默认外观DA字符串并操作其字体和颜色设置指令。
一些示例代码
我使用 Adobe Acrobat 创建了一个带有标注注释示例的文件:Callout-Yellow.pdf。它包含外观流、富文本和简单属性,因此可以使用此文件进行不同级别的示例操作。
我将此代码应用到 keepAppearanceStream
和 keepRichText
的不同值(你没有提到你是否将 PDF Clown 用于 Java 或 .Net;所以我选择 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" 或 "..5 g" 的 ". . . rg" 来完成这项工作:字体可以使用不同的样式属性或名称,并且可以使用不同的着色说明。
Adobe 中的屏幕截图
原文件:
keepAppearanceStream = true
:keepAppearanceStream = false
和keepRichText = true
:keepAppearanceStream = false
和keepRichText = false
:
作为 post 评论 Mkl 您的好建议对创建新注释非常有帮助。我确实将以下方法应用为 "copying" 现有注释的方法,其中 note 是 "cloned" 注释 ad baseAnnotation the source
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];
}
}
再次感谢