Change page orientation for only some pages in the resulting PDF (created out of html)

自作多情 提交于 2021-02-17 05:13:21

问题


I would like to change page orientation for just specific few pages in my PDF document. The PDF document is created out of html template using html2pdf. It goes like this: if the content of the page (typically a table) is too wide to be properly shown in portrait orientation, show page in landscape.

Following the hint in [how to rotate pages into landscape and page content should be in portrait iTextpdf][1]

[1]: how to rotate pages into landscape and page content should be in portrait iTextpdf I have created my custom tag and TagWorker.

public class LandscapeTagWorker extends BodyTagWorker {

public LandscapeTagWorker(IElementNode element, ProcessorContext context) {
    super(element, context);
}

/**
 * @param element
 * @param context
 * @see com.itextpdf.html2pdf.attach.ITagWorker#processEnd(com.itextpdf.html2pdf.html.node.IElementNode, com.itextpdf.html2pdf.attach.ProcessorContext)
 */
@Override
public void processEnd(IElementNode element, ProcessorContext context) {
    super.processEnd(element, context);
    String value = element.getAttribute("value");
     if ( "true".equalsIgnoreCase(value) ) {
        PdfDocument doc = context.getPdfDocument();

        doc.setDefaultPageSize(doc.getDefaultPageSize().rotate());
     }
}

}

The problems are: first, this does nothing. Even if it would work, I do not want to change the orientation of the whole document, just orientation of the pages where the content of the <landscape value="true">is found.

How can I extract the current page(s) out of ProcessorContext/PdfDocument and how to change page orientation of only those pages?


回答1:


In the answer below I'm going to show how tables with the class attribute set as landscape can be procesed in the way you want.

First of all, let's create a custom css applier for such tables. Its purpose would be to set a custom property on a table tag (for example, 10001). Then during the layout we will check that property and decide whether to change the pagesize or not.

    class CustomCssApplierFactory extends DefaultCssApplierFactory {
    @Override
    public ICssApplier getCustomCssApplier(IElementNode tag) {
        if ("table".equals(tag.name()) && "landscape".equals(tag.getAttribute(AttributeConstants.CLASS))) {
            return new CustomLandscapeCssApplier();
        }
        return null;
    }
}

class CustomLandscapeCssApplier extends TableTagCssApplier {
    @Override
    public void apply(ProcessorContext context, IStylesContainer stylesContainer, ITagWorker tagWorker) {
        super.apply(context, stylesContainer, tagWorker);
        IPropertyContainer container = tagWorker.getElementResult();
        if (null != container) {
            container.setProperty(10001, true);
        }
    }
}

Now let's convert your html not to a pdf file, but to a list of IElements which form that html (you're particularly interested in the last line):

    PdfDocument pdfDocument = new PdfDocument(new PdfWriter(pdfDest));
    Document document = new Document(pdfDocument);

    ConverterProperties converterProperties = new ConverterProperties();
    converterProperties.setCssApplierFactory(new CustomCssApplierFactory());
    List<IElement> list = HtmlConverter.convertToElements(new FileInputStream(htmlSource), converterProperties);

Now let's add these elements to the document one by one and check whether the current element (the element to be added) has the magic 10001 property. If not then just add the element. If it exists, let's break the page and set the size of the next page while breaking:

        for (IElement element : list) {
        if (element instanceof IBlockElement) {
            if (!element.hasProperty(10001)) {
                document.add((IBlockElement) element);
            } else {
                document.add(new AreaBreak(new PageSize(PageSize.A4).rotate()));
                document.add((IBlockElement) element);
                document.getPdfDocument().setDefaultPageSize(PageSize.A4);
            }
        }
    }

As you can see in the snippet above, I broke the page (setting the size of the next page), added the element and then again set the size of the next layout area, so that the next page after the one that contains the element would have the default pagesize).

That's how the result looks like:

There are some limitations however:

1) Generally speaking, the pdf file is not just a set of elements rendered on the page. Under HtmlConverter.convertToPdf iText handles some other features as well (outlines, etc). However it seems that for your purposes the method works perfectly.

2) As you can see, I do not check whether there are some tables which are not the direct children of the body tag. Actually, in order to perform correctly in a general case, you need to modify the loop: check the children of all the elements (probably with `AbstractElement#getChildren()) and place some AreaBreaks inside them. But that feels like another question.



来源:https://stackoverflow.com/questions/54215613/change-page-orientation-for-only-some-pages-in-the-resulting-pdf-created-out-of

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!