com.openhtmltopdf.java2d.Java2DRenderer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openhtmltopdf-java2d Show documentation
Show all versions of openhtmltopdf-java2d Show documentation
Openhtmltopdf is a CSS 2.1 renderer written in Java. This artifact supports image output with the Java2D API.
package com.openhtmltopdf.java2d;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.io.*;
import java.util.List;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import com.openhtmltopdf.bidi.BidiReorderer;
import com.openhtmltopdf.bidi.BidiSplitter;
import com.openhtmltopdf.bidi.BidiSplitterFactory;
import com.openhtmltopdf.bidi.SimpleBidiReorderer;
import com.openhtmltopdf.context.StyleReference;
import com.openhtmltopdf.css.style.CalculatedStyle;
import com.openhtmltopdf.extend.*;
import com.openhtmltopdf.java2d.api.FSPage;
import com.openhtmltopdf.java2d.api.FSPageProcessor;
import com.openhtmltopdf.java2d.api.IJava2DRenderer;
import com.openhtmltopdf.layout.BoxBuilder;
import com.openhtmltopdf.layout.Layer;
import com.openhtmltopdf.layout.LayoutContext;
import com.openhtmltopdf.layout.SharedContext;
import com.openhtmltopdf.outputdevice.helper.BaseDocument;
import com.openhtmltopdf.outputdevice.helper.NullUserInterface;
import com.openhtmltopdf.outputdevice.helper.PageDimensions;
import com.openhtmltopdf.outputdevice.helper.UnicodeImplementation;
import com.openhtmltopdf.render.BlockBox;
import com.openhtmltopdf.render.PageBox;
import com.openhtmltopdf.render.RenderingContext;
import com.openhtmltopdf.render.ViewportBox;
import com.openhtmltopdf.resource.XMLResource;
import com.openhtmltopdf.simple.extend.XhtmlNamespaceHandler;
import com.openhtmltopdf.swing.NaiveUserAgent;
import com.openhtmltopdf.util.Configuration;
import com.openhtmltopdf.util.ThreadCtx;
import com.openhtmltopdf.util.XRLog;
public class Java2DRenderer implements IJava2DRenderer, Closeable {
private final List _domMutators;
private final SVGDrawer _mathMLImpl;
private BlockBox _root;
private final SharedContext _sharedContext;
private final Java2DOutputDevice _outputDevice;
private BidiSplitterFactory _splitterFactory;
private byte _defaultTextDirection = BidiSplitter.LTR;
private BidiReorderer _reorderer;
private final SVGDrawer _svgImpl;
private Document _doc;
private final FSObjectDrawerFactory _objectDrawerFactory;
private final FSPageProcessor _pageProcessor;
private static final int DEFAULT_DOTS_PER_PIXEL = 1;
private static final int DEFAULT_DPI = 72;
private final int _initialPageNo;
private final short _pagingMode;
/**
* Subject to change. Not public API. Used exclusively by the Java2DRendererBuilder class.
* @param _svgImpl
* @param preferredTransformerFactoryImplementationClass
* @param _domMutators
*/
public Java2DRenderer(
BaseDocument doc,
UnicodeImplementation unicode,
HttpStreamFactory httpStreamFactory,
FSUriResolver resolver,
FSCache cache,
SVGDrawer _svgImpl, SVGDrawer _mathMLImpl,
PageDimensions pageSize,
String replacementText,
boolean testMode,
FSPageProcessor pageProcessor,
Graphics2D layoutGraphics,
int initialPageNumber, short pagingMode,
FSObjectDrawerFactory objectDrawerFactory,
String preferredTransformerFactoryImplementationClass,
String preferredDocumentBuilderFactoryImplementationClass,
List _domMutators) {
_pagingMode = pagingMode;
_pageProcessor = pageProcessor;
_initialPageNo = initialPageNumber;
this._svgImpl = _svgImpl;
this._mathMLImpl = _mathMLImpl;
this._domMutators = _domMutators;
_objectDrawerFactory = objectDrawerFactory;
_outputDevice = new Java2DOutputDevice(layoutGraphics);
NaiveUserAgent uac = new NaiveUserAgent();
if (httpStreamFactory != null) {
uac.setHttpStreamFactory(httpStreamFactory);
}
if (resolver != null) {
uac.setUriResolver(resolver);
}
if (cache != null) {
uac.setExternalCache(cache);
}
_sharedContext = new SharedContext();
_sharedContext.registerWithThread();
_sharedContext._preferredTransformerFactoryImplementationClass = preferredTransformerFactoryImplementationClass;
_sharedContext._preferredDocumentBuilderFactoryImplementationClass = preferredDocumentBuilderFactoryImplementationClass;
_sharedContext.setUserAgentCallback(uac);
_sharedContext.setCss(new StyleReference(uac));
// uac.setSharedContext(_sharedContext);
// _outputDevice.setSharedContext(_sharedContext);
Java2DFontResolver fontResolver = new Java2DFontResolver(_sharedContext);
_sharedContext.setFontResolver(fontResolver);
Java2DReplacedElementFactory replacedFactory = new Java2DReplacedElementFactory(this._svgImpl,
_objectDrawerFactory, this._mathMLImpl);
_sharedContext.setReplacedElementFactory(replacedFactory);
_sharedContext.setTextRenderer(new Java2DTextRenderer());
_sharedContext.setDPI(DEFAULT_DPI * DEFAULT_DOTS_PER_PIXEL);
_sharedContext.setDotsPerPixel(DEFAULT_DOTS_PER_PIXEL);
_sharedContext.setPrint(true);
_sharedContext.setInteractive(false);
_sharedContext.setDefaultPageSize(pageSize.w, pageSize.h, pageSize.isSizeInches);
if (replacementText != null) {
_sharedContext.setReplacementText(replacementText);
}
if (unicode.splitterFactory != null) {
this._splitterFactory = unicode.splitterFactory;
}
if (unicode.reorderer != null) {
this._reorderer = unicode.reorderer;
this._outputDevice.setBidiReorderer(_reorderer);
}
if (unicode.lineBreaker != null) {
_sharedContext.setLineBreaker(unicode.lineBreaker);
}
if (unicode.charBreaker != null) {
_sharedContext.setCharacterBreaker(unicode.charBreaker);
}
if (unicode.toLowerTransformer != null) {
_sharedContext.setUnicodeToLowerTransformer(unicode.toLowerTransformer);
}
if (unicode.toUpperTransformer != null) {
_sharedContext.setUnicodeToUpperTransformer(unicode.toUpperTransformer);
}
if (unicode.toTitleTransformer != null) {
_sharedContext.setUnicodeToTitleTransformer(unicode.toTitleTransformer);
}
this._defaultTextDirection = unicode.textDirection ? BidiSplitter.RTL : BidiSplitter.LTR;
if (doc.html != null) {
this.setDocumentFromString(doc.html, doc.baseUri);
}
else if (doc.document != null) {
this.setDocument(doc.document, doc.baseUri);
}
else if (doc.uri != null) {
this.setDocument(doc.uri);
}
else if (doc.file != null) {
try {
this.setDocument(doc.file);
} catch (IOException e) {
XRLog.exception("Problem trying to read input XHTML file", e);
throw new RuntimeException("File IO problem", e);
}
}
}
private void setDocumentFromString(String content, String baseUrl) {
InputSource is = new InputSource(new BufferedReader(new StringReader(content)));
Document dom = XMLResource.load(is).getDocument();
setDocument(dom, baseUrl);
}
private void setDocument(Document doc, String url) {
setDocument(doc, url, new XhtmlNamespaceHandler());
}
private void setDocument(File file) throws IOException {
File parent = file.getAbsoluteFile().getParentFile();
setDocument(loadDocument(file.toURI().toURL().toExternalForm()), (parent == null ? "" : parent.toURI().toURL().toExternalForm()));
}
private void setDocument(String uri) {
setDocument(loadDocument(uri), uri);
}
private Document loadDocument(String uri) {
return _sharedContext.getUserAgentCallback().getXMLResource(uri).getDocument();
}
private void setDocument(Document doc, String url, NamespaceHandler nsh) {
_doc = doc;
/*
* Apply potential DOM mutations
*/
for (FSDOMMutator domMutator : _domMutators)
domMutator.mutateDocument(doc);
//TODOgetFontResolver().flushFontFaceFonts();
_sharedContext.reset();
if (Configuration.isTrue("xr.cache.stylesheets", true)) {
_sharedContext.getCss().flushStyleSheets();
} else {
_sharedContext.getCss().flushAllStyleSheets();
}
_sharedContext.setBaseURL(url);
_sharedContext.setNamespaceHandler(nsh);
_sharedContext.getCss().setDocumentContext(_sharedContext, _sharedContext.getNamespaceHandler(), doc, new NullUserInterface());
getFontResolver().importFontFaces(_sharedContext.getCss().getFontFaceRules());
if (_svgImpl != null) {
_svgImpl.importFontFaceRules(_sharedContext.getCss().getFontFaceRules(), _sharedContext);
}
if (_mathMLImpl != null) {
_mathMLImpl.importFontFaceRules(_sharedContext.getCss().getFontFaceRules(), _sharedContext);
}
}
public Java2DFontResolver getFontResolver() {
return (Java2DFontResolver) _sharedContext.getFontResolver();
}
public void layout() {
LayoutContext c = newLayoutContext();
BlockBox root = BoxBuilder.createRootBox(c, _doc);
root.setContainingBlock(new ViewportBox(getInitialExtents(c)));
root.layout(c);
Dimension dim = root.getLayer().getPaintingDimension(c);
root.getLayer().trimEmptyPages(c, dim.height);
root.getLayer().layoutPages(c);
_root = root;
}
private Rectangle getInitialExtents(LayoutContext c) {
PageBox first = Layer.createPageBox(c, "first");
return new Rectangle(0, 0, first.getContentWidth(c), first.getContentHeight(c));
}
private RenderingContext newRenderingContext() {
RenderingContext result = _sharedContext.newRenderingContextInstance();
result.setFontContext(new Java2DFontContext(_outputDevice.getGraphics()));
result.setOutputDevice(_outputDevice);
if (_reorderer != null)
result.setBidiReorderer(_reorderer);
_outputDevice.setRenderingContext(result);
_sharedContext.getTextRenderer().setup(result.getFontContext());
result.setRootLayer(_root.getLayer());
return result;
}
private LayoutContext newLayoutContext() {
LayoutContext result = _sharedContext.newLayoutContextInstance();
result.setFontContext(new Java2DFontContext(_outputDevice.getGraphics()));
if (_splitterFactory != null)
result.setBidiSplitterFactory(_splitterFactory);
if (_reorderer != null)
result.setBidiReorderer(_reorderer);
result.setDefaultTextDirection(_defaultTextDirection);
((Java2DTextRenderer) _sharedContext.getTextRenderer()).setup(result.getFontContext(), _reorderer != null ? _reorderer : new SimpleBidiReorderer());
return result;
}
public void writePages() throws IOException {
List pages = _root.getLayer().getPages();
RenderingContext c = newRenderingContext();
c.setInitialPageNo(_initialPageNo);
PageBox firstPage = pages.get(0);
Rectangle2D firstPageSize = new Rectangle2D.Float(0, 0,
firstPage.getWidth(c) / DEFAULT_DOTS_PER_PIXEL,
firstPage.getHeight(c) / DEFAULT_DOTS_PER_PIXEL);
writePageImages(pages, c, firstPageSize);
}
public void writePage(int zeroBasedPageNumber) throws IOException {
List pages = _root.getLayer().getPages();
if (zeroBasedPageNumber >= pages.size()) {
throw new IndexOutOfBoundsException();
}
RenderingContext c = newRenderingContext();
c.setInitialPageNo(_initialPageNo);
PageBox page = pages.get(zeroBasedPageNumber);
Rectangle2D pageSize = new Rectangle2D.Float(0, 0,
page.getWidth(c) / DEFAULT_DOTS_PER_PIXEL,
page.getHeight(c) / DEFAULT_DOTS_PER_PIXEL);
_outputDevice.setRoot(_root);
FSPage pg = _pageProcessor.createPage(zeroBasedPageNumber, (int) pageSize.getWidth(), (int) pageSize.getHeight());
_outputDevice.initializePage(pg.getGraphics());
_root.getLayer().assignPagePaintingPositions(c, _pagingMode);
c.setPageCount(pages.size());
c.setPage(zeroBasedPageNumber, page);
paintPage(c, page);
_pageProcessor.finishPage(pg);
_outputDevice.finish(c, _root);
}
public void writeSinglePage(){
List pages = _root.getLayer().getPages();
RenderingContext c = newRenderingContext();
c.setInitialPageNo(_initialPageNo);
PageBox page = pages.get(0);
Rectangle2D pageSize = new Rectangle2D.Float(0, 0,
page.getWidth(c) / DEFAULT_DOTS_PER_PIXEL,
_root.getHeight()/ DEFAULT_DOTS_PER_PIXEL);
_outputDevice.setRoot(_root);
FSPage pg = _pageProcessor.createPage(0, (int) pageSize.getWidth(), _root.getHeight());
_outputDevice.initializePage(pg.getGraphics());
_root.getLayer().assignPagePaintingPositions(c, _pagingMode);
c.setPageCount(pages.size());
c.setPage(0, page);
page.paintBackground(c, 0, _pagingMode);
page.paintMarginAreas(c, 0, _pagingMode);
page.paintBorder(c, 0, _pagingMode);
Shape working = _outputDevice.getClip();
_root.getLayer().paint(c);
_outputDevice.setClip(working);
_pageProcessor.finishPage(pg);
_outputDevice.finish(c, _root);
}
public int getPageCount() {
return _root.getLayer().getPages().size();
}
private void writePageImages(List pages, RenderingContext c, Rectangle2D firstPageSize) throws IOException {
_outputDevice.setRoot(_root);
FSPage pg = _pageProcessor.createPage(0, (int) firstPageSize.getWidth(), (int) firstPageSize.getHeight());
_outputDevice.initializePage(pg.getGraphics());
_root.getLayer().assignPagePaintingPositions(c, _pagingMode);
int pageCount = _root.getLayer().getPages().size();
c.setPageCount(pageCount);
for (int i = 0; i < pageCount; i++) {
PageBox currentPage = pages.get(i);
c.setPage(i, currentPage);
paintPage(c, currentPage);
_pageProcessor.finishPage(pg);
if (i != pageCount - 1) {
PageBox nextPage = pages.get(i + 1);
Rectangle2D nextPageSize = new Rectangle2D.Float(0, 0, nextPage.getWidth(c) / DEFAULT_DOTS_PER_PIXEL,
nextPage.getHeight(c) / DEFAULT_DOTS_PER_PIXEL);
pg = _pageProcessor.createPage(i + 1, (int) nextPageSize.getWidth(), (int) nextPageSize.getHeight());
_outputDevice.initializePage(pg.getGraphics());
}
}
_outputDevice.finish(c, _root);
}
private void paintPage(RenderingContext c, PageBox page) {
page.paintBackground(c, 0, _pagingMode);
page.paintMarginAreas(c, 0, _pagingMode);
page.paintBorder(c, 0, _pagingMode);
Shape working = _outputDevice.getClip();
Rectangle content = page.getPrintClippingBounds(c);
_outputDevice.clip(content);
int top = -page.getPaintingTop() + page.getMarginBorderPadding(c, CalculatedStyle.TOP);
int left = page.getMarginBorderPadding(c, CalculatedStyle.LEFT);
_outputDevice.translate(left, top);
_root.getLayer().paint(c);
_outputDevice.translate(-left, -top);
_outputDevice.setClip(working);
}
@Override
public void close() {
_sharedContext.removeFromThread();
ThreadCtx.cleanup();
if (_svgImpl != null) {
try {
_svgImpl.close();
} catch (IOException ignored) {
}
}
if (_mathMLImpl != null) {
try {
_mathMLImpl.close();
} catch (IOException ignored) {
}
}
}
}