Search code examples
itextitext7

iText 7 text annotation styling issue


Using the approach from post I was able to successfully create a text annotation for specific text. However I ran into a styling issue using this approach.

My dynamically generated HTML has a p tag with negative text-indent. The custom tag is a child of this p tag. Now the problem is the custom annot tag is inheriting the negative text-indent from it's parent which is making the content overlap with the preceding text. I tried to override the text-indent property by adding 0pt style for annot tag. It works fine when I open the HTML directly in the browser. But iText doesn't consider this style while generating the final output. Please see the sample below.

PDF Output:

enter image description here

HTML Code

<html>
<meta charset="utf-8">
<title>iText test</title> 
    <head>
        <style type="text/css">
                annot {
                display: inline-block;
                text-indent:0px !important;
            }
        </style> 
    </head> 
    <body>
        <div>
            <p style="margin-left:18pt; text-indent:-18pt; page-break-inside:avoid; page-break-after:avoid; line-height:120%"> The example of <annot> text markup </annot> annotation. </p>
        </div>
    </body>
</html>

Java Code:

public class C05E04_QRCode2 {

/**
 * The path to the resulting PDF file.
 */
public static final String DEST = "C:\\dest.pdf";

/**
 * The path to the source HTML file.
 */
public static final String SRC = "C:\\source.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().setTagWorkerFactory(new CustomTagWorkerFactory());
    HtmlConverter.convertToPdf(new File(SRC), new File(DEST), properties);
}


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

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);
    }
}

}


Solution

  • By default the <annot> tag would be ignored in terms of CSS properties processing, so your text-indent: 0px !important declaration is essentially ignored. To enable CSS processing for custom tags, use a custom CSS applier factory:

    private static class CustomCssApplierFactory extends DefaultCssApplierFactory {
        @Override
        public ICssApplier getCustomCssApplier(IElementNode tag) {
            if ("annot".equals(tag.name())) {
                return new BlockCssApplier();
            }
            return super.getCustomCssApplier(tag);
        }
    }
    

    It's plugged into pdfHTML via ConverterProperties as well:

    ConverterProperties properties = new ConverterProperties()
            .setTagWorkerFactory(new CustomTagWorkerFactory())
            .setCssApplierFactory(new CustomCssApplierFactory());