diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/context/StyleReference.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/context/StyleReference.java index 0faabf21b..b2a9a623c 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/context/StyleReference.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/context/StyleReference.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.logging.Level; +import com.openhtmltopdf.css.sheet.FontFaceRule; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -281,7 +282,7 @@ public void removeStyle(Element e) { } } - public List getFontFaceRules() { + public List getFontFaceRules() { return _matcher.getFontFaceRules(); } diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/newmatch/Matcher.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/newmatch/Matcher.java index 869d6b68f..e8caefd70 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/newmatch/Matcher.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/newmatch/Matcher.java @@ -34,10 +34,7 @@ import com.openhtmltopdf.css.extend.AttributeResolver; import com.openhtmltopdf.css.extend.StylesheetFactory; import com.openhtmltopdf.css.extend.TreeResolver; -import com.openhtmltopdf.css.sheet.MediaRule; -import com.openhtmltopdf.css.sheet.PageRule; -import com.openhtmltopdf.css.sheet.Ruleset; -import com.openhtmltopdf.css.sheet.Stylesheet; +import com.openhtmltopdf.css.sheet.*; import com.openhtmltopdf.util.Util; import com.openhtmltopdf.util.XRLog; @@ -61,7 +58,7 @@ public class Matcher { private Set _visitElements; private List _pageRules; - private List _fontFaceRules; + private List _fontFaceRules; public Matcher( TreeResolver tr, AttributeResolver ar, StylesheetFactory factory, List stylesheets, String medium) { @@ -71,7 +68,7 @@ public Matcher( _styleFactory = factory; _pageRules = new ArrayList(); - _fontFaceRules = new ArrayList(); + _fontFaceRules = new ArrayList(); docMapper = createDocumentMapper(stylesheets, medium); } @@ -125,7 +122,7 @@ public PageInfo getPageCascadedStyle(String pageName, String pseudoPage) { return new PageInfo(props, style, marginBoxes); } - public List getFontFaceRules() { + public List getFontFaceRules() { return _fontFaceRules; } diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/extend/FSDOMMutator.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/extend/FSDOMMutator.java new file mode 100644 index 000000000..86b9f899a --- /dev/null +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/extend/FSDOMMutator.java @@ -0,0 +1,8 @@ +package com.openhtmltopdf.extend; + +/** + * Allows to modify the HTML document DOM after it has been parsed + */ +public interface FSDOMMutator { + void mutateDocument(org.w3c.dom.Document document); +} diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/Layer.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/Layer.java index 06e4386fb..401571ef6 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/Layer.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/Layer.java @@ -434,7 +434,7 @@ protected List applyTranform(RenderingContext c, Box box) { * We must apply the top/bottom margins from the previous pages, otherwise * our transform center is wrong. */ - for (int i = 0; i < c.getPageNo(); i++) { + for (int i = 0; i < c.getPageNo() && i < getPages().size(); i++) { RectPropertySet prevMargin = getPages().get(i).getMargin(c); relTranslateY += prevMargin.top() + prevMargin.bottom(); } diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/outputdevice/helper/BaseRendererBuilder.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/outputdevice/helper/BaseRendererBuilder.java new file mode 100644 index 000000000..f3af0908b --- /dev/null +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/outputdevice/helper/BaseRendererBuilder.java @@ -0,0 +1,355 @@ +package com.openhtmltopdf.outputdevice.helper; + +import com.openhtmltopdf.bidi.BidiReorderer; +import com.openhtmltopdf.bidi.BidiSplitterFactory; +import com.openhtmltopdf.extend.*; +import com.openhtmltopdf.layout.Layer; +import org.w3c.dom.Document; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * Baseclass for all RendererBuilders (PDF and Java2D), has all common settings + */ +@SuppressWarnings("unchecked") +public abstract class BaseRendererBuilder { + public static final float PAGE_SIZE_LETTER_WIDTH = 8.5f; + public static final float PAGE_SIZE_LETTER_HEIGHT = 11.0f; + public static final PageSizeUnits PAGE_SIZE_LETTER_UNITS = PageSizeUnits.INCHES; + protected final List _domMutators = new ArrayList(); + protected HttpStreamFactory _httpStreamFactory; + protected FSCache _cache; + protected FSUriResolver _resolver; + protected String _html; + protected String _baseUri; + protected Document _document; + protected SVGDrawer _svgImpl; + protected SVGDrawer _mathmlImpl; + protected String _replacementText; + protected FSTextBreaker _lineBreaker; + protected FSTextBreaker _charBreaker; + protected FSTextTransformer _unicodeToUpperTransformer; + protected FSTextTransformer _unicodeToLowerTransformer; + protected FSTextTransformer _unicodeToTitleTransformer; + protected BidiSplitterFactory _splitter; + protected BidiReorderer _reorderer; + protected boolean _textDirection = false; + protected Float _pageWidth; + protected Float _pageHeight; + protected boolean _isPageSizeInches; + protected String _uri; + protected File _file; + protected boolean _testMode = false; + protected int _initialPageNumber; + protected short _pagingMode = Layer.PAGED_MODE_PRINT; + protected FSObjectDrawerFactory _objectDrawerFactory; + protected String _preferredTransformerFactoryImplementationClass = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"; + + /** + * Add a DOM mutator to this builder. DOM mutators allow to modify the DOM + * before it is rendered. e.g. LaTeXDOMMutator can be used to translate latex + * text within a <latex> node to HTMl and MathML. + * + * @param domMutator + * the DOM Mutator + * @return this for method chaining + */ + public TFinalClass addDOMMutator(FSDOMMutator domMutator) { + _domMutators.add(domMutator); + return (TFinalClass) this; + } + + /** + * This method should be considered advanced and is not required for most + * setups. Set a preferred implementation class for use as + * javax.xml.transform.TransformerFactory. Use null to let a default + * implementation class be used. The default is + * "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl". This + * seems to work with most systems but not JBoss Wildfly and related setups. In + * this case you can use null to let the container use whatever + * TransformerFactory it has available. + * + * @param transformerFactoryClass + * @return this for method chaining + */ + public final TFinalClass useTransformerFactoryImplementationClass(String transformerFactoryClass) { + this._preferredTransformerFactoryImplementationClass = transformerFactoryClass; + return (TFinalClass) this; + } + + /** + * The default text direction of the document. LTR by default. + * + * @param textDirection + * @return this for method chaining + */ + public final TFinalClass defaultTextDirection(TextDirection textDirection) { + this._textDirection = textDirection == TextDirection.RTL; + return (TFinalClass) this; + } + + /** + * Whether to use test mode and output the PDF uncompressed. Turned off by + * default. + * + * @param mode + * @return this for method chaining + */ + public final TFinalClass testMode(boolean mode) { + this._testMode = mode; + return (TFinalClass) this; + } + + /** + * Provides an HttpStreamFactory implementation if the user desires to use an + * external HTTP/HTTPS implementation. Uses URL::openStream by default. + * + * @param factory + * @return + */ + public final TFinalClass useHttpStreamImplementation(HttpStreamFactory factory) { + this._httpStreamFactory = factory; + return (TFinalClass) this; + } + + /** + * Provides a uri resolver to resolve relative uris or private uri schemes. + * + * @param resolver + * @return + */ + public final TFinalClass useUriResolver(FSUriResolver resolver) { + this._resolver = resolver; + return (TFinalClass) this; + } + + /** + * Provides an external cache which can choose to cache items between runs, such + * as fonts or logo images. + * + * @param cache + * @return + */ + public final TFinalClass useCache(FSCache cache) { + this._cache = cache; + return (TFinalClass) this; + } + + /** + * Provides a text splitter to split text into directional runs. Does nothing by + * default. + * + * @param splitter + * @return + */ + public final TFinalClass useUnicodeBidiSplitter(BidiSplitterFactory splitter) { + this._splitter = splitter; + return (TFinalClass) this; + } + + /** + * Provides a reorderer to properly reverse RTL text. No-op by default. + * + * @param reorderer + * @return + */ + public final TFinalClass useUnicodeBidiReorderer(BidiReorderer reorderer) { + this._reorderer = reorderer; + return (TFinalClass) this; + } + + /** + * Provides a string containing XHTML/XML to convert to PDF. + * + * @param html + * @param baseUri + * @return + */ + public final TFinalClass withHtmlContent(String html, String baseUri) { + this._html = html; + this._baseUri = baseUri; + return (TFinalClass) this; + } + + /** + * Provides a w3c DOM Document acquired from an external source. + * + * @param doc + * @param baseUri + * @return + */ + public final TFinalClass withW3cDocument(org.w3c.dom.Document doc, String baseUri) { + this._document = doc; + this._baseUri = baseUri; + return (TFinalClass) this; + } + + /** + * Provides a URI to convert to PDF. The URI MUST point to a strict XHTML/XML + * document. + * + * @param uri + * @return + */ + public final TFinalClass withUri(String uri) { + this._uri = uri; + return (TFinalClass) this; + } + + /** + * Provides a file to convert to PDF. The file MUST contain XHTML/XML in UTF-8 + * encoding. + * + * @param file + * @return this for method chaining + */ + public final TFinalClass withFile(File file) { + this._file = file; + return (TFinalClass) this; + } + + /** + * Uses the specified SVG drawer implementation. + * + * @param svgImpl the SVG implementation + * @return this for method chaining + */ + public final TFinalClass useSVGDrawer(SVGDrawer svgImpl) { + this._svgImpl = svgImpl; + return (TFinalClass) this; + } + + /** + * Use the specified MathML implementation. + * + * @param mathMlImpl the MathML implementation + * @return this for method chaining + */ + public final TFinalClass useMathMLDrawer(SVGDrawer mathMlImpl) { + this._mathmlImpl = mathMlImpl; + return (TFinalClass) this; + } + + /** + * The replacement text to use if a character is cannot be renderered by any of + * the specified fonts. This is not broken across lines so should be one or zero + * characters for best results. Also, make sure it can be rendered by at least + * one of your specified fonts! The default is the # character. + * + * @param replacement the default replacement text + * @return this for method chaining + */ + public final TFinalClass useReplacementText(String replacement) { + this._replacementText = replacement; + return (TFinalClass) this; + } + + /** + * Specify the line breaker. By default a Java default BreakIterator line + * instance is used with US locale. Additionally, this is wrapped with + * UrlAwareLineBreakIterator to also break before the forward slash (/) + * character so that long URIs can be broken on to multiple lines. + * + * You may want to use a BreakIterator with a different locale (wrapped by + * UrlAwareLineBreakIterator or not) or a more advanced BreakIterator from icu4j + * (see the rtl-support module for an example). + * + * @param breaker + * @return + */ + public final TFinalClass useUnicodeLineBreaker(FSTextBreaker breaker) { + this._lineBreaker = breaker; + return (TFinalClass)this; + } + + /** + * Specify the character breaker. By default a break iterator character instance + * is used with US locale. Currently this is used when + * word-wrap: break-word is in effect. + * + * @param breaker + * @return + */ + public final TFinalClass useUnicodeCharacterBreaker(FSTextBreaker breaker) { + this._charBreaker = breaker; + return (TFinalClass)this; + } + + /** + * Specify a transformer to use to upper case strings. By default + * String::toUpperCase(Locale.US) is used. + * + * @param tr + * @return + */ + public final TFinalClass useUnicodeToUpperTransformer(FSTextTransformer tr) { + this._unicodeToUpperTransformer = tr; + return (TFinalClass)this; + } + + /** + * Specify a transformer to use to lower case strings. By default + * String::toLowerCase(Locale.US) is used. + * + * @param tr + * @return + */ + public final TFinalClass useUnicodeToLowerTransformer(FSTextTransformer tr) { + this._unicodeToLowerTransformer = tr; + return (TFinalClass)this; + } + + /** + * Specify a transformer to title case strings. By default a best effort + * implementation (non locale aware) is used. + * + * @param tr + * @return + */ + public final TFinalClass useUnicodeToTitleTransformer(FSTextTransformer tr) { + this._unicodeToTitleTransformer = tr; + return (TFinalClass)this; + } + + /** + * Specifies the default page size to use if none is specified in CSS. + * + * @param pageWidth + * @param pageHeight + * @param units + * either mm or inches. + * @see {@link #PAGE_SIZE_LETTER_WIDTH}, {@link #PAGE_SIZE_LETTER_HEIGHT} and + * {@link #PAGE_SIZE_LETTER_UNITS} + * @return + */ + public final TFinalClass useDefaultPageSize(float pageWidth, float pageHeight, PageSizeUnits units) { + this._pageWidth = pageWidth; + this._pageHeight = pageHeight; + this._isPageSizeInches = (units == PageSizeUnits.INCHES); + return (TFinalClass)this; + } + + /** + * Set a factory for <object> drawers + * + * @param objectDrawerFactory + * Object Drawer Factory + * @return this for method chaining + */ + public final TFinalClass useObjectDrawerFactory(FSObjectDrawerFactory objectDrawerFactory) { + this._objectDrawerFactory = objectDrawerFactory; + return (TFinalClass)this; + } + + public enum TextDirection { + RTL, LTR; + } + public enum PageSizeUnits { + MM, INCHES + } + public enum FontStyle { + NORMAL, ITALIC, OBLIQUE + } +} diff --git a/openhtmltopdf-examples/pom.xml b/openhtmltopdf-examples/pom.xml index ac5267a6e..9409350c5 100644 --- a/openhtmltopdf-examples/pom.xml +++ b/openhtmltopdf-examples/pom.xml @@ -59,6 +59,11 @@ openhtmltopdf-objects ${project.version} + + com.openhtmltopdf + openhtmltopdf-latex-support + ${project.version} + com.vladsch.flexmark diff --git a/openhtmltopdf-examples/src/main/java/com/openhtmltopdf/freemarker/FreeMarkerGenerator.java b/openhtmltopdf-examples/src/main/java/com/openhtmltopdf/freemarker/FreeMarkerGenerator.java index 9a6e8b197..53c26d5b3 100644 --- a/openhtmltopdf-examples/src/main/java/com/openhtmltopdf/freemarker/FreeMarkerGenerator.java +++ b/openhtmltopdf-examples/src/main/java/com/openhtmltopdf/freemarker/FreeMarkerGenerator.java @@ -2,9 +2,12 @@ import com.openhtmltopdf.bidi.support.ICUBidiReorderer; import com.openhtmltopdf.bidi.support.ICUBidiSplitter; +import com.openhtmltopdf.latexsupport.LaTeXDOMMutator; +import com.openhtmltopdf.mathmlsupport.MathMLDrawer; import com.openhtmltopdf.objects.StandardObjectDrawerFactory; import com.openhtmltopdf.pdfboxout.PdfBoxRenderer; import com.openhtmltopdf.pdfboxout.PdfRendererBuilder; +import com.openhtmltopdf.svgsupport.BatikSVGDrawer; import com.openhtmltopdf.swing.NaiveUserAgent.DefaultUriResolver; import freemarker.cache.ClassTemplateLoader; import freemarker.template.*; @@ -62,6 +65,9 @@ public byte[] generatePDF(String html) throws IOException { builder.useUnicodeBidiReorderer(new ICUBidiReorderer()); builder.defaultTextDirection(PdfRendererBuilder.TextDirection.LTR); builder.withHtmlContent(html, "/freemarker"); + builder.useSVGDrawer(new BatikSVGDrawer()); + builder.useMathMLDrawer(new MathMLDrawer()); + builder.addDOMMutator(LaTeXDOMMutator.INSTANCE); builder.useUriResolver(new DefaultUriResolver() { @Override public String resolveURI(String baseUri, String uri) { @@ -85,7 +91,7 @@ public String resolveURI(String baseUri, String uri) { try { pdfBoxRenderer.layout(); pdfBoxRenderer.createPDF(); - } catch (Exception e) { + } finally { pdfBoxRenderer.close(); } outputStream.close(); diff --git a/openhtmltopdf-examples/src/main/java/com/openhtmltopdf/testcases/TestcaseRunner.java b/openhtmltopdf-examples/src/main/java/com/openhtmltopdf/testcases/TestcaseRunner.java index 2ef2b18f7..90703f369 100644 --- a/openhtmltopdf-examples/src/main/java/com/openhtmltopdf/testcases/TestcaseRunner.java +++ b/openhtmltopdf-examples/src/main/java/com/openhtmltopdf/testcases/TestcaseRunner.java @@ -11,6 +11,8 @@ import javax.imageio.ImageIO; +import com.openhtmltopdf.latexsupport.LaTeXDOMMutator; +import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder; import org.apache.pdfbox.io.IOUtils; import org.apache.pdfbox.util.Charsets; import org.w3c.dom.Element; @@ -27,7 +29,6 @@ import com.openhtmltopdf.mathmlsupport.MathMLDrawer; import com.openhtmltopdf.objects.StandardObjectDrawerFactory; import com.openhtmltopdf.pdfboxout.PdfRendererBuilder; -import com.openhtmltopdf.pdfboxout.PdfRendererBuilder.TextDirection; import com.openhtmltopdf.render.DefaultObjectDrawerFactory; import com.openhtmltopdf.render.RenderingContext; import com.openhtmltopdf.svgsupport.BatikSVGDrawer; @@ -105,10 +106,13 @@ public static void main(String[] args) throws Exception { runTestCase("math-ml"); + runTestCase("latex-sample"); + /* * Broken rotate() on the second page */ runTestCase("RepeatedTableTransformSample"); + /* Add additional test cases here. */ } @@ -171,9 +175,10 @@ private static void renderPDF(String html, OutputStream outputStream) throws Exc PdfRendererBuilder builder = new PdfRendererBuilder(); builder.useUnicodeBidiSplitter(new ICUBidiSplitter.ICUBidiSplitterFactory()); builder.useUnicodeBidiReorderer(new ICUBidiReorderer()); - builder.defaultTextDirection(TextDirection.LTR); + builder.defaultTextDirection(BaseRendererBuilder.TextDirection.LTR); builder.useSVGDrawer(new BatikSVGDrawer()); builder.useMathMLDrawer(new MathMLDrawer()); + builder.addDOMMutator(LaTeXDOMMutator.INSTANCE); builder.useObjectDrawerFactory(buildObjectDrawerFactory()); builder.withHtmlContent(html, TestcaseRunner.class.getResource("/testcases/").toString()); @@ -193,6 +198,8 @@ private static DefaultObjectDrawerFactory buildObjectDrawerFactory() { private static void renderPNG(String html, final String filename) throws Exception { Java2DRendererBuilder builder = new Java2DRendererBuilder(); builder.useSVGDrawer(new BatikSVGDrawer()); + builder.useMathMLDrawer(new MathMLDrawer()); + builder.addDOMMutator(LaTeXDOMMutator.INSTANCE); builder.useObjectDrawerFactory(buildObjectDrawerFactory()); builder.withHtmlContent(html, TestcaseRunner.class.getResource("/testcases/").toString()); BufferedImagePageProcessor bufferedImagePageProcessor = new BufferedImagePageProcessor( diff --git a/openhtmltopdf-examples/src/main/resources/freemarker/featuredocumentation.ftl b/openhtmltopdf-examples/src/main/resources/freemarker/featuredocumentation.ftl index 23f8a1675..eb826de16 100644 --- a/openhtmltopdf-examples/src/main/resources/freemarker/featuredocumentation.ftl +++ b/openhtmltopdf-examples/src/main/resources/freemarker/featuredocumentation.ftl @@ -76,6 +76,15 @@ h1, h2, h3, h4 { -fs-page-break-min-height: 4cm; } + + /* does not seem to work (yet?) */ + #tocPlaceholder:after { + content:element(toc); + display:block; + min-height:20px; + min-width:20px; + border:2px solid black; + } @@ -98,11 +107,33 @@ Page / +[#assign tableOfContentHTML = ""] +[#assign sectionAnchorCounter = 0] +[#macro hn level] + [#assign sectionAnchorCounter = sectionAnchorCounter + 1] + + [#local content][#nested][/#local] + ${content} + [#assign tableOfContentHTML] + ${tableOfContentHTML} +
  • ${content}
  • + [/#assign] +[/#macro] +[#macro h1][@hn 1][#nested][/@hn][/#macro] +[#macro h2][@hn 2][#nested][/@hn][/#macro] +[#macro h3][@hn 3][#nested][/@hn][/#macro] +[#macro h4][@hn 4][#nested][/@hn][/#macro] + [#macro pomCode] [#local content][#nested][/#local]
    ${content?trim?html}
     
    [/#macro] +[#macro javaCode] + [#local content][#nested][/#local] +
    ${content?trim?html}
    +
    +[/#macro] [#macro htmlCode] [#local content][#nested][/#local] @@ -125,7 +156,9 @@ [/#macro] -

    OpenHtmlToPdf Feature Documentation

    +
    + +[@h1]OpenHtmlToPdf Feature Documentation[/@h1] This documentation tries to show the advanced features of OpenHtmlToPdf. To generate the documentation a @@ -136,7 +169,7 @@ IDEA IDE this is a very productive environment to build reports. Please lookup the source of this document if you want to know some tricks not explicit mentioned in this documentation. -

    Pagebreak Tuning

    +[@h2]Pagebreak Tuning[/@h2] In a perfect world [@htmlCode]style="page-break-inside: avoid"[/@htmlCode] would just work and all reports would look beautiful. OpenHtmlToPdf tries its best to avoid a page break inside. But this is not always possible and also rather @@ -153,7 +186,68 @@ Example: [/@htmlCode] -

    Objects

    +[@h2]MathML & LaTeX[/@h2] +To display math you can use MathML and latex. To do so you need the dependencies: + +[@pomCode] + + com.openhtmltopdf + openhtmltopdf-mathml-support + ... + + + com.openhtmltopdf + openhtmltopdf-latex-support + ... + +[/@pomCode] + +If you don't use the LaTeX feature you don't need to include the openhtmltopdf-latex-support. +You must activate the support in the Builder to use it: + +[@javaCode] +builder.useMathMLDrawer(new MathMLDrawer()); +builder.addDOMMutator(LaTeXDOMMutator.INSTANCE); +[/@javaCode] + +The LaTeX support translates a LaTeX fragment using SnuggleTeX to HTML+MathML, which is then +rendered using the MathML support. + +[@htmlCodeAndExec] + + This is a small inline formular: $$a^2 + b^2 = c^2$$. You can use many LaTeX + features and environments. The exact amount of supported features is depending on + StruggleTex and JEuclid which are the backing libraries for the LaTeX and MathML support. + + $$\sum\limits_{i=1}^n i^2 = \frac{n(n+1)(2n+1)}{6}$$ + + $\prod\limits_{i=1}^n x = x^n$ + +[/@htmlCodeAndExec] + +Here is some pure MathML: + +[@htmlCodeAndExec] + + + a + x + + + b + + + c + + + + + +
    +If you are writing the document by hand it may be just simpler to use LaTeX: +$$a x (b+c)$$ +[/@htmlCodeAndExec] + +[@h2]Objects[/@h2] OpenHtmlToPdf comes with some builtin objects, which you can use to quickly create diagrams, add background PDF images and so on. To use them include the openhtmltopdf-objects dependency in your pom: @@ -166,7 +260,7 @@ and so on. To use them include the openhtmltopdf-objects dependency in your pom:
    [/@pomCode] -

    Merge Background PDF

    +[@h3]Merge Background PDF[/@h3] You can add a watermark / background to your document. To do so you should place @@ -181,7 +275,7 @@ lower corner.
  • pdfpage: Page to import from the PDF file.
  • -

    JFreeGraph

    +[@h3]JFreeGraph[/@h3] For simple charts you can use the builtin objects for JFreeGraph. Note: You must specify the dependency to JFreeMarker in your @@ -198,7 +292,7 @@ POM, because it is declared as a optional dependency on openhtmltopdf-objects. If you specify a URL for a data point then the segment in the diagram used for that datapoint is a link to that URL. Note: This only works in Acrobat Reader, all other PDF Viewer ignore this feature. -

    The Pie Diagram

    +[@h4]The Pie Diagram[/@h4] [@htmlCodeAndExec] [/@htmlCodeAndExec] -

    The Bar Diagram

    +[@h4]The Bar Diagram[/@h4] [@htmlCodeAndExec] [/@htmlCodeAndExec] +
    + +
    + Table of Content +
      + ${tableOfContentHTML} +
    +
    \ No newline at end of file diff --git a/openhtmltopdf-examples/src/main/resources/testcases/latex-sample.html b/openhtmltopdf-examples/src/main/resources/testcases/latex-sample.html new file mode 100644 index 000000000..6ebb4703c --- /dev/null +++ b/openhtmltopdf-examples/src/main/resources/testcases/latex-sample.html @@ -0,0 +1,122 @@ + +Sample LaTeX based HTML document + +

    Creating Math using LaTeX

    + + + $\sqrt[5]{\frac{123.45}{678.12}}$ + + +
    +$$ a^2 = b^2 + c^2 $$ +
    + +$$ + \left [ \mathbb{P}\mathbb{Z}\infty \right ] + \sum \sqrt[4]{10} $$ + + +In the source you must quote &: + + \begin{tabular}{ l c r } + 1 & 2 & 3 \\ + 4 & 5 & 6 \\ + 7 & 8 & 9 \\ + \end{tabular} + + +or just use CDATA: + + + + + + + + + + + \ No newline at end of file diff --git a/openhtmltopdf-java2d/src/main/java/com/openhtmltopdf/java2d/Java2DRenderer.java b/openhtmltopdf-java2d/src/main/java/com/openhtmltopdf/java2d/Java2DRenderer.java index 09d0b5a47..76604d040 100644 --- a/openhtmltopdf-java2d/src/main/java/com/openhtmltopdf/java2d/Java2DRenderer.java +++ b/openhtmltopdf-java2d/src/main/java/com/openhtmltopdf/java2d/Java2DRenderer.java @@ -1,5 +1,13 @@ 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; @@ -26,21 +34,12 @@ 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; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; - -import java.awt.*; -import java.awt.geom.Rectangle2D; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.StringReader; -import java.util.List; - -public class Java2DRenderer implements IJava2DRenderer { - private Document _doc; +public class Java2DRenderer implements IJava2DRenderer, Closeable { + private final List _domMutators; + private final SVGDrawer _mathMLImpl; private BlockBox _root; private final SharedContext _sharedContext; @@ -51,6 +50,7 @@ public class Java2DRenderer implements IJava2DRenderer { private BidiReorderer _reorderer; private final SVGDrawer _svgImpl; + private Document _doc; private final FSObjectDrawerFactory _objectDrawerFactory; private final FSPageProcessor _pageProcessor; @@ -63,7 +63,9 @@ public class Java2DRenderer implements IJava2DRenderer { /** * Subject to change. Not public API. Used exclusively by the Java2DRendererBuilder class. - * @param preferredTransformerFactoryImplementationClass + * @param _svgImpl + * @param preferredTransformerFactoryImplementationClass + * @param _domMutators */ public Java2DRenderer( BaseDocument doc, @@ -71,20 +73,22 @@ public Java2DRenderer( HttpStreamFactory httpStreamFactory, FSUriResolver resolver, FSCache cache, - SVGDrawer svgImpl, + SVGDrawer _svgImpl, SVGDrawer _mathMLImpl, PageDimensions pageSize, String replacementText, boolean testMode, FSPageProcessor pageProcessor, Graphics2D layoutGraphics, int initialPageNumber, short pagingMode, - FSObjectDrawerFactory objectDrawerFactory, - String preferredTransformerFactoryImplementationClass) { + FSObjectDrawerFactory objectDrawerFactory, + String preferredTransformerFactoryImplementationClass, List _domMutators) { _pagingMode = pagingMode; _pageProcessor = pageProcessor; _initialPageNo = initialPageNumber; - _svgImpl = svgImpl; + this._svgImpl = _svgImpl; + this._mathMLImpl = _mathMLImpl; + this._domMutators = _domMutators; _objectDrawerFactory = objectDrawerFactory; _outputDevice = new Java2DOutputDevice(layoutGraphics); @@ -115,7 +119,8 @@ public Java2DRenderer( Java2DFontResolver fontResolver = new Java2DFontResolver(_sharedContext); _sharedContext.setFontResolver(fontResolver); - Java2DReplacedElementFactory replacedFactory = new Java2DReplacedElementFactory(_svgImpl, _objectDrawerFactory); + Java2DReplacedElementFactory replacedFactory = new Java2DReplacedElementFactory(this._svgImpl, + _objectDrawerFactory, this._mathMLImpl); _sharedContext.setReplacedElementFactory(replacedFactory); _sharedContext.setTextRenderer(new Java2DTextRenderer()); @@ -204,6 +209,12 @@ private Document loadDocument(String uri) { private void setDocument(Document doc, String url, NamespaceHandler nsh) { _doc = doc; + + /* + * Apply potential DOM mutations + */ + for (FSDOMMutator domMutator : _domMutators) + domMutator.mutateDocument(doc); //TODOgetFontResolver().flushFontFaceFonts(); @@ -222,6 +233,10 @@ private void setDocument(Document doc, String url, NamespaceHandler nsh) { if (_svgImpl != null) { _svgImpl.importFontFaceRules(_sharedContext.getCss().getFontFaceRules(), _sharedContext); } + + if (_mathMLImpl != null) { + _mathMLImpl.importFontFaceRules(_sharedContext.getCss().getFontFaceRules(), _sharedContext); + } } public Java2DFontResolver getFontResolver() { @@ -383,7 +398,7 @@ private void writePageImages(List pages, RenderingContext c, Rectangle2 _pageProcessor.finishPage(pg); if (i != pageCount - 1) { - PageBox nextPage = (PageBox) pages.get(i + 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); @@ -395,7 +410,7 @@ private void writePageImages(List pages, RenderingContext c, Rectangle2 _outputDevice.finish(c, _root); } - private void paintPage(RenderingContext c, PageBox page) throws IOException { + private void paintPage(RenderingContext c, PageBox page) { page.paintBackground(c, 0, _pagingMode); page.paintMarginAreas(c, 0, _pagingMode); page.paintBorder(c, 0, _pagingMode); @@ -415,4 +430,23 @@ private void paintPage(RenderingContext c, PageBox page) throws IOException { _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) { + } + } + } } diff --git a/openhtmltopdf-java2d/src/main/java/com/openhtmltopdf/java2d/Java2DReplacedElementFactory.java b/openhtmltopdf-java2d/src/main/java/com/openhtmltopdf/java2d/Java2DReplacedElementFactory.java index c22ce1888..e2326e2c0 100644 --- a/openhtmltopdf-java2d/src/main/java/com/openhtmltopdf/java2d/Java2DReplacedElementFactory.java +++ b/openhtmltopdf-java2d/src/main/java/com/openhtmltopdf/java2d/Java2DReplacedElementFactory.java @@ -15,10 +15,13 @@ public class Java2DReplacedElementFactory extends SwingReplacedElementFactory { private final SVGDrawer _svgImpl; private final FSObjectDrawerFactory _objectDrawerFactory; + private final SVGDrawer _mathMLImpl; - public Java2DReplacedElementFactory(SVGDrawer svgImpl, FSObjectDrawerFactory objectDrawerFactory) { + public Java2DReplacedElementFactory(SVGDrawer svgImpl, FSObjectDrawerFactory objectDrawerFactory, SVGDrawer + mathMLImpl) { this._svgImpl = svgImpl; this._objectDrawerFactory = objectDrawerFactory; + this._mathMLImpl = mathMLImpl; } @Override @@ -30,7 +33,9 @@ public ReplacedElement createReplacedElement(LayoutContext context, BlockBox box } String nodeName = e.getNodeName(); - if (nodeName.equals("svg") && _svgImpl != null) { + if (nodeName.equals("math") && _mathMLImpl != null) { + return new Java2DSVGReplacedElement(e, _mathMLImpl, cssWidth, cssHeight, box, context); + } else if (nodeName.equals("svg") && _svgImpl != null) { return new Java2DSVGReplacedElement(e, _svgImpl, cssWidth, cssHeight, box, context); } else if (nodeName.equals("object") && _objectDrawerFactory != null) { FSObjectDrawer drawer = _objectDrawerFactory.createDrawer(e); diff --git a/openhtmltopdf-java2d/src/main/java/com/openhtmltopdf/java2d/api/Java2DRendererBuilder.java b/openhtmltopdf-java2d/src/main/java/com/openhtmltopdf/java2d/api/Java2DRendererBuilder.java index e3cbd4f3e..45bd68a97 100644 --- a/openhtmltopdf-java2d/src/main/java/com/openhtmltopdf/java2d/api/Java2DRendererBuilder.java +++ b/openhtmltopdf-java2d/src/main/java/com/openhtmltopdf/java2d/api/Java2DRendererBuilder.java @@ -2,20 +2,16 @@ import java.awt.*; import java.awt.image.BufferedImage; -import java.io.File; import java.io.InputStream; import java.util.ArrayList; import java.util.List; -import com.openhtmltopdf.layout.Layer; - -import org.w3c.dom.Document; - -import com.openhtmltopdf.bidi.BidiReorderer; -import com.openhtmltopdf.bidi.BidiSplitterFactory; -import com.openhtmltopdf.extend.*; +import com.openhtmltopdf.extend.FSSupplier; +import com.openhtmltopdf.extend.OutputDevice; import com.openhtmltopdf.java2d.Java2DRenderer; +import com.openhtmltopdf.layout.Layer; import com.openhtmltopdf.outputdevice.helper.BaseDocument; +import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder; import com.openhtmltopdf.outputdevice.helper.PageDimensions; import com.openhtmltopdf.outputdevice.helper.UnicodeImplementation; import com.openhtmltopdf.render.RenderingContext; @@ -25,338 +21,81 @@ * Build a Java2D renderer for a given HTML. The renderer allows to get a * BufferedImage of the HTML and to render it in components (using Graphics2D). */ -public class Java2DRendererBuilder { - private HttpStreamFactory _httpStreamFactory; - private FSCache _cache; - private FSUriResolver _resolver; - private String _html; - private String _baseUri; - private Document _document; - private SVGDrawer _svgImpl; - private String _replacementText; - private FSTextBreaker _lineBreaker; - private FSTextBreaker _charBreaker; - private FSTextTransformer _unicodeToUpperTransformer; - private FSTextTransformer _unicodeToLowerTransformer; - private FSTextTransformer _unicodeToTitleTransformer; - private BidiSplitterFactory _splitter; - private BidiReorderer _reorderer; - private boolean _textDirection = false; - private Float _pageWidth; - private Float _pageHeight; - private boolean _isPageSizeInches; - private FSPageProcessor _pageProcessor; - private String _uri; - private File _file; - private boolean _testMode = false; - private Graphics2D _layoutGraphics; - private int _initialPageNumber; - private short _pagingMode = Layer.PAGED_MODE_PRINT; - private FSObjectDrawerFactory _objectDrawerFactory; - - public enum TextDirection { RTL, LTR; } - public enum PageSizeUnits { MM, INCHES } - public enum FontStyle { NORMAL, ITALIC, OBLIQUE } - - private static class AddedFont { - private final FSSupplier supplier; - private final Integer weight; - private final String family; - private final FontStyle style; - - private AddedFont(FSSupplier supplier, Integer weight, String family, FontStyle style) { - this.supplier = supplier; - this.weight = weight; - this.family = family; - this.style = style; - } - } - - private List _fonts = new ArrayList(); - private String _preferredTransformerFactoryImplementationClass = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl";; +public class Java2DRendererBuilder extends BaseRendererBuilder { + protected Graphics2D _layoutGraphics; + protected FSPageProcessor _pageProcessor; + private List _fonts = new ArrayList(); /** - * Provides an HttpStreamFactory implementation if the user desires to use - * an external HTTP/HTTPS implementation. Uses URL::openStream by default. + * Compulsory method. The layout graphics are used to measure text and should be + * from an image or device with the same characteristics as the output graphicsw + * provided by the page processor. * - * @param factory + * @param g2d * @return */ - public Java2DRendererBuilder useHttpStreamImplementation(HttpStreamFactory factory) { - this._httpStreamFactory = factory; + public Java2DRendererBuilder useLayoutGraphics(Graphics2D g2d) { + this._layoutGraphics = g2d; return this; } /** - * Provides a uri resolver to resolve relative uris or private uri schemes. + * Add a font programmatically. The font will only be downloaded if needed. * - * @param resolver - * @return - */ - public Java2DRendererBuilder useUriResolver(FSUriResolver resolver) { - this._resolver = resolver; - return this; - } - - /** - * Provides an external cache which can choose to cache items between runs, - * such as fonts or logo images. + * The InputStream returned by the supplier will be closed by the caller. + * FSSupplier is a lambda compatible interface. * - * @param cache + * Fonts can also be added using a font-face at-rule in the CSS. + * + * @param supplier + * @param fontFamily + * @param fontWeight + * @param fontStyle * @return */ - public Java2DRendererBuilder useCache(FSCache cache) { - this._cache = cache; + public Java2DRendererBuilder useFont(FSSupplier supplier, String fontFamily, Integer fontWeight, + FontStyle fontStyle) { + this._fonts.add(new AddedFont(supplier, fontWeight, fontFamily, fontStyle)); return this; } /** - * Provides a string containing XHTML/XML to convert to image. + * Simpler overload for {@link #useFont(FSSupplier, String, Integer, FontStyle)} * - * @param html - * @param baseUri + * @param supplier + * @param fontFamily * @return */ - public Java2DRendererBuilder withHtmlContent(String html, String baseUri) { - this._html = html; - this._baseUri = baseUri; - return this; + public Java2DRendererBuilder useFont(FSSupplier supplier, String fontFamily) { + return this.useFont(supplier, fontFamily, 400, FontStyle.NORMAL); } /** - * Provides a w3c DOM Document acquired from an external source. + * Used to set an initial page number for use with page counters, etc. * - * @param doc - * @param baseUri + * @param pageNumberInitial * @return */ - public Java2DRendererBuilder withW3cDocument(org.w3c.dom.Document doc, String baseUri) { - this._document = doc; - this._baseUri = baseUri; + public Java2DRendererBuilder useInitialPageNumber(int pageNumberInitial) { + this._initialPageNumber = pageNumberInitial; return this; } - - /** - * Provides a URI to convert to image. The URI MUST point to a strict XHTML/XML document. - * @param uri - * @return - */ - public Java2DRendererBuilder withUri(String uri) { - this._uri = uri; - return this; - } /** - * Uses the specified SVG drawer implementation. - * - * @param svgImpl - * @return + * Render everything to a single page. I.e. only one big page is genereated, no + * pagebreak will be done. The page is only as height as needed. */ - public Java2DRendererBuilder useSVGDrawer(SVGDrawer svgImpl) { - this._svgImpl = svgImpl; - return this; - } - - /** - * Compulsory method. The layout graphics are used to measure text and should be from an image or device with - * the same characteristics as the output graphicsw provided by the page processor. - * @param g2d - * @return - */ - public Java2DRendererBuilder useLayoutGraphics(Graphics2D g2d) { - this._layoutGraphics = g2d; + public Java2DRendererBuilder toSinglePage(FSPageProcessor pageProcessor) { + this._pagingMode = Layer.PAGED_MODE_SCREEN; + this._pageProcessor = pageProcessor; return this; } - - /** - * The default text direction of the document. LTR by default. - * @param textDirection - * @return - */ - public Java2DRendererBuilder defaultTextDirection(TextDirection textDirection) { - this._textDirection = textDirection == TextDirection.RTL; - return this; - } - - /** - * Whether to use test mode which will output box boundaries on the result. Turned off by default. - * @param mode - * @return - */ - public Java2DRendererBuilder testMode(boolean mode) { - this._testMode = mode; - return this; - } - - /** - * Provides a text splitter to split text into directional runs. Does nothing by default. - * @param splitter - * @return - */ - public Java2DRendererBuilder useUnicodeBidiSplitter(BidiSplitterFactory splitter) { - this._splitter = splitter; - return this; - } - - /** - * Provides a reorderer to properly reverse RTL text. No-op by default. - * @param reorderer - * @return - */ - public Java2DRendererBuilder useUnicodeBidiReorderer(BidiReorderer reorderer) { - this._reorderer = reorderer; - return this; - } - - /** - * Provides a file to convert to PDF. The file MUST contain XHTML/XML in UTF-8 encoding. - * @param file - * @return - */ - public Java2DRendererBuilder withFile(File file) { - this._file = file; - return this; - } - - /** - * The replacement text to use if a character is cannot be renderered by any of the specified fonts. - * This is not broken across lines so should be one or zero characters for best results. - * Also, make sure it can be rendered by at least one of your specified fonts! - * The default is the # character. - * @param replacement - * @return - */ - public Java2DRendererBuilder useReplacementText(String replacement) { - this._replacementText = replacement; - return this; - } - - /** - * Specify the line breaker. By default a Java default BreakIterator line instance is used - * with US locale. Additionally, this is wrapped with UrlAwareLineBreakIterator to also - * break before the forward slash (/) character so that long URIs can be broken on to multiple lines. - * - * You may want to use a BreakIterator with a different locale (wrapped by UrlAwareLineBreakIterator or not) - * or a more advanced BreakIterator from icu4j (see the rtl-support module for an example). - * @param breaker - * @return - */ - public Java2DRendererBuilder useUnicodeLineBreaker(FSTextBreaker breaker) { - this._lineBreaker = breaker; - return this; - } - - /** - * Specify the character breaker. By default a break iterator character instance is used with - * US locale. Currently this is used when word-wrap: break-word is in - * effect. - * @param breaker - * @return - */ - public Java2DRendererBuilder useUnicodeCharacterBreaker(FSTextBreaker breaker) { - this._charBreaker = breaker; - return this; - } - - /** - * Specify a transformer to use to upper case strings. - * By default String::toUpperCase(Locale.US) is used. - * @param tr - * @return - */ - public Java2DRendererBuilder useUnicodeToUpperTransformer(FSTextTransformer tr) { - this._unicodeToUpperTransformer = tr; - return this; - } - - /** - * Specify a transformer to use to lower case strings. - * By default String::toLowerCase(Locale.US) is used. - * @param tr - * @return - */ - public Java2DRendererBuilder useUnicodeToLowerTransformer(FSTextTransformer tr) { - this._unicodeToLowerTransformer = tr; - return this; - } - - /** - * Specify a transformer to title case strings. - * By default a best effort implementation (non locale aware) is used. - * @param tr - * @return - */ - public Java2DRendererBuilder useUnicodeToTitleTransformer(FSTextTransformer tr) { - this._unicodeToTitleTransformer = tr; - return this; - } - - /** - * Add a font programmatically. The font will only be downloaded if needed. - * - * The InputStream returned by the supplier will be closed by the caller. - * FSSupplier is a lambda compatible interface. - * - * Fonts can also be added using a font-face at-rule in the CSS. - * @param supplier - * @param fontFamily - * @param fontWeight - * @param fontStyle - * @return - */ - public Java2DRendererBuilder useFont(FSSupplier supplier, String fontFamily, Integer fontWeight, FontStyle fontStyle) { - this._fonts.add(new AddedFont(supplier, fontWeight, fontFamily, fontStyle)); - return this; - } - - /** - * Simpler overload for {@link #useFont(FSSupplier, String, Integer, FontStyle)} - * @param supplier - * @param fontFamily - * @return - */ - public Java2DRendererBuilder useFont(FSSupplier supplier, String fontFamily) { - return this.useFont(supplier, fontFamily, 400, FontStyle.NORMAL); - } - - /** - * Specifies the default page size to use if none is specified in CSS. - * @param pageWidth - * @param pageHeight - * @param units either mm or inches. - * @see {@link #PAGE_SIZE_LETTER_WIDTH}, {@link #PAGE_SIZE_LETTER_HEIGHT} and {@link #PAGE_SIZE_LETTER_UNITS} - * @return - */ - public Java2DRendererBuilder useDefaultPageSize(float pageWidth, float pageHeight, PageSizeUnits units) { - this._pageWidth = pageWidth; - this._pageHeight = pageHeight; - this._isPageSizeInches = (units == PageSizeUnits.INCHES); - return this; - } - - /** - * Used to set an initial page number for use with page counters, etc. - * @param pageNumberInitial - * @return - */ - public Java2DRendererBuilder useInitialPageNumber(int pageNumberInitial) { - this._initialPageNumber = pageNumberInitial; - return this; - } - - /** - * Render everything to a single page. I.e. only one big page is genereated, - * no pagebreak will be done. The page is only as height as needed. - */ - public Java2DRendererBuilder toSinglePage(FSPageProcessor pageProcessor){ - this._pagingMode = Layer.PAGED_MODE_SCREEN; - this._pageProcessor = pageProcessor; - return this; - } /** - * Output the document in paged format. The user can use the DefaultPageProcessor or use its source - * as a reference to code their own page processor for advanced usage. + * Output the document in paged format. The user can use the + * DefaultPageProcessor or use its source as a reference to code their own page + * processor for advanced usage. + * * @param pageProcessor * @return */ @@ -365,84 +104,76 @@ public Java2DRendererBuilder toPageProcessor(FSPageProcessor pageProcessor) { this._pageProcessor = pageProcessor; return this; } - + /** - * useLayoutGraphics and toPageProcessor MUST have been called. - * Also a document MUST have been set with one of the with* methods. - * This will build the renderer and output each page of the document to the specified page - * processor. + * useLayoutGraphics and toPageProcessor MUST have + * been called. Also a document MUST have been set with one of the with* + * methods. This will build the renderer and output each page of the document to + * the specified page processor. + * * @throws Exception */ public void runPaged() throws Exception { Java2DRenderer renderer = this.buildJava2DRenderer(); renderer.layout(); - if( _pagingMode == Layer.PAGED_MODE_PRINT) + if (_pagingMode == Layer.PAGED_MODE_PRINT) renderer.writePages(); else renderer.writeSinglePage(); } /** - * useLayoutGraphics and toPageProcessor MUST have been called. - * Also a document MUST have been set with one of the with* methods. - * This will build the renderer and output the first page of the document to the specified page - * processor. + * useLayoutGraphics and toPageProcessor MUST have + * been called. Also a document MUST have been set with one of the with* + * methods. This will build the renderer and output the first page of the + * document to the specified page processor. + * * @throws Exception */ public void runFirstPage() throws Exception { Java2DRenderer renderer = this.buildJava2DRenderer(); renderer.layout(); - if( _pagingMode == Layer.PAGED_MODE_PRINT) + if (_pagingMode == Layer.PAGED_MODE_PRINT) renderer.writePage(0); else renderer.writeSinglePage(); } - + public Java2DRenderer buildJava2DRenderer() { - UnicodeImplementation unicode = new UnicodeImplementation(_reorderer, _splitter, _lineBreaker, - _unicodeToLowerTransformer, _unicodeToUpperTransformer, _unicodeToTitleTransformer, _textDirection, _charBreaker); + UnicodeImplementation unicode = new UnicodeImplementation(_reorderer, _splitter, _lineBreaker, + _unicodeToLowerTransformer, _unicodeToUpperTransformer, _unicodeToTitleTransformer, _textDirection, + _charBreaker); + + PageDimensions pageSize = new PageDimensions(_pageWidth, _pageHeight, _isPageSizeInches); - PageDimensions pageSize = new PageDimensions(_pageWidth, _pageHeight, _isPageSizeInches); - - BaseDocument doc = new BaseDocument(_baseUri, _html, _document, _file, _uri); + BaseDocument doc = new BaseDocument(_baseUri, _html, _document, _file, _uri); - /* - * If no layout graphics is provied, just use a sane default - */ - if(_layoutGraphics == null ) { + /* + * If no layout graphics is provied, just use a sane default + */ + if (_layoutGraphics == null) { BufferedImage bf = new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR); _layoutGraphics = bf.createGraphics(); } - return new Java2DRenderer( - doc, unicode, _httpStreamFactory, _resolver, _cache, _svgImpl, pageSize, _replacementText, - _testMode, _pageProcessor, _layoutGraphics, _initialPageNumber, _pagingMode, - _objectDrawerFactory, _preferredTransformerFactoryImplementationClass); - } + return new Java2DRenderer(doc, unicode, _httpStreamFactory, _resolver, _cache, _svgImpl, _mathmlImpl, pageSize, + _replacementText, _testMode, _pageProcessor, _layoutGraphics, _initialPageNumber, _pagingMode, + _objectDrawerFactory, _preferredTransformerFactoryImplementationClass, _domMutators); + } - /** - * Set a factory for <object> drawers - * @param objectDrawerFactory Object Drawer Factory - * @return this for method chaining - */ - public Java2DRendererBuilder useObjectDrawerFactory(FSObjectDrawerFactory objectDrawerFactory) { - this._objectDrawerFactory = objectDrawerFactory; - return this; + private static class AddedFont { + private final FSSupplier supplier; + private final Integer weight; + private final String family; + private final FontStyle style; + + private AddedFont(FSSupplier supplier, Integer weight, String family, FontStyle style) { + this.supplier = supplier; + this.weight = weight; + this.family = family; + this.style = style; + } } - - /** - * This method should be considered advanced and is not required for most setups. - * Set a preferred implementation class for use as javax.xml.transform.TransformerFactory. Use null to let - * a default implementation class be used. The default is "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl". - * This seems to work with most systems but not JBoss Wildfly and related setups. In this case you can use null to let - * the container use whatever TransformerFactory it has available. - * @param transformerFactoryClass - * @return this for method chaining - */ - public Java2DRendererBuilder useTransformerFactoryImplementationClass(String transformerFactoryClass) { - this._preferredTransformerFactoryImplementationClass = transformerFactoryClass; - return this; - } public static abstract class Graphics2DPaintingReplacedElement extends EmptyReplacedElement { protected Graphics2DPaintingReplacedElement(int width, int height) { diff --git a/openhtmltopdf-latex-support/pom.xml b/openhtmltopdf-latex-support/pom.xml new file mode 100644 index 000000000..4aa528f6f --- /dev/null +++ b/openhtmltopdf-latex-support/pom.xml @@ -0,0 +1,63 @@ + + + + 4.0.0 + + + com.openhtmltopdf + openhtmltopdf-parent + 0.0.1-RC13-SNAPSHOT + + + openhtmltopdf-latex-support + + jar + + Openhtmltopdf Latex Math Support + Open HTML to PDF is a CSS 2.1 renderer written in Java. This artifact supports drawing MathML graphics on to PDFs using JEuclid. + + + + GNU Lesser General Public License (LGPL), version 2.1 or later + http://www.gnu.org/licenses/lgpl.html + + + + + + bintray + https://api.bintray.com/maven/danfickle/maven/com.openhtmltopdf:openhtmltopdf-parent + + + + + + com.openhtmltopdf + openhtmltopdf-core + ${project.version} + + + com.openhtmltopdf + openhtmltopdf-mathml-support + ${project.version} + + + de.rototor.snuggletex + snuggletex-core + 1.3.0 + + + + + + + ../ + ${project.build.outputDirectory}/META-INF + + LICENSE* + + + + + + diff --git a/openhtmltopdf-latex-support/src/main/java/com/openhtmltopdf/latexsupport/DOMConverter.java b/openhtmltopdf-latex-support/src/main/java/com/openhtmltopdf/latexsupport/DOMConverter.java new file mode 100644 index 000000000..1a064ce20 --- /dev/null +++ b/openhtmltopdf-latex-support/src/main/java/com/openhtmltopdf/latexsupport/DOMConverter.java @@ -0,0 +1,46 @@ +package com.openhtmltopdf.latexsupport; + +import java.io.IOException; + +import org.w3c.dom.Element; + +import uk.ac.ed.ph.snuggletex.SnuggleEngine; +import uk.ac.ed.ph.snuggletex.SnuggleInput; +import uk.ac.ed.ph.snuggletex.SnuggleSession; +import uk.ac.ed.ph.snuggletex.utilities.StylesheetManager; + +class DOMConverter { + private StylesheetManager stylesheetManager = new StylesheetManager(); + + void convert(Element latexElement) throws IOException { + String rawInputLaTeX = latexElement.getTextContent(); + String inputLaTeX = rawInputLaTeX.replaceAll("(\r\n|\r|\n)", "\n"); + + SnuggleEngine engine = createSnuggleEngine(); + SnuggleSession session = engine.createSession(); + SnuggleInput input = new SnuggleInput(inputLaTeX, "LaTeX Element"); + + try { + session.parseInput(input); + } catch (Exception e) { + throw new IOException("Error while parsing: " + rawInputLaTeX + ": " + e.getMessage(), e); + } + + while (latexElement.getChildNodes().getLength() != 0) + latexElement.removeChild(latexElement.getFirstChild()); + + try { + session.buildDOMSubtree(latexElement); + } catch (Exception e) { + throw new IOException("Error while building DOM for: " + rawInputLaTeX + ": " + e.getMessage(), e); + } + } + + private StylesheetManager getStylesheetManager() { + return stylesheetManager; + } + + private SnuggleEngine createSnuggleEngine() { + return new SnuggleEngine(getStylesheetManager()); + } +} diff --git a/openhtmltopdf-latex-support/src/main/java/com/openhtmltopdf/latexsupport/LaTeXDOMMutator.java b/openhtmltopdf-latex-support/src/main/java/com/openhtmltopdf/latexsupport/LaTeXDOMMutator.java new file mode 100644 index 000000000..cc977f425 --- /dev/null +++ b/openhtmltopdf-latex-support/src/main/java/com/openhtmltopdf/latexsupport/LaTeXDOMMutator.java @@ -0,0 +1,55 @@ +package com.openhtmltopdf.latexsupport; + +import java.io.IOException; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import com.openhtmltopdf.extend.FSDOMMutator; +import uk.ac.ed.ph.snuggletex.utilities.CSSUtilities; + +/** + * Allows to use <latex> tags within the HTML to use LaTeX for math and + * format output. Register using + */ +public class LaTeXDOMMutator implements FSDOMMutator { + /** + * The singleton instance to use. + */ + public final static LaTeXDOMMutator INSTANCE = new LaTeXDOMMutator(); + private final DOMConverter converter = new DOMConverter(); + + private LaTeXDOMMutator() { + } + + @Override + public void mutateDocument(org.w3c.dom.Document document) { + try { + NodeList latexNodes = document.getElementsByTagName("latex"); + for (int i = 0; i < latexNodes.getLength(); i++) { + converter.convert((Element) latexNodes.item(i)); + } + if (latexNodes.getLength() != 0) { + /* + * We must append the style sheet, otherwise we wont get everything rendered + * correctly + */ + String defaultCSS = CSSUtilities.writeDefaultStylesheet(); + NodeList list = document.getElementsByTagName("head"); + if (list.getLength() == 0) + list = document.getElementsByTagName("body"); + if (list.getLength() > 0) { + Element style = document.createElement("style"); + style.setTextContent(defaultCSS); + style.setAttribute("type", "text/css"); + /* + * We add the style as the first element, so that it can be overwritten + */ + list.item(0).insertBefore(style, list.item(0).getFirstChild()); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/openhtmltopdf-mathml-support/pom.xml b/openhtmltopdf-mathml-support/pom.xml index 89a55961c..51c1ce06d 100644 --- a/openhtmltopdf-mathml-support/pom.xml +++ b/openhtmltopdf-mathml-support/pom.xml @@ -1,57 +1,65 @@ - - - 4.0.0 - - - com.openhtmltopdf - openhtmltopdf-parent - 0.0.1-RC13-SNAPSHOT - - - openhtmltopdf-mathml-support - - jar - - Openhtmltopdf MathML Support - Open HTML to PDF is a CSS 2.1 renderer written in Java. This artifact supports drawing MathML graphics on to PDFs using JEuclid. - - - - GNU Lesser General Public License (LGPL), version 2.1 or later - http://www.gnu.org/licenses/lgpl.html - - - - - - bintray - https://api.bintray.com/maven/danfickle/maven/com.openhtmltopdf:openhtmltopdf-parent - - - - - - com.openhtmltopdf - openhtmltopdf-core - ${project.version} - - - de.rototor.jeuclid - jeuclid-core - 3.1.12 - - - - - - - ../ - ${project.build.outputDirectory}/META-INF - - LICENSE* - - - - + + + 4.0.0 + + + com.openhtmltopdf + openhtmltopdf-parent + 0.0.1-RC13-SNAPSHOT + + + openhtmltopdf-mathml-support + + jar + + Openhtmltopdf MathML Support + Open HTML to PDF is a CSS 2.1 renderer written in Java. This artifact supports drawing MathML graphics + on to PDFs using JEuclid. + + + + + GNU Lesser General Public License (LGPL), version 2.1 or later + http://www.gnu.org/licenses/lgpl.html + + + + + + bintray + https://api.bintray.com/maven/danfickle/maven/com.openhtmltopdf:openhtmltopdf-parent + + + + + + com.openhtmltopdf + openhtmltopdf-core + ${project.version} + + + com.openhtmltopdf + openhtmltopdf-svg-support + ${project.version} + + + de.rototor.jeuclid + jeuclid-core + 3.1.12 + + + + + + + ../ + ${project.build.outputDirectory}/META-INF + + LICENSE* + + + + diff --git a/openhtmltopdf-mathml-support/src/main/java/com/openhtmltopdf/mathmlsupport/MathMLImage.java b/openhtmltopdf-mathml-support/src/main/java/com/openhtmltopdf/mathmlsupport/MathMLImage.java index 7e03f5dd4..67e562b03 100644 --- a/openhtmltopdf-mathml-support/src/main/java/com/openhtmltopdf/mathmlsupport/MathMLImage.java +++ b/openhtmltopdf-mathml-support/src/main/java/com/openhtmltopdf/mathmlsupport/MathMLImage.java @@ -1,7 +1,6 @@ package com.openhtmltopdf.mathmlsupport; import java.awt.Graphics2D; -import java.util.Arrays; import java.util.List; import net.sourceforge.jeuclid.DOMBuilder; diff --git a/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxRenderer.java b/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxRenderer.java index b906eeefd..28e53d659 100644 --- a/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxRenderer.java +++ b/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxRenderer.java @@ -31,6 +31,7 @@ import com.openhtmltopdf.layout.LayoutContext; import com.openhtmltopdf.layout.SharedContext; import com.openhtmltopdf.outputdevice.helper.BaseDocument; +import com.openhtmltopdf.extend.FSDOMMutator; import com.openhtmltopdf.outputdevice.helper.PageDimensions; import com.openhtmltopdf.outputdevice.helper.UnicodeImplementation; import com.openhtmltopdf.pdfboxout.PdfBoxOutputDevice.Metadata; @@ -75,6 +76,7 @@ public class PdfBoxRenderer implements Closeable { private final SharedContext _sharedContext; private final PdfBoxOutputDevice _outputDevice; + private final List _domMutators; private Document _doc; private BlockBox _root; @@ -105,12 +107,12 @@ public class PdfBoxRenderer implements Closeable { /** * This method is constantly changing as options are added to the builder. */ - PdfBoxRenderer(BaseDocument doc, UnicodeImplementation unicode, - HttpStreamFactory httpStreamFactory, - OutputStream os, FSUriResolver resolver, FSCache cache, SVGDrawer svgImpl, - PageDimensions pageSize, float pdfVersion, String replacementText, boolean testMode, - FSObjectDrawerFactory objectDrawerFactory, String preferredTransformerFactoryImplementationClass, - String producer, SVGDrawer mathmlImpl) { + PdfBoxRenderer(BaseDocument doc, UnicodeImplementation unicode, + HttpStreamFactory httpStreamFactory, + OutputStream os, FSUriResolver resolver, FSCache cache, SVGDrawer svgImpl, + PageDimensions pageSize, float pdfVersion, String replacementText, boolean testMode, + FSObjectDrawerFactory objectDrawerFactory, String preferredTransformerFactoryImplementationClass, + String producer, SVGDrawer mathmlImpl, List domMutators) { _pdfDoc = new PDDocument(); _pdfDoc.setVersion(pdfVersion); @@ -196,7 +198,9 @@ public class PdfBoxRenderer implements Closeable { } this._defaultTextDirection = unicode.textDirection ? BidiSplitter.RTL : BidiSplitter.LTR; - + + this._domMutators = domMutators; + if (doc.html != null) { this.setDocumentFromStringP(doc.html, doc.baseUri); } @@ -263,6 +267,12 @@ private void setDocumentFromStringP(String content, String baseUrl) { private void setDocumentP(Document doc, String url, NamespaceHandler nsh) { _doc = doc; + /* + * Apply potential DOM mutations + */ + for (FSDOMMutator domMutator : _domMutators) + domMutator.mutateDocument(doc); + getFontResolver().flushFontFaceFonts(); _sharedContext.reset(); diff --git a/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfRendererBuilder.java b/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfRendererBuilder.java index 7dade28f6..619bc1c96 100644 --- a/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfRendererBuilder.java +++ b/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfRendererBuilder.java @@ -5,6 +5,8 @@ import com.openhtmltopdf.css.constants.IdentValue; import com.openhtmltopdf.extend.*; import com.openhtmltopdf.outputdevice.helper.BaseDocument; +import com.openhtmltopdf.extend.FSDOMMutator; +import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder; import com.openhtmltopdf.outputdevice.helper.PageDimensions; import com.openhtmltopdf.outputdevice.helper.UnicodeImplementation; import com.openhtmltopdf.util.XRLog; @@ -17,104 +19,52 @@ import java.util.List; import java.util.logging.Level; -public class PdfRendererBuilder -{ - - public static enum TextDirection { RTL, LTR; } - public static enum PageSizeUnits { MM, INCHES } - public static enum FontStyle { NORMAL, ITALIC, OBLIQUE } - - public static final float PAGE_SIZE_LETTER_WIDTH = 8.5f; - public static final float PAGE_SIZE_LETTER_HEIGHT = 11.0f; - public static final PageSizeUnits PAGE_SIZE_LETTER_UNITS = PageSizeUnits.INCHES; - - private boolean _textDirection = false; - private boolean _testMode = false; - private HttpStreamFactory _httpStreamFactory; - private BidiSplitterFactory _splitter; - private BidiReorderer _reorderer; - private String _html; - private Document _document; - private String _baseUri; - private String _uri; - private File _file; - private OutputStream _os; - private FSUriResolver _resolver; - private FSCache _cache; - private SVGDrawer _svgImpl; - private SVGDrawer _mathmlImpl; - private Float _pageWidth; - private Float _pageHeight; - private boolean _isPageSizeInches; - private float _pdfVersion = 1.7f; - private String _replacementText; - private String _producer; - private FSTextBreaker _lineBreaker; - private FSTextBreaker _charBreaker; - private FSTextTransformer _unicodeToUpperTransformer; - private FSTextTransformer _unicodeToLowerTransformer; - private FSTextTransformer _unicodeToTitleTransformer; - private FSObjectDrawerFactory _objectDrawerFactory; - private String _preferredTransformerFactoryImplementationClass = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"; - - private static class AddedFont { - private final FSSupplier supplier; - private final File fontFile; - private final Integer weight; - private final String family; - private final boolean subset; - private final FontStyle style; - - private AddedFont(FSSupplier supplier, File fontFile, Integer weight, String family, boolean subset, FontStyle style) { - this.supplier = supplier; - this.fontFile = fontFile; - this.weight = weight; - this.family = family; - this.subset = subset; - this.style = style; - } - } - - private final List _fonts = new ArrayList(); - - /** - * Run the XHTML/XML to PDF conversion and output to an output stream set by toStream. - * @throws Exception - */ - public void run() throws Exception { - PdfBoxRenderer renderer = null; - try { - renderer = this.buildPdfRenderer(); - renderer.layout(); - renderer.createPDF(); - } finally { - if (renderer != null) - renderer.close(); - } - } - - /** - * Build a PdfBoxRenderer for further customization. - * Remember to call {@link PdfBoxRenderer#cleanup()} after use. - * @return - */ - public PdfBoxRenderer buildPdfRenderer() { - UnicodeImplementation unicode = new UnicodeImplementation(_reorderer, _splitter, _lineBreaker, - _unicodeToLowerTransformer, _unicodeToUpperTransformer, _unicodeToTitleTransformer, _textDirection, _charBreaker); - - PageDimensions pageSize = new PageDimensions(_pageWidth, _pageHeight, _isPageSizeInches); - - BaseDocument doc = new BaseDocument(_baseUri, _html, _document, _file, _uri); - - PdfBoxRenderer renderer = new PdfBoxRenderer( - doc, unicode, _httpStreamFactory, _os, _resolver, - _cache, _svgImpl, pageSize, _pdfVersion, _replacementText, - _testMode, _objectDrawerFactory, _preferredTransformerFactoryImplementationClass, - _producer, _mathmlImpl); - - /* - * Register all Fonts - */ +public class PdfRendererBuilder extends BaseRendererBuilder { + private final List _fonts = new ArrayList(); + private OutputStream _os; + private float _pdfVersion = 1.7f; + private String _producer; + + /** + * Run the XHTML/XML to PDF conversion and output to an output stream set by + * toStream. + * + * @throws Exception + */ + public void run() throws Exception { + PdfBoxRenderer renderer = null; + try { + renderer = this.buildPdfRenderer(); + renderer.layout(); + renderer.createPDF(); + } finally { + if (renderer != null) + renderer.close(); + } + } + + /** + * Build a PdfBoxRenderer for further customization. Remember to call + * {@link PdfBoxRenderer#cleanup()} after use. + * + * @return + */ + public PdfBoxRenderer buildPdfRenderer() { + UnicodeImplementation unicode = new UnicodeImplementation(_reorderer, _splitter, _lineBreaker, + _unicodeToLowerTransformer, _unicodeToUpperTransformer, _unicodeToTitleTransformer, _textDirection, + _charBreaker); + + PageDimensions pageSize = new PageDimensions(_pageWidth, _pageHeight, _isPageSizeInches); + + BaseDocument doc = new BaseDocument(_baseUri, _html, _document, _file, _uri); + + PdfBoxRenderer renderer = new PdfBoxRenderer(doc, unicode, _httpStreamFactory, _os, _resolver, _cache, _svgImpl, + pageSize, _pdfVersion, _replacementText, _testMode, _objectDrawerFactory, + _preferredTransformerFactoryImplementationClass, _producer, _mathmlImpl, _domMutators); + + /* + * Register all Fonts + */ PdfBoxFontResolver resolver = renderer.getFontResolver(); for (AddedFont font : _fonts) { IdentValue fontStyle = null; @@ -127,10 +77,9 @@ public PdfBoxRenderer buildPdfRenderer() { fontStyle = IdentValue.OBLIQUE; } - if( font.supplier != null) { + if (font.supplier != null) { resolver.addFont(font.supplier, font.family, font.weight, fontStyle, font.subset); - } - else { + } else { try { resolver.addFont(font.fontFile, font.family, font.weight, fontStyle, font.subset); } catch (Exception e) { @@ -139,339 +88,127 @@ public PdfBoxRenderer buildPdfRenderer() { } } - return renderer; - } - - /** - * The default text direction of the document. LTR by default. - * @param textDirection - * @return - */ - public PdfRendererBuilder defaultTextDirection(TextDirection textDirection) { - this._textDirection = textDirection == TextDirection.RTL; - return this; - } - - /** - * Whether to use test mode and output the PDF uncompressed. Turned off by default. - * @param mode - * @return - */ - public PdfRendererBuilder testMode(boolean mode) { - this._testMode = mode; - return this; - } - - /** - * Provides an HttpStreamFactory implementation if the user desires to use an external - * HTTP/HTTPS implementation. Uses URL::openStream by default. - * @param factory - * @return - */ - public PdfRendererBuilder useHttpStreamImplementation(HttpStreamFactory factory) { - this._httpStreamFactory = factory; - return this; - } - - /** - * Provides a uri resolver to resolve relative uris or private uri schemes. - * @param resolver - * @return - */ - public PdfRendererBuilder useUriResolver(FSUriResolver resolver) { - this._resolver = resolver; - return this; - } - - /** - * Provides an external cache which can choose to cache items between runs, - * such as fonts or logo images. - * @param cache - * @return - */ - public PdfRendererBuilder useCache(FSCache cache) { - this._cache = cache; - return this; - } - - /** - * Provides a text splitter to split text into directional runs. Does nothing by default. - * @param splitter - * @return - */ - public PdfRendererBuilder useUnicodeBidiSplitter(BidiSplitterFactory splitter) { - this._splitter = splitter; - return this; - } - - /** - * Provides a reorderer to properly reverse RTL text. No-op by default. - * @param reorderer - * @return - */ - public PdfRendererBuilder useUnicodeBidiReorderer(BidiReorderer reorderer) { - this._reorderer = reorderer; - return this; - } - - /** - * Provides a string containing XHTML/XML to convert to PDF. - * @param html - * @param baseUri - * @return - */ - public PdfRendererBuilder withHtmlContent(String html, String baseUri) { - this._html = html; - this._baseUri = baseUri; - return this; - } - - /** - * Provides a w3c DOM Document acquired from an external source. - * @param doc - * @param baseUri - * @return - */ - public PdfRendererBuilder withW3cDocument(org.w3c.dom.Document doc, String baseUri) { - this._document = doc; - this._baseUri = baseUri; - return this; - } - - /** - * Provides a URI to convert to PDF. The URI MUST point to a strict XHTML/XML document. - * @param uri - * @return - */ - public PdfRendererBuilder withUri(String uri) { - this._uri = uri; - return this; - } - - /** - * Provides a file to convert to PDF. The file MUST contain XHTML/XML in UTF-8 encoding. - * @param file - * @return - */ - public PdfRendererBuilder withFile(File file) { - this._file = file; - return this; - } - - /** - * An output stream to output the resulting PDF. The caller is required to close the output stream after calling - * run. - * @param out - * @return - */ - public PdfRendererBuilder toStream(OutputStream out) { - this._os = out; - return this; - } - - /** - * Uses the specified SVG drawer implementation. - * @param svgImpl - * @return - */ - public PdfRendererBuilder useSVGDrawer(SVGDrawer svgImpl) { - this._svgImpl = svgImpl; - return this; - } - - /** - * Use the specified MathML implementation. - * @param mathMlImpl - * @return this for method chaining - */ - public PdfRendererBuilder useMathMLDrawer(SVGDrawer mathMlImpl) { - this._mathmlImpl = mathMlImpl; - return this; - } - - /** - * Specifies the default page size to use if none is specified in CSS. - * @param pageWidth - * @param pageHeight - * @param units either mm or inches. - * @see {@link #PAGE_SIZE_LETTER_WIDTH}, {@link #PAGE_SIZE_LETTER_HEIGHT} and {@link #PAGE_SIZE_LETTER_UNITS} - * @return - */ - public PdfRendererBuilder useDefaultPageSize(float pageWidth, float pageHeight, PageSizeUnits units) { - this._pageWidth = pageWidth; - this._pageHeight = pageHeight; - this._isPageSizeInches = (units == PageSizeUnits.INCHES); - return this; - } - - /** - * Set the PDF version, typically we use 1.7. - * If you set a lower version, it is your responsibility to make sure - * no more recent PDF features are used. - * @param version - * @return - */ - public PdfRendererBuilder usePdfVersion(float version) { - this._pdfVersion = version; - return this; - } - - /** - * The replacement text to use if a character is cannot be renderered by any of the specified fonts. - * This is not broken across lines so should be one or zero characters for best results. - * Also, make sure it can be rendered by at least one of your specified fonts! - * The default is the # character. - * @param replacement - * @return - */ - public PdfRendererBuilder useReplacementText(String replacement) { - this._replacementText = replacement; - return this; - } - - /** - * Specify the line breaker. By default a Java default BreakIterator line instance is used - * with US locale. Additionally, this is wrapped with UrlAwareLineBreakIterator to also - * break before the forward slash (/) character so that long URIs can be broken on to multiple lines. - * - * You may want to use a BreakIterator with a different locale (wrapped by UrlAwareLineBreakIterator or not) - * or a more advanced BreakIterator from icu4j (see the rtl-support module for an example). - * @param breaker - * @return - */ - public PdfRendererBuilder useUnicodeLineBreaker(FSTextBreaker breaker) { - this._lineBreaker = breaker; - return this; - } - - /** - * Specify the character breaker. By default a break iterator character instance is used with - * US locale. Currently this is used when word-wrap: break-word is in - * effect. - * @param breaker - * @return - */ - public PdfRendererBuilder useUnicodeCharacterBreaker(FSTextBreaker breaker) { - this._charBreaker = breaker; - return this; - } - - /** - * Specify a transformer to use to upper case strings. - * By default String::toUpperCase(Locale.US) is used. - * @param tr - * @return - */ - public PdfRendererBuilder useUnicodeToUpperTransformer(FSTextTransformer tr) { - this._unicodeToUpperTransformer = tr; - return this; - } - - /** - * Specify a transformer to use to lower case strings. - * By default String::toLowerCase(Locale.US) is used. - * @param tr - * @return - */ - public PdfRendererBuilder useUnicodeToLowerTransformer(FSTextTransformer tr) { - this._unicodeToLowerTransformer = tr; - return this; - } - - /** - * Specify a transformer to title case strings. - * By default a best effort implementation (non locale aware) is used. - * @param tr - * @return - */ - public PdfRendererBuilder useUnicodeToTitleTransformer(FSTextTransformer tr) { - this._unicodeToTitleTransformer = tr; - return this; - } - - /** - * Add a font programmatically. If the font is NOT subset, it will be downloaded when the renderer is run, otherwise - * the font will only be downloaded if needed. Therefore, the user could add many fonts, confidant that only those - * that are used will be downloaded and processed. - * - * The InputStream returned by the supplier will be closed by the caller. Fonts should generally be subset, except - * when used in form controls. FSSupplier is a lambda compatible interface. - * - * Fonts can also be added using a font-face at-rule in the CSS. - * @param supplier - * @param fontFamily - * @param fontWeight - * @param fontStyle - * @param subset - * @return - */ - public PdfRendererBuilder useFont(FSSupplier supplier, String fontFamily, Integer fontWeight, FontStyle fontStyle, boolean subset) { - this._fonts.add(new AddedFont(supplier, null, fontWeight, fontFamily, subset, fontStyle)); - return this; - } - - /** - * Simpler overload for {@link #useFont(FSSupplier, String, Integer, FontStyle, boolean)} - * @param supplier - * @param fontFamily - * @return - */ - public PdfRendererBuilder useFont(FSSupplier supplier, String fontFamily) { - return this.useFont(supplier, fontFamily, 400, FontStyle.NORMAL, true); - } - - /** - * Like {@link #useFont(FSSupplier, String, Integer, FontStyle, boolean)}, but allows to supply a font file. If the font file - * is a .ttc file it is handled as TrueTypeCollection. If you have the font in file form you should use this API. - */ - public PdfRendererBuilder useFont(File fontFile, String fontFamily, Integer fontWeight, FontStyle fontStyle, boolean subset) { - this._fonts.add(new AddedFont(null, fontFile, fontWeight, fontFamily, subset, fontStyle)); - return this; - } - - /** - * Simpler overload for {@link #useFont(File, String, Integer, FontStyle, boolean)} - * @param fontFile - * @param fontFamily - * @return - */ - public PdfRendererBuilder useFont(File fontFile, String fontFamily) { - return this.useFont(fontFile, fontFamily, 400, FontStyle.NORMAL, true); - } - - /** - * Set a factory for <object> drawers - * @param objectDrawerFactory Object Drawer Factory - * @return this for method chaining - */ - public PdfRendererBuilder useObjectDrawerFactory(FSObjectDrawerFactory objectDrawerFactory) { - this._objectDrawerFactory = objectDrawerFactory; - return this; - } - - /** - * This method should be considered advanced and is not required for most setups. - * Set a preferred implementation class for use as javax.xml.transform.TransformerFactory. Use null to let - * a default implementation class be used. The default is "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl". - * This seems to work with most systems but not JBoss Wildfly and related setups. In this case you can use null to let - * the container use whatever TransformerFactory it has available. - * @param transformerFactoryClass - * @return this for method chaining - */ - public PdfRendererBuilder useTransformerFactoryImplementationClass(String transformerFactoryClass) { - this._preferredTransformerFactoryImplementationClass = transformerFactoryClass; - return this; - } - - /** - * Set a producer on the output document - * - * @param producer the name of the producer to set defaults to openhtmltopdf.com - * @return this for method chaining - */ - public PdfRendererBuilder withProducer(String producer) { - this._producer = producer; - return this; - } + return renderer; + } + + /** + * An output stream to output the resulting PDF. The caller is required to close + * the output stream after calling run. + * + * @param out + * @return + */ + public PdfRendererBuilder toStream(OutputStream out) { + this._os = out; + return this; + } + + /** + * Set the PDF version, typically we use 1.7. If you set a lower version, it is + * your responsibility to make sure no more recent PDF features are used. + * + * @param version + * @return + */ + public PdfRendererBuilder usePdfVersion(float version) { + this._pdfVersion = version; + return this; + } + + + + /** + * Add a font programmatically. If the font is NOT subset, it will be downloaded + * when the renderer is run, otherwise the font will only be downloaded if + * needed. Therefore, the user could add many fonts, confidant that only those + * that are used will be downloaded and processed. + * + * The InputStream returned by the supplier will be closed by the caller. Fonts + * should generally be subset, except when used in form controls. FSSupplier is + * a lambda compatible interface. + * + * Fonts can also be added using a font-face at-rule in the CSS. + * + * @param supplier + * @param fontFamily + * @param fontWeight + * @param fontStyle + * @param subset + * @return + */ + public PdfRendererBuilder useFont(FSSupplier supplier, String fontFamily, Integer fontWeight, + FontStyle fontStyle, boolean subset) { + this._fonts.add(new AddedFont(supplier, null, fontWeight, fontFamily, subset, fontStyle)); + return this; + } + + /** + * Simpler overload for + * {@link #useFont(FSSupplier, String, Integer, FontStyle, boolean)} + * + * @param supplier + * @param fontFamily + * @return + */ + public PdfRendererBuilder useFont(FSSupplier supplier, String fontFamily) { + return this.useFont(supplier, fontFamily, 400, FontStyle.NORMAL, true); + } + + /** + * Like {@link #useFont(FSSupplier, String, Integer, FontStyle, boolean)}, but + * allows to supply a font file. If the font file is a .ttc file it is handled + * as TrueTypeCollection. If you have the font in file form you should use this + * API. + */ + public PdfRendererBuilder useFont(File fontFile, String fontFamily, Integer fontWeight, FontStyle fontStyle, + boolean subset) { + this._fonts.add(new AddedFont(null, fontFile, fontWeight, fontFamily, subset, fontStyle)); + return this; + } + + /** + * Simpler overload for + * {@link #useFont(File, String, Integer, FontStyle, boolean)} + * + * @param fontFile + * @param fontFamily + * @return + */ + public PdfRendererBuilder useFont(File fontFile, String fontFamily) { + return this.useFont(fontFile, fontFamily, 400, FontStyle.NORMAL, true); + } + + + /** + * Set a producer on the output document + * + * @param producer + * the name of the producer to set defaults to openhtmltopdf.com + * @return this for method chaining + */ + public PdfRendererBuilder withProducer(String producer) { + this._producer = producer; + return this; + } + + + private static class AddedFont { + private final FSSupplier supplier; + private final File fontFile; + private final Integer weight; + private final String family; + private final boolean subset; + private final FontStyle style; + + private AddedFont(FSSupplier supplier, File fontFile, Integer weight, String family, + boolean subset, FontStyle style) { + this.supplier = supplier; + this.fontFile = fontFile; + this.weight = weight; + this.family = family; + this.subset = subset; + this.style = style; + } + } } diff --git a/pom.xml b/pom.xml index 0353345f7..af8023589 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,7 @@ openhtmltopdf-java2d openhtmltopdf-objects openhtmltopdf-mathml-support + openhtmltopdf-latex-support