diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/CssContext.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/CssContext.java index 097483db7..07b5e3215 100644 --- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/CssContext.java +++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/css/style/CssContext.java @@ -1,5 +1,7 @@ package org.xhtmlrenderer.css.style; +import com.google.errorprone.annotations.CheckReturnValue; +import org.jspecify.annotations.Nullable; import org.xhtmlrenderer.context.StyleReference; import org.xhtmlrenderer.css.value.FontSpecification; import org.xhtmlrenderer.render.FSFont; @@ -18,6 +20,8 @@ public interface CssContext { float getXHeight(FontSpecification parentFont); + @Nullable + @CheckReturnValue FSFont getFont(FontSpecification font); // FIXME Doesn't really belong here, but this is diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/LayoutContext.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/LayoutContext.java index 6383ddea0..6f55b9e34 100644 --- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/LayoutContext.java +++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/LayoutContext.java @@ -281,11 +281,14 @@ public float getXHeight(FontSpecification parentFont) { return _sharedContext.getXHeight(getFontContext(), parentFont); } + @Nullable + @CheckReturnValue @Override public FSFont getFont(FontSpecification font) { return _sharedContext.getFont(font); } + @CheckReturnValue public UserAgentCallback getUac() { return _sharedContext.getUac(); } diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/SharedContext.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/SharedContext.java index 91225dc10..c3ab65005 100644 --- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/SharedContext.java +++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/layout/SharedContext.java @@ -19,6 +19,7 @@ */ package org.xhtmlrenderer.layout; +import com.google.errorprone.annotations.CheckReturnValue; import org.jspecify.annotations.Nullable; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -45,6 +46,7 @@ import org.xhtmlrenderer.render.RenderingContext; import org.xhtmlrenderer.simple.extend.FormSubmissionListener; import org.xhtmlrenderer.swing.Java2DTextRenderer; +import org.xhtmlrenderer.swing.NaiveUserAgent; import org.xhtmlrenderer.swing.SwingReplacedElementFactory; import org.xhtmlrenderer.util.XRLog; @@ -55,6 +57,7 @@ import java.util.Set; import static java.util.Arrays.asList; +import static java.util.Objects.requireNonNull; /** * The SharedContext is that which is kept between successive layout and render runs. @@ -87,24 +90,41 @@ public final class SharedContext { private int dotsPerPixel = 1; + @Nullable private Map styleMap; private ReplacedElementFactory replacedElementFactory; + @Nullable private Rectangle temporaryCanvas; private LineBreakingStrategy lineBreakingStrategy = new DefaultLineBreakingStrategy(); public SharedContext() { + this(new NaiveUserAgent()); + } + + public SharedContext(UserAgentCallback userAgent, FontResolver fontResolver, + ReplacedElementFactory replacedElementFactory, + TextRenderer textRenderer, + float dpi, int dotsPerPixel) { + uac = requireNonNull(userAgent); + this.css = new StyleReference(userAgent); + this.fontResolver = requireNonNull(fontResolver); + this.replacedElementFactory = replacedElementFactory; + this.textRenderer = requireNonNull(textRenderer); + media = "screen"; + setDPI(dpi); + setDotsPerPixel(dotsPerPixel); } public SharedContext(UserAgentCallback uac) { fontResolver = new AWTFontResolver(); replacedElementFactory = new SwingReplacedElementFactory(); - setMedia("screen"); - this.uac = uac; - setCss(new StyleReference(uac)); + this.media = "screen"; + this.uac = requireNonNull(uac); + this.css = new StyleReference(uac); XRLog.render("Using CSS implementation from: " + getCss().getClass().getName()); - setTextRenderer(new Java2DTextRenderer()); + this.textRenderer = new Java2DTextRenderer(); try { setDPI(Toolkit.getDefaultToolkit().getScreenResolution()); } catch (HeadlessException e) { @@ -114,13 +134,13 @@ public SharedContext(UserAgentCallback uac) { public SharedContext(UserAgentCallback uac, FontResolver fr, ReplacedElementFactory ref, TextRenderer tr, float dpi) { - fontResolver = fr; + fontResolver = requireNonNull(fr); replacedElementFactory = ref; - setMedia("screen"); + this.media = "screen"; this.uac = uac; - setCss(new StyleReference(uac)); + this.css = new StyleReference(uac); XRLog.render("Using CSS implementation from: " + getCss().getClass().getName()); - setTextRenderer(tr); + this.textRenderer = requireNonNull(tr); setDPI(dpi); } @@ -148,6 +168,7 @@ public RenderingContext newRenderingContextInstance(OutputDevice outputDevice, F * * @return The fontResolver value */ + @CheckReturnValue public FontResolver getFontResolver() { return fontResolver; } @@ -161,6 +182,7 @@ public void flushFonts() { /** * The media for this context */ + @CheckReturnValue public String getMedia() { return media; } @@ -171,8 +193,10 @@ public String getMedia() { private boolean debug_draw_inline_boxes; private boolean debug_draw_font_metrics; + @Nullable private FSCanvas canvas; + @CheckReturnValue public TextRenderer getTextRenderer() { return textRenderer; } @@ -222,6 +246,8 @@ public void setCss(StyleReference css) { this.css = css; } + @Nullable + @CheckReturnValue public FSCanvas getCanvas() { return canvas; } @@ -235,6 +261,7 @@ public void setTemporaryCanvas(Rectangle rect) { } + @Nullable public Rectangle getFixedRectangle() { if (getCanvas() == null) { return temporaryCanvas; @@ -245,12 +272,14 @@ public Rectangle getFixedRectangle() { } } + @Nullable private NamespaceHandler namespaceHandler; public void setNamespaceHandler(NamespaceHandler nh) { namespaceHandler = nh; } + @Nullable public NamespaceHandler getNamespaceHandler() { return namespaceHandler; } @@ -259,6 +288,8 @@ public void addBoxId(String id, Box box) { idMap.put(id, box); } + @Nullable + @CheckReturnValue public Box getBoxById(String id) { return idMap.get(id); } @@ -278,7 +309,7 @@ public Map getIdMap() * @param textRenderer The new textRenderer value */ public void setTextRenderer(TextRenderer textRenderer) { - this.textRenderer = textRenderer; + this.textRenderer = requireNonNull(textRenderer); } /** @@ -291,7 +322,7 @@ public void setTextRenderer(TextRenderer textRenderer) { * @param media The new media value */ public void setMedia(String media) { - this.media = media; + this.media = requireNonNull(media); } /** @@ -299,6 +330,7 @@ public void setMedia(String media) { * * @return The uac value */ + @CheckReturnValue public UserAgentCallback getUac() { return uac; } @@ -308,11 +340,8 @@ public UserAgentCallback getUserAgentCallback() { } public void setUserAgentCallback(UserAgentCallback userAgentCallback) { - StyleReference styleReference = getCss(); - if (styleReference != null) { - styleReference.setUserAgentCallback(userAgentCallback); - } - uac = userAgentCallback; + getCss().setUserAgentCallback(userAgentCallback); + uac = requireNonNull(userAgentCallback); } /** @@ -348,14 +377,16 @@ public float getMmPerPx() { return mm_per_dot; } + @Nullable + @CheckReturnValue public FSFont getFont(FontSpecification spec) { - return getFontResolver().resolveFont(this, spec); + return fontResolver.resolveFont(this, spec); } //strike-through offset should always be half of the height of lowercase x... //and it is defined even for fonts without 'x'! public float getXHeight(FontContext fontContext, FontSpecification fs) { - FSFont font = getFontResolver().resolveFont(this, fs); + FSFont font = fontResolver.resolveFont(this, fs); FSFontMetrics fm = getTextRenderer().getFSFontMetrics(fontContext, font, " "); float sto = fm.getStrikethroughOffset(); return fm.getAscent() - 2 * Math.abs(sto) + fm.getStrikethroughThickness(); @@ -406,11 +437,7 @@ public boolean isPrint() { public void setPrint(boolean print) { this.print = print; - if (print) { - setMedia("print"); - } else { - setMedia("screen"); - } + setMedia(print ? "print" : "screen"); } /** @@ -440,14 +467,13 @@ public void setPrint(boolean print) { * add a new font mapping, or replace an existing one */ public void setFontMapping(String name, Font font) { - FontResolver resolver = getFontResolver(); - if (resolver instanceof AWTFontResolver) { - ((AWTFontResolver)resolver).setFontMapping(name, font); + if (fontResolver instanceof AWTFontResolver) { + ((AWTFontResolver) fontResolver).setFontMapping(name, font); } } public void setFontResolver(FontResolver resolver) { - fontResolver = resolver; + fontResolver = requireNonNull(resolver); } public int getDotsPerPixel() { @@ -507,9 +533,7 @@ public void setReplacedElementFactory(ReplacedElementFactory ref) { throw new NullPointerException("replacedElementFactory may not be null"); } - if (this.replacedElementFactory != null) { - this.replacedElementFactory.reset(); - } + this.replacedElementFactory.reset(); this.replacedElementFactory = ref; } diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/render/RenderingContext.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/render/RenderingContext.java index a2ae42b83..0f9555d6d 100644 --- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/render/RenderingContext.java +++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/render/RenderingContext.java @@ -19,6 +19,7 @@ */ package org.xhtmlrenderer.render; +import com.google.errorprone.annotations.CheckReturnValue; import org.jspecify.annotations.Nullable; import org.xhtmlrenderer.context.StyleReference; import org.xhtmlrenderer.css.style.CssContext; @@ -116,10 +117,14 @@ public FontResolver getFontResolver() { return sharedContext.getFontResolver(); } + @Nullable + @CheckReturnValue public FSFont getFont(FontSpecification font) { return sharedContext.getFont(font); } + @Nullable + @CheckReturnValue public FSCanvas getCanvas() { return sharedContext.getCanvas(); } @@ -213,6 +218,8 @@ public int getInitialPageNo() { return initialPageNo; } + @Nullable + @CheckReturnValue public Box getBoxById(String id) { return sharedContext.getBoxById(id); } diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/simple/Graphics2DRenderer.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/simple/Graphics2DRenderer.java index 25dfbf920..1b2e9a1b4 100755 --- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/simple/Graphics2DRenderer.java +++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/simple/Graphics2DRenderer.java @@ -19,6 +19,7 @@ */ package org.xhtmlrenderer.simple; +import org.jspecify.annotations.Nullable; import org.w3c.dom.Document; import org.xhtmlrenderer.layout.SharedContext; @@ -55,6 +56,7 @@ public class Graphics2DRenderer { /** * Dimensions of the image to render, in pixels. */ + @Nullable protected Dimension dim; /** @@ -73,7 +75,7 @@ public Graphics2DRenderer() { * @param g2 the canvas to layout on. * @param dim dimensions of the container for the document */ - public void layout(Graphics2D g2, Dimension dim) { + public void layout(Graphics2D g2, @Nullable Dimension dim) { this.dim = dim; if (dim != null) { panel.setSize(dim); @@ -115,15 +117,6 @@ public void setDocument(Document doc, String base_url) { panel.setDocument(doc, base_url); } - /** - * Sets the SharedContext for rendering. - * - * @param ctx The new renderingContext value - */ - public void setSharedContext(SharedContext ctx) { - panel.setSharedContext(ctx); - } - /** * Returns the size image needed to render the document without anything * going off the side. Could be different from the dimensions passed into diff --git a/flying-saucer-core/src/main/java/org/xhtmlrenderer/swing/Java2DRenderer.java b/flying-saucer-core/src/main/java/org/xhtmlrenderer/swing/Java2DRenderer.java index c5c3bcec9..a8f4c5ce7 100644 --- a/flying-saucer-core/src/main/java/org/xhtmlrenderer/swing/Java2DRenderer.java +++ b/flying-saucer-core/src/main/java/org/xhtmlrenderer/swing/Java2DRenderer.java @@ -95,7 +95,9 @@ public class Java2DRenderer { * Whether we've completed rendering; image will only be rendered once. */ private boolean rendered; + @Nullable private String sourceDocument; + @Nullable private String sourceDocumentBase; private final int width; private final int height; @@ -324,14 +326,6 @@ private Java2DRenderer(int width, int height, int bufferedImageType) { UserAgentCallback userAgent = new NaiveUserAgent(); sharedContext = new SharedContext(userAgent); - - AWTFontResolver fontResolver = new AWTFontResolver(); - sharedContext.setFontResolver(fontResolver); - - SwingReplacedElementFactory replacedElementFactory = new SwingReplacedElementFactory(); - sharedContext.setReplacedElementFactory(replacedElementFactory); - - sharedContext.setTextRenderer(new Java2DTextRenderer()); sharedContext.setDPI(72 * (float) Java2DRenderer.DEFAULT_DOTS_PER_POINT); sharedContext.setDotsPerPixel(Java2DRenderer.DEFAULT_DOTS_PER_PIXEL); sharedContext.setPrint(false); diff --git a/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/ITextOutputDevice.java b/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/ITextOutputDevice.java index 693425d60..1f78254bf 100644 --- a/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/ITextOutputDevice.java +++ b/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/ITextOutputDevice.java @@ -139,7 +139,7 @@ public class ITextOutputDevice extends AbstractOutputDevice implements OutputDev private List _bookmarks = new ArrayList<>(); - private final List _metadata = new ArrayList<>(); + private final List<@Nullable Metadata> _metadata = new ArrayList<>(); private Box _root; @@ -1090,12 +1090,12 @@ public void addMetadata(String name, String value) { * @return the content value of the first found metadata element; otherwise * null. */ + @Nullable + @CheckReturnValue public String getMetadataByName(String name) { - if (name != null) { - for (Metadata m : _metadata) { - if ((m != null) && m.getName().equalsIgnoreCase(name)) { - return m.getContent(); - } + for (Metadata m : _metadata) { + if ((m != null) && m.getName().equalsIgnoreCase(name)) { + return m.getContent(); } } return null; diff --git a/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/ITextRenderer.java b/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/ITextRenderer.java index f3f683c11..6154b9037 100644 --- a/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/ITextRenderer.java +++ b/flying-saucer-pdf/src/main/java/org/xhtmlrenderer/pdf/ITextRenderer.java @@ -26,7 +26,6 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; -import org.xhtmlrenderer.context.StyleReference; import org.xhtmlrenderer.css.style.CalculatedStyle.Edge; import org.xhtmlrenderer.extend.FontResolver; import org.xhtmlrenderer.extend.NamespaceHandler; @@ -61,6 +60,7 @@ import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; +import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; @@ -75,20 +75,25 @@ public class ITextRenderer { private final SharedContext _sharedContext; private final ITextOutputDevice _outputDevice; + @Nullable private Document _doc; + @Nullable private BlockBox _root; private final float _dotsPerPoint; private com.lowagie.text.Document _pdfDoc; + @Nullable private PdfWriter _writer; + @Nullable private PDFEncryption _pdfEncryption; // note: not hard-coding a default version in the _pdfVersion field as this // may change between iText releases // check for null before calling writer.setPdfVersion() // use one of the values in PDFWriter.VERSION... + @Nullable private Character _pdfVersion; private final char[] validPdfVersions = { @@ -100,12 +105,12 @@ public class ITextRenderer { PdfWriter.VERSION_1_7 }; + @Nullable private Integer _pdfXConformance; + @Nullable private PDFCreationListener _listener; - private boolean _timeouted; - public ITextRenderer(File file) throws IOException { this(); File parent = file.getAbsoluteFile().getParentFile(); @@ -155,18 +160,12 @@ public ITextRenderer(float dotsPerPoint, int dotsPerPixel, ITextOutputDevice out TextRenderer textRenderer) { _dotsPerPoint = dotsPerPoint; _outputDevice = outputDevice; - _sharedContext = new SharedContext(); - _sharedContext.setUserAgentCallback(userAgent); - _sharedContext.setCss(new StyleReference(userAgent)); + _sharedContext = new SharedContext(userAgent, fontResolver, replacedElementFactory, textRenderer, + 72 * _dotsPerPoint); + _outputDevice.setSharedContext(_sharedContext); - _sharedContext.setFontResolver(fontResolver); - _sharedContext.setReplacedElementFactory(replacedElementFactory); - _sharedContext.setTextRenderer(textRenderer); - _sharedContext.setDPI(72 * _dotsPerPoint); - _sharedContext.setDotsPerPixel(dotsPerPixel); _sharedContext.setPrint(true); _sharedContext.setInteractive(false); - _timeouted= false; } public Document getDocument() { @@ -246,15 +245,12 @@ public void setPDFEncryption(PDFEncryption pdfEncryption) { } public void setPDFVersion(char _v) { - for (char validPdfVersion : validPdfVersions) { - if (_v == validPdfVersion) { - _pdfVersion = _v; - return; - } + if (Arrays.binarySearch(validPdfVersions, _v) < 0) { + throw new IllegalArgumentException(""" + Invalid PDF version character: "%s"; use one of constants PdfWriter.VERSION_1_N. + """.formatted(_v).trim()); } - throw new IllegalArgumentException(""" - Invalid PDF version character: "%s"; use one of constants PdfWriter.VERSION_1_N. - """.formatted(_v).trim()); + _pdfVersion = _v; } public char getPDFVersion() { @@ -427,7 +423,7 @@ private void writePDF(List pages, RenderingContext c, com.lowagie.text. setDidValues(doc); // set PDF header fields from meta data for (int i = 0; i < pageCount; i++) { - if (isTimeouted() || Thread.currentThread().isInterrupted()) + if (Thread.currentThread().isInterrupted()) throw new RuntimeException("Timeout occurred"); PageBox currentPage = pages.get(i); @@ -598,16 +594,4 @@ public PDFCreationListener getListener() { public void setListener(PDFCreationListener listener) { _listener = listener; } - - public PdfWriter getWriter() { - return _writer; - } - - public void setTimeouted(boolean timeouted) { - _timeouted= timeouted; - } - - public boolean isTimeouted() { - return _timeouted; - } }