We have HTML's for which we incorporate print layouts with mixed landscape and portrait pages. The page orientation is applied with the page rules to keep it clean and maintainable.
It seems html2pdf is completely ignoring this, as per example:
<html>
<head>
<title>UnitTest</title>
<style type="text/css">* {
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
}
@page page-portrait {
size: A4;
orientation: portait;
}
@page page-landscape {
size: A4;
orientation: landscape;
}
.page-portrait {
page-break-after: always;
page: page-portrait;
}
.page-landscapes {
page-break-after: always;
page: page-landscape;
}
</style>
</head>
<body>
<div class="page-portrait">
<p>This should be a portrait page</p>
</div>
<div class="page-landscapes">
<p>This should be a landscape page</p>
</div>
<div class="page-portrait">
<p>This should be a portrait page</p>
</div>
<div class="page-landscapes">
<p>This should be a landscape page</p>
</div>
</body>
</html>
The best solution we have managed to find until now is the following, it works based on DIV level CSS class assignment to control the page orientation. It can be extended to work for other HTML tags and/or CSS classes.
The solution is designed to notify a page listener with the right orientation to apply, and rotating the page as needed. The tag worker inspects the HTML and looks for specific CSS classes to signal the page listener with the orientation found.
The benefit of this approach is that it allows to keep things 'simple' and use the 'HtmlConverter.convertToPdf' method which avoids lossing certain features baked into the underlying implementation.
PageOrientationsEventHandler eventHandler = new PageOrientationsEventHandler(); pdfDocument.addEventHandler(PdfDocumentEvent.START_PAGE, eventHandler);
converterProperties.setTagWorkerFactory(
new DefaultTagWorkerFactory() {
@Override
public ITagWorker getCustomTagWorker(IElementNode tag, ProcessorContext context) {
if (HTML_DIV.equalsIgnoreCase(tag.name())) {
return new CustomDivTagWorker(tag, context, pdfDocument, eventHandler);
}
return null;
}
});
// Run the conversion
HtmlConverter.convertToPdf(fis, pdfDocument, converterProperties);
And following referenced constants:
private static final String CSS_PAGE_PORTRAIT = "page";
private static final String CSS_PAGE_LANDSCAPE = "page-landscape";
And following referenced classes:
private static final class PageOrientationsEventHandler implements IEventHandler {
public static final PdfNumber PORTRAIT = new PdfNumber(0);
public static final PdfNumber LANDSCAPE = new PdfNumber(90);
private PdfNumber orientation = PORTRAIT;
private Map<Integer, PdfNumber> orientationHistory = new HashMap<Integer, PdfNumber>();
public void setOrientation(PdfNumber orientation) {
this.orientation = orientation;
}
@Override
public void handleEvent(Event currentEvent) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) currentEvent;
PdfPage page = docEvent.getPage();
// Check if we already rendered this page before
int pageNr = docEvent.getDocument().getPageNumber(page);
if(orientationHistory.containsKey(pageNr)) {
// We did, use the same orientation
// as we re-render we have lost track of the right orientation instrucrion
// since it only works well on 1st pass, not on subsequent renders
orientation = orientationHistory.get(pageNr);
} else {
// First render pass, store orientation
orientationHistory.put(pageNr, orientation);
}
// Toggle orientation
page.setIgnorePageRotationForContent(LANDSCAPE.equals(orientation));
page.put(PdfName.Rotate, orientation);
}
}
private static final class CustomDivTagWorker extends DivTagWorker {
private IElementNode element;
private PageOrientationsEventHandler eventHandler;
public CustomDivTagWorker(IElementNode element, ProcessorContext context, PdfDocument pdfDocument, PageOrientationsEventHandler eventHandler) {
super(element, context);
this.element = element;
this.eventHandler = eventHandler;
}
@Override
public IPropertyContainer getElementResult() {
IPropertyContainer baseElementResult = super.getElementResult();
// We are interested in Divs only
if (baseElementResult instanceof Div) {
// Check landscape based on class identifier
boolean landscape = false;
String cssClass = element.getAttribute(AttributeConstants.CLASS);
if (CSS_PAGE_LANDSCAPE.equals(cssClass)) {
landscape=true;
} else if (CSS_PAGE_PORTRAIT.equals(cssClass)) {
landscape=false;
}
// Flag requested orientation to our start page handler
if(cssClass!=null && cssClass.length()>0) {
eventHandler.setOrientation(landscape?PageOrientationsEventHandler.LANDSCAPE:PageOrientationsEventHandler.PORTRAIT);
}
}
return baseElementResult;
}
}