来源
这个答案涵盖了OP通过评论中的谷歌驱动器链接提供的更新的源代码:
public static void main(String[] args) throws Exception {
PdfReader reader = new PdfReader("src");
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("dest"));
Rectangle location = new Rectangle(544.8f, 517.65f, 663f, 373.35f);
PdfArray lineEndings = new PdfArray();
lineEndings.add(new PdfName("None"));
lineEndings.add(new PdfName("None"));
PdfAnnotation stamp = PdfAnnotation.createLine(stamper.getWriter(), location,
"comment", 550.05f, 510.9f, 656.25f, 378.6f);
stamp.put(new PdfName("LE"), lineEndings);
stamp.put(new PdfName("IT"), new PdfName("Line"));
stamp.setBorderStyle(new PdfBorderDictionary(1, PdfBorderDictionary.STYLE_SOLID));
stamp.setColor(PdfGraphics2D.prepareColor(Color.RED));
stamp.put(PdfName.ROTATE, new PdfNumber(270));
stamper.addAnnotation(stamp, 1);
addAppearance(stamper, stamp, location);
stamper.close();
reader.close();
}
private static void addAppearance(PdfStamper stamper, PdfAnnotation stamp, Rectangle location) {
PdfContentByte cb = stamper.getOverContent(1);
PdfAppearance app = cb.createAppearance(location.getWidth(), location.getHeight());
PdfArray rect = stamp.getAsArray(PdfName.RECT);
Rectangle bbox = app.getBoundingBox();
double[] lineArray = stamp.getAsArray(PdfName.L).asDoubleArray();
double x1 = lineArray[0] - rect.getAsNumber(0).doubleValue();
double y1 = lineArray[1] - rect.getAsNumber(1).doubleValue();
double x2 = lineArray[2] - rect.getAsNumber(0).doubleValue();
double y2 = lineArray[3] - rect.getAsNumber(1).doubleValue();
app.moveTo(x1, y1);
app.lineTo(x2, y2);
app.stroke();
stamp.setAppearance(PdfName.N, app);
}
没有出现
在 Chrome 中查看生成的 PDF 时的第一个观察结果是,正如 OP 在评论中所说的那样:
什么也没有出现
检查 PDF,原因很清楚:注释没有外观流。因此,有限的 PDF 查看器只能通过外观流显示注释,而不能通过描述性值显示注释,例如 Chrome 中的集成查看器不会显示注释。
这是由于 OP 在其代码中调用 iText 功能的顺序造成的:
[... create annotation object stamp ...]
stamper.addAnnotation(stamp, 1);
addAppearance(stamper, stamp, location);
所以他首先通过以下方式将注释添加到PDF中:stamper.addAnnotation
然后创建一个外观并将其附加到stamp
object.
这个顺序是错误的。在使用 iText 的情况下,我们必须意识到该库会尝试尽早将添加内容写入输出流以减少其内存占用。 (顺便说一句,这是 iText 在服务器应用程序环境中的重要功能之一,在服务器应用程序中可能必须并行处理多个 PDF。)
所以已经在stamper.addAnnotation(stamp, 1)
注解被写入输出流,由于还没有出现,所以输出流中的注解是没有出现的。后来的addAppearance
调用仅向注释的内存中表示添加外观,该外观将不再被序列化。
将顺序更改为
[... create annotation object stamp ...]
addAppearance(stamper, stamp, location);
stamper.addAnnotation(stamp, 1);
生成带有画线的 PDF。不幸的是,没有达到所需的位置,但这是另一个问题。
位置错误
这条线的位置和方向都错误的原因是基于 iText 的一个特性,该特性已经成为一个话题这个答案 https://stackoverflow.com/a/43466487/1729265 and in 这个答案 https://stackoverflow.com/a/40930180/1729265:
对于旋转页面,iText 试图减轻在绘制直立文本所需的页面内容中添加旋转和平移的负担,并将坐标系原点设置在用户肩膀的页面左下角,这样用户就不会完全处理页面旋转。因此,它对于注释也是如此。
由于您已经有了要使用的实际坐标,iText 的这种“帮助”会损坏您的注释。正如其他答案中所讨论的,不幸的是没有明确的开关来关闭该机制;不过,有一个简单的解决方法:在进行操作之前,只需删除页面旋转条目,然后再次将其添加回来:
PdfReader reader = ...;
PdfStamper stamper = ...;
// hide the page rotation
PdfDictionary pageDict = reader.getPageN(1);
PdfNumber rotation = pageDict.getAsNumber(PdfName.ROTATE);
pageDict.remove(PdfName.ROTATE);
Rectangle location = new Rectangle(544.8f, 517.65f, 663f, 373.35f);
PdfArray lineEndings = new PdfArray();
lineEndings.add(new PdfName("None"));
lineEndings.add(new PdfName("None"));
PdfAnnotation stamp = PdfAnnotation.createLine(stamper.getWriter(), location,
"comment", 550.05f, 510.9f, 656.25f, 378.6f);
stamp.put(new PdfName("LE"), lineEndings);
stamp.put(new PdfName("IT"), new PdfName("Line"));
stamp.setBorderStyle(new PdfBorderDictionary(1, PdfBorderDictionary.STYLE_SOLID));
stamp.setColor(PdfGraphics2D.prepareColor(Color.RED));
stamp.put(PdfName.ROTATE, new PdfNumber(270));
addAppearance(stamper, stamp, location);
stamper.addAnnotation(stamp, 1);
// add page rotation again if required
if (rotation != null)
pageDict.put(PdfName.ROTATE, rotation);
stamper.close();
reader.close();
这似乎是根据需要创建注释外观。