iText 7 在特定位置添加注解
iText 7 Add Annotation at a specific position
我使用 iText7 将 HTML 转换为 PDF,并且需要为 HTML 中的特定文本添加文本标记注释。我正在按照 link and then I am using the annotation examples given here.
中的说明使用 CustomTagWorkers
通过用 link 注释替换 qr 标签,我能够成功添加 Link 注释。但是我的要求是添加文本标记注释。 Text Markup 注释只能通过给出我在代码中不知道的页面(矩形对象)的具体坐标来绘制。我试着给 Rectangle(0, 0)
希望 iText 会渲染它来代替标签。但是我无法将文本标记注释添加到段落对象,该对象是 public IPropertyContainer getElementResult() {
的 return 对象。
这是我的全部代码:
/**
* Converts an HTML file to a PDF document, introducing a custom tag to create a
* QR Code involving a custom TagWorker and a custom CssApplier.
*/
public class C05E04_QRCode2 {
/**
* The path to the resulting PDF file.
*/
public static final String DEST = "C:\Samples\itext-annotation\qrcode.pdf";
/**
* The path to the source HTML file.
*/
public static final String SRC = "C:\Samples\itext-annotation\qrcode.html";
/**
* The main method of this example.
*
* @param args no arguments are needed to run this example.
* @throws IOException signals that an I/O exception has occurred.
*/
public static void main(String[] args) throws IOException {
File file = new File(DEST);
file.getParentFile().mkdirs();
C05E04_QRCode2 app = new C05E04_QRCode2();
System.out.println("pdf");
app.createPdf(SRC, DEST);
}
/**
* Creates the PDF file.
*
* @param src the path to the source HTML file
* @param dest the path to the resulting PDF
* @throws IOException signals that an I/O exception has occurred.
*/
public void createPdf(String src, String dest) throws IOException {
ConverterProperties properties = new ConverterProperties();
properties.setCssApplierFactory(new QRCodeTagCssApplierFactory())
.setTagWorkerFactory(new QRCodeTagWorkerFactory());
HtmlConverter.convertToPdf(new File(src), new File(dest), properties);
}
/**
* A factory for creating QRCodeTagCssApplier objects.
*/
class QRCodeTagCssApplierFactory extends DefaultCssApplierFactory {
/**
* Gets the custom css applier.
*
* @param tag the tag
* @return the custom css applier
*/
/*
* (non-Javadoc)
*
* @see com.itextpdf.html2pdf.css.apply.impl.DefaultCssApplierFactory#
* getCustomCssApplier(com.itextpdf.html2pdf.html.node.IElementNode)
*/
@Override
public ICssApplier getCustomCssApplier(IElementNode tag) {
if (tag.name().equals("qr")) {
return new BlockCssApplier();
}
return null;
}
}
/**
* A factory for creating QRCodeTagWorker objects.
*/
class QRCodeTagWorkerFactory extends DefaultTagWorkerFactory {
/**
* Gets the custom tag worker.
*
* @param tag the tag
* @param context the context
* @return the custom tag worker
*/
@Override
public ITagWorker getCustomTagWorker(IElementNode tag, ProcessorContext context) {
if (tag.name().equals("qr")) {
return new QRCodeTagWorker(tag, context);
}
return null;
}
}
/**
* The custom ITagWorker implementation for the qr-tag.
*/
static class QRCodeTagWorker implements ITagWorker {
/** The p. */
private Paragraph p;
/** The annotation. */
private PdfLinkAnnotation linkAnnotation;
/**
* Instantiates a new QR code tag worker.
*
* @param element the element
* @param context the context
*/
public QRCodeTagWorker(IElementNode element, ProcessorContext context) {
}
/**
* Process content.
*
* @param content the content
* @param context the context
* @return true, if successful
*/
@Override
public boolean processContent(String content, ProcessorContext context) {
return true;
}
/**
* Process tag child.
*
* @param childTagWorker the child tag worker
* @param context the context
* @return true, if successful
*/
@Override
public boolean processTagChild(ITagWorker childTagWorker, ProcessorContext context) {
return false;
}
/**
* Process end.
*
* @param element the element
* @param context the context
*/
@Override
public void processEnd(IElementNode element, ProcessorContext context) {
//Link Annotation
linkAnnotation = new PdfLinkAnnotation(new Rectangle(0, 0)).setAction(PdfAction.createURI(
"https://kb.itextpdf.com/"));
Link link = new Link("here", linkAnnotation);
p = new Paragraph("The example of link annotation. Click ").add(link.setUnderline())
.add(" to learn more...");
//Line Annotation
float[] floatArray = new float[] { 169, 790, 105, 790, 169, 800, 105, 800 };
PdfAnnotation lineAnnotation = PdfTextMarkupAnnotation.createHighLight(new Rectangle(0, 0), floatArray);
lineAnnotation.setTitle(new PdfString("You are here:"));
lineAnnotation.setContents("Cambridge Innovation Center");
lineAnnotation.setColor(ColorConstants.YELLOW);
//Text Markup Annotation
PdfAnnotation ann = PdfTextMarkupAnnotation.createHighLight(
new Rectangle(105, 790, 64, 10),
new float[]{169, 790, 105, 790, 169, 800, 105, 800})
.setColor(Color.YELLOW)
.setTitle(new PdfString("Hello!"))
.setContents(new PdfString("I'm a popup."))
.setTitle(new PdfString("iText"))
.setOpen(true)
.setRectangle(new PdfArray(new float[]{100, 600, 200, 100}));
}
/**
* Gets the element result.
*
* @return the element result
*/
@Override
public IPropertyContainer getElementResult() {
return p;
}
}
}
HTML 文件:
<html>
<head>
<meta charset="UTF-8">
<title>QRCode Example</title>
<link rel="stylesheet" type="text/css" href="css/qrcode.css"/>
</head>
<body>
<span> The example of <qr> text markup </qr> annotation. </span>
</body>
</html>
CSS 文件:
qr {
border: solid 1px red;
height: 200px;
width: 200px;
}
最终输出图
最简单实用的方法是让您的 <qr>
标签表现为内联块。这将确保我们不会将标签中的文本换行到下一行(在这种情况下注释很难定义),而且我们将有一个自然的分组元素,我们将能够从中获取坐标。
我稍微修改了输入 HTML,去掉 <qr>
标签以支持 <annot>
并添加上面提到的 display: inline-block
行为:
<html>
<head>
<style>
annot {
display: inline-block;
}
</style>
</head>
<body>
<span> The example of <annot> text markup </annot> annotation. </span>
</body>
</html>
我们以与原始 post 类似的方式继续定义自定义标签工人工厂和自定义标签工人:
private static class CustomTagWorkerFactory extends DefaultTagWorkerFactory {
@Override
public ITagWorker getCustomTagWorker(IElementNode tag, ProcessorContext context) {
if ("annot".equals(tag.name())) {
return new AnnotTagWorker(tag, context);
}
return super.getCustomTagWorker(tag, context);
}
}
private static class AnnotTagWorker extends PTagWorker {
public AnnotTagWorker(IElementNode element, ProcessorContext context) {
super(element, context);
}
@Override
public IPropertyContainer getElementResult() {
IPropertyContainer baseResult = super.getElementResult();
if (baseResult instanceof Paragraph) {
((Paragraph) baseResult).setNextRenderer(new AnnotTagRenderer((Paragraph) baseResult));
}
return baseResult;
}
}
现在内容丰富的部分将在AnnotTagRenderer
中实现。请注意我们如何将渲染器设置为 AnnotTagWorker
中的结果元素。 reredrer 实现知道与我们在 PDF 页面上的 <annot>
标签相对应的文本块的物理坐标。我们现在可以在 draw()
方法中从 this.getOccupiedArea()
中获取坐标,这对我们来说是主要技巧 - 我们需要做的剩下的就是创建正确的注释对象并将其添加到页面中。这是实现:
private static class AnnotTagRenderer extends ParagraphRenderer {
public AnnotTagRenderer(Paragraph modelElement) {
super(modelElement);
}
@Override
public IRenderer getNextRenderer() {
return new AnnotTagRenderer((Paragraph) modelElement);
}
@Override
public void draw(DrawContext drawContext) {
super.draw(drawContext);
Rectangle occupiedArea = this.getOccupiedAreaBBox();
float[] quadPoints = new float[] {occupiedArea.getLeft(), occupiedArea.getTop(), occupiedArea.getRight(), occupiedArea.getTop(),
occupiedArea.getLeft(), occupiedArea.getBottom(), occupiedArea.getRight(), occupiedArea.getBottom()};
PdfAnnotation ann = PdfTextMarkupAnnotation.createHighLight(
new Rectangle(occupiedArea), quadPoints)
.setColor(ColorConstants.YELLOW)
.setTitle(new PdfString("Hello!"))
.setContents(new PdfString("I'm a popup."))
.setTitle(new PdfString("iText"));
drawContext.getDocument().getPage(this.getOccupiedArea().getPageNumber()).addAnnotation(ann);
}
}
视觉效果如下:
最后,不要忘记将自定义标签工作者工厂插入到转换器属性中:
ConverterProperties properties = new ConverterProperties().setTagWorkerFactory(new CustomTagWorkerFactory());
HtmlConverter.convertToPdf(new File(sourceHTML), new File(targetPDF), properties);
我使用 iText7 将 HTML 转换为 PDF,并且需要为 HTML 中的特定文本添加文本标记注释。我正在按照 link and then I am using the annotation examples given here.
中的说明使用 CustomTagWorkers通过用 link 注释替换 qr 标签,我能够成功添加 Link 注释。但是我的要求是添加文本标记注释。 Text Markup 注释只能通过给出我在代码中不知道的页面(矩形对象)的具体坐标来绘制。我试着给 Rectangle(0, 0)
希望 iText 会渲染它来代替标签。但是我无法将文本标记注释添加到段落对象,该对象是 public IPropertyContainer getElementResult() {
的 return 对象。
这是我的全部代码:
/**
* Converts an HTML file to a PDF document, introducing a custom tag to create a
* QR Code involving a custom TagWorker and a custom CssApplier.
*/
public class C05E04_QRCode2 {
/**
* The path to the resulting PDF file.
*/
public static final String DEST = "C:\Samples\itext-annotation\qrcode.pdf";
/**
* The path to the source HTML file.
*/
public static final String SRC = "C:\Samples\itext-annotation\qrcode.html";
/**
* The main method of this example.
*
* @param args no arguments are needed to run this example.
* @throws IOException signals that an I/O exception has occurred.
*/
public static void main(String[] args) throws IOException {
File file = new File(DEST);
file.getParentFile().mkdirs();
C05E04_QRCode2 app = new C05E04_QRCode2();
System.out.println("pdf");
app.createPdf(SRC, DEST);
}
/**
* Creates the PDF file.
*
* @param src the path to the source HTML file
* @param dest the path to the resulting PDF
* @throws IOException signals that an I/O exception has occurred.
*/
public void createPdf(String src, String dest) throws IOException {
ConverterProperties properties = new ConverterProperties();
properties.setCssApplierFactory(new QRCodeTagCssApplierFactory())
.setTagWorkerFactory(new QRCodeTagWorkerFactory());
HtmlConverter.convertToPdf(new File(src), new File(dest), properties);
}
/**
* A factory for creating QRCodeTagCssApplier objects.
*/
class QRCodeTagCssApplierFactory extends DefaultCssApplierFactory {
/**
* Gets the custom css applier.
*
* @param tag the tag
* @return the custom css applier
*/
/*
* (non-Javadoc)
*
* @see com.itextpdf.html2pdf.css.apply.impl.DefaultCssApplierFactory#
* getCustomCssApplier(com.itextpdf.html2pdf.html.node.IElementNode)
*/
@Override
public ICssApplier getCustomCssApplier(IElementNode tag) {
if (tag.name().equals("qr")) {
return new BlockCssApplier();
}
return null;
}
}
/**
* A factory for creating QRCodeTagWorker objects.
*/
class QRCodeTagWorkerFactory extends DefaultTagWorkerFactory {
/**
* Gets the custom tag worker.
*
* @param tag the tag
* @param context the context
* @return the custom tag worker
*/
@Override
public ITagWorker getCustomTagWorker(IElementNode tag, ProcessorContext context) {
if (tag.name().equals("qr")) {
return new QRCodeTagWorker(tag, context);
}
return null;
}
}
/**
* The custom ITagWorker implementation for the qr-tag.
*/
static class QRCodeTagWorker implements ITagWorker {
/** The p. */
private Paragraph p;
/** The annotation. */
private PdfLinkAnnotation linkAnnotation;
/**
* Instantiates a new QR code tag worker.
*
* @param element the element
* @param context the context
*/
public QRCodeTagWorker(IElementNode element, ProcessorContext context) {
}
/**
* Process content.
*
* @param content the content
* @param context the context
* @return true, if successful
*/
@Override
public boolean processContent(String content, ProcessorContext context) {
return true;
}
/**
* Process tag child.
*
* @param childTagWorker the child tag worker
* @param context the context
* @return true, if successful
*/
@Override
public boolean processTagChild(ITagWorker childTagWorker, ProcessorContext context) {
return false;
}
/**
* Process end.
*
* @param element the element
* @param context the context
*/
@Override
public void processEnd(IElementNode element, ProcessorContext context) {
//Link Annotation
linkAnnotation = new PdfLinkAnnotation(new Rectangle(0, 0)).setAction(PdfAction.createURI(
"https://kb.itextpdf.com/"));
Link link = new Link("here", linkAnnotation);
p = new Paragraph("The example of link annotation. Click ").add(link.setUnderline())
.add(" to learn more...");
//Line Annotation
float[] floatArray = new float[] { 169, 790, 105, 790, 169, 800, 105, 800 };
PdfAnnotation lineAnnotation = PdfTextMarkupAnnotation.createHighLight(new Rectangle(0, 0), floatArray);
lineAnnotation.setTitle(new PdfString("You are here:"));
lineAnnotation.setContents("Cambridge Innovation Center");
lineAnnotation.setColor(ColorConstants.YELLOW);
//Text Markup Annotation
PdfAnnotation ann = PdfTextMarkupAnnotation.createHighLight(
new Rectangle(105, 790, 64, 10),
new float[]{169, 790, 105, 790, 169, 800, 105, 800})
.setColor(Color.YELLOW)
.setTitle(new PdfString("Hello!"))
.setContents(new PdfString("I'm a popup."))
.setTitle(new PdfString("iText"))
.setOpen(true)
.setRectangle(new PdfArray(new float[]{100, 600, 200, 100}));
}
/**
* Gets the element result.
*
* @return the element result
*/
@Override
public IPropertyContainer getElementResult() {
return p;
}
}
}
HTML 文件:
<html>
<head>
<meta charset="UTF-8">
<title>QRCode Example</title>
<link rel="stylesheet" type="text/css" href="css/qrcode.css"/>
</head>
<body>
<span> The example of <qr> text markup </qr> annotation. </span>
</body>
</html>
CSS 文件:
qr {
border: solid 1px red;
height: 200px;
width: 200px;
}
最终输出图
最简单实用的方法是让您的 <qr>
标签表现为内联块。这将确保我们不会将标签中的文本换行到下一行(在这种情况下注释很难定义),而且我们将有一个自然的分组元素,我们将能够从中获取坐标。
我稍微修改了输入 HTML,去掉 <qr>
标签以支持 <annot>
并添加上面提到的 display: inline-block
行为:
<html>
<head>
<style>
annot {
display: inline-block;
}
</style>
</head>
<body>
<span> The example of <annot> text markup </annot> annotation. </span>
</body>
</html>
我们以与原始 post 类似的方式继续定义自定义标签工人工厂和自定义标签工人:
private static class CustomTagWorkerFactory extends DefaultTagWorkerFactory {
@Override
public ITagWorker getCustomTagWorker(IElementNode tag, ProcessorContext context) {
if ("annot".equals(tag.name())) {
return new AnnotTagWorker(tag, context);
}
return super.getCustomTagWorker(tag, context);
}
}
private static class AnnotTagWorker extends PTagWorker {
public AnnotTagWorker(IElementNode element, ProcessorContext context) {
super(element, context);
}
@Override
public IPropertyContainer getElementResult() {
IPropertyContainer baseResult = super.getElementResult();
if (baseResult instanceof Paragraph) {
((Paragraph) baseResult).setNextRenderer(new AnnotTagRenderer((Paragraph) baseResult));
}
return baseResult;
}
}
现在内容丰富的部分将在AnnotTagRenderer
中实现。请注意我们如何将渲染器设置为 AnnotTagWorker
中的结果元素。 reredrer 实现知道与我们在 PDF 页面上的 <annot>
标签相对应的文本块的物理坐标。我们现在可以在 draw()
方法中从 this.getOccupiedArea()
中获取坐标,这对我们来说是主要技巧 - 我们需要做的剩下的就是创建正确的注释对象并将其添加到页面中。这是实现:
private static class AnnotTagRenderer extends ParagraphRenderer {
public AnnotTagRenderer(Paragraph modelElement) {
super(modelElement);
}
@Override
public IRenderer getNextRenderer() {
return new AnnotTagRenderer((Paragraph) modelElement);
}
@Override
public void draw(DrawContext drawContext) {
super.draw(drawContext);
Rectangle occupiedArea = this.getOccupiedAreaBBox();
float[] quadPoints = new float[] {occupiedArea.getLeft(), occupiedArea.getTop(), occupiedArea.getRight(), occupiedArea.getTop(),
occupiedArea.getLeft(), occupiedArea.getBottom(), occupiedArea.getRight(), occupiedArea.getBottom()};
PdfAnnotation ann = PdfTextMarkupAnnotation.createHighLight(
new Rectangle(occupiedArea), quadPoints)
.setColor(ColorConstants.YELLOW)
.setTitle(new PdfString("Hello!"))
.setContents(new PdfString("I'm a popup."))
.setTitle(new PdfString("iText"));
drawContext.getDocument().getPage(this.getOccupiedArea().getPageNumber()).addAnnotation(ann);
}
}
视觉效果如下:
最后,不要忘记将自定义标签工作者工厂插入到转换器属性中:
ConverterProperties properties = new ConverterProperties().setTagWorkerFactory(new CustomTagWorkerFactory());
HtmlConverter.convertToPdf(new File(sourceHTML), new File(targetPDF), properties);