【问题标题】:iText 7 Add Annotation at a specific positioniText 7 在特定位置添加注释
【发布时间】:2021-08-04 20:47:26
【问题描述】:

我使用 iText7 将 HTML 转换为 PDF,并且需要为 HTML 中的特定文本添加文本标记注释。我正在使用 link 中解释的 CustomTagWorkers,然后使用 here 给出的注释示例。

我可以通过将 qr 标记替换为链接注释来成功添加链接注释。但是我的要求是添加一个文本标记注释。 Text Markup注解只能通过给出我在代码中不知道的页面(矩形对象)的具体坐标来绘制。我试图给Rectangle(0, 0),希望iText 能代替标签呈现它。但是我无法将文本标记注释添加到段落对象,这是public IPropertyContainer getElementResult() { 的返回对象。

这是我的全部代码:

/**
 * 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;
}

最终输出的插图

【问题讨论】:

  • 好的,您希望在页面的哪个位置绘制线条注释?可以加个插图吗?
  • @AlexeySubach - 关于您询问的位置,HTML 是动态生成的。所以我们不知道它会在哪里。但是我们想在找到 标签的任何地方添加注释。
  • Qr 标签包含一些文本,可以是多行的。是否要将注释放在第一行和第二行的中间?在第一行之上?最后一行的底部?或者您确定文本不会跨行拆分?另外,线注释是您真正想要的,还是您只想要视觉下划线?
  • @AlexeySubach - 在查看了其他注释示例之后,看起来文本标记注释适合我的用例。我更新了我的原始帖子,以包含我想要的视觉说明。在图像“文本标记”中有一个文本标记注释。在我们的示例中, 标签将包含“文本标记”。我更新了原始帖子中的代码以包含文本标记注释。如何计算 new Rectangle(105, 790, 64, 10), new float[]{169, 790, 105, 790, 169, 800, 105, 800}) 的值

标签: itext itext7


【解决方案1】:

最简单实用的方法是让您的&lt;qr&gt; 标记表现为内联块。这将确保我们不会将标签中的文本换行到下一行(在这种情况下,注释很难定义),并且我们将拥有一个自然的分组元素,我们将能够从中获取坐标。

我稍微修改了输入 HTML,去掉了&lt;qr&gt; 标签,改成了&lt;annot&gt;,并添加了上面提到的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>

我们以与原帖类似的方式继续定义自定义标签工作者工厂和自定义标签工作者:

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 中的结果元素。渲染器实现知道与 PDF 页面上的&lt;annot&gt; 标签对应的文本块的物理坐标。我们现在可以在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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    相关资源
    最近更新 更多