From 16222810df1cc40dba8bfa1465111b96841bb3b5 Mon Sep 17 00:00:00 2001 From: danfickle Date: Sat, 15 May 2021 18:10:05 +1000 Subject: [PATCH] Put relative raster images from SVGs thru the resolver. Also change tests so that they do not call out on network. Only way I could achieve it was to register a bridge extension. --- .../html/malicious-svg-insecure-mode.html | 2 +- .../html/malicious-svg-secure-mode.html | 2 +- .../svgsupport/PDFTranscoder.java | 14 +- .../svgsupport/SVGImageExtension.java | 129 ++++++++++++++++++ 4 files changed, 136 insertions(+), 11 deletions(-) create mode 100644 openhtmltopdf-svg-support/src/main/java/com/openhtmltopdf/svgsupport/SVGImageExtension.java diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/html/malicious-svg-insecure-mode.html b/openhtmltopdf-examples/src/main/resources/visualtest/html/malicious-svg-insecure-mode.html index 8724891d5..7cb7b2217 100644 --- a/openhtmltopdf-examples/src/main/resources/visualtest/html/malicious-svg-insecure-mode.html +++ b/openhtmltopdf-examples/src/main/resources/visualtest/html/malicious-svg-insecure-mode.html @@ -27,7 +27,7 @@ - + diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/html/malicious-svg-secure-mode.html b/openhtmltopdf-examples/src/main/resources/visualtest/html/malicious-svg-secure-mode.html index e61515376..836900465 100644 --- a/openhtmltopdf-examples/src/main/resources/visualtest/html/malicious-svg-secure-mode.html +++ b/openhtmltopdf-examples/src/main/resources/visualtest/html/malicious-svg-secure-mode.html @@ -27,7 +27,7 @@ - + diff --git a/openhtmltopdf-svg-support/src/main/java/com/openhtmltopdf/svgsupport/PDFTranscoder.java b/openhtmltopdf-svg-support/src/main/java/com/openhtmltopdf/svgsupport/PDFTranscoder.java index 091173ed9..8cbab8f3a 100644 --- a/openhtmltopdf-svg-support/src/main/java/com/openhtmltopdf/svgsupport/PDFTranscoder.java +++ b/openhtmltopdf-svg-support/src/main/java/com/openhtmltopdf/svgsupport/PDFTranscoder.java @@ -16,10 +16,10 @@ import com.openhtmltopdf.simple.extend.ReplacedElementScaleHelper; import com.openhtmltopdf.util.LogMessageId; import com.openhtmltopdf.util.XRLog; + import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.FontFace; import org.apache.batik.bridge.FontFamilyResolver; -import org.apache.batik.bridge.svg12.SVG12BridgeContext; import org.apache.batik.gvt.font.GVTFontFamily; import org.apache.batik.transcoder.ErrorHandler; import org.apache.batik.transcoder.SVGAbstractTranscoder; @@ -264,14 +264,10 @@ public void setSecurityOptions(boolean allowScripts, boolean allowExternalResour this.allowedProtocols = allowedProtocols; } - @Override - protected BridgeContext createBridgeContext(String svgVersion) { - if ("1.2".equals(svgVersion)) { - return new SVG12BridgeContext(userAgent, new OpenHtmlDocumentLoader(userAgent, userAgentCallback)); - } else { - return new BridgeContext(userAgent, new OpenHtmlDocumentLoader(userAgent, userAgentCallback)); - } - } + @Override + protected BridgeContext createBridgeContext(String svgVersion) { + return SVGImageExtension.newBridge(svgVersion, userAgent, userAgentCallback); + } @Override protected void transcode(Document svg, String uri, TranscoderOutput out) throws TranscoderException { diff --git a/openhtmltopdf-svg-support/src/main/java/com/openhtmltopdf/svgsupport/SVGImageExtension.java b/openhtmltopdf-svg-support/src/main/java/com/openhtmltopdf/svgsupport/SVGImageExtension.java new file mode 100644 index 000000000..95615fcb6 --- /dev/null +++ b/openhtmltopdf-svg-support/src/main/java/com/openhtmltopdf/svgsupport/SVGImageExtension.java @@ -0,0 +1,129 @@ +package com.openhtmltopdf.svgsupport; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.logging.Level; + +import org.apache.batik.bridge.BridgeContext; +import org.apache.batik.bridge.SVGBridgeExtension; +import org.apache.batik.bridge.SVGImageElementBridge; +import org.apache.batik.bridge.UserAgent; +import org.apache.batik.bridge.svg12.SVG12BridgeContext; +import org.apache.batik.bridge.svg12.SVG12BridgeExtension; +import org.apache.batik.util.ParsedURL; +import org.w3c.dom.Document; + +import com.openhtmltopdf.extend.UserAgentCallback; +import com.openhtmltopdf.util.LogMessageId; +import com.openhtmltopdf.util.XRLog; + +public class SVGImageExtension { + public static BridgeContext newBridge(String svgVersion, UserAgent userAgent, UserAgentCallback uac) { + if ("1.2".equals(svgVersion)) { + return new Bridge12Ctx(userAgent, uac); + } else { + return new BridgeCtx(userAgent, uac); + } + } + + public static String resolveUri(String uri, UserAgentCallback userAgentCallback) { + try { + // special handling of relative uri in case of file protocol, we receive something like "file:file.svg" + // ie. Batik adds file: to the start of relative uris for some reason. + URI parsedURI = new URI(uri); + + if ("file".equals(parsedURI.getScheme())) { + return new URI(parsedURI.getSchemeSpecificPart()).isAbsolute() ? + userAgentCallback.resolveURI(parsedURI.toString()) : + userAgentCallback.resolveURI(parsedURI.getSchemeSpecificPart()); + } else { + return userAgentCallback.resolveURI(uri); + } + } catch (URISyntaxException uriSyntaxException) { + XRLog.log(Level.WARNING, LogMessageId.LogMessageId1Param.EXCEPTION_URI_SYNTAX_WHILE_LOADING_EXTERNAL_SVG_RESOURCE, uri, uriSyntaxException); + return null; + } + } + + private static class BridgeCtx extends BridgeContext { + UserAgentCallback uac; + + public BridgeCtx(UserAgent userAgent, UserAgentCallback userAgentCallback) { + super(userAgent, new OpenHtmlDocumentLoader(userAgent, userAgentCallback)); + this.uac = userAgentCallback; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public List getBridgeExtensions(Document doc) { + List existing = super.getBridgeExtensions(doc); + existing.add(new Ext(uac)); + return existing; + } + } + + private static class Bridge12Ctx extends SVG12BridgeContext { + UserAgentCallback uac; + + public Bridge12Ctx(UserAgent userAgent, UserAgentCallback userAgentCallback) { + super(userAgent, new OpenHtmlDocumentLoader(userAgent, userAgentCallback)); + this.uac = userAgentCallback; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public List getBridgeExtensions(Document doc) { + List existing = super.getBridgeExtensions(doc); + existing.add(new Ext12(uac)); + return existing; + } + } + + private static class Ext extends SVGBridgeExtension { + final UserAgentCallback uac; + + public Ext(UserAgentCallback uac) { + this.uac = uac; + } + + @Override + public void registerTags(BridgeContext ctx) { + super.registerTags(ctx); + ctx.putBridge(new ImageBridge(uac)); + } + } + + private static class Ext12 extends SVG12BridgeExtension { + final UserAgentCallback uac; + + public Ext12(UserAgentCallback uac) { + this.uac = uac; + } + + @Override + public void registerTags(BridgeContext ctx) { + super.registerTags(ctx); + ctx.putBridge(new ImageBridge(uac)); + } + } + + private static class ImageBridge extends SVGImageElementBridge { + final UserAgentCallback uac; + + public ImageBridge(UserAgentCallback uac) { + this.uac = uac; + } + + @Override + public org.apache.batik.bridge.Bridge getInstance() { + return new ImageBridge(uac); + } + + @Override + protected org.apache.batik.gvt.GraphicsNode createImageGraphicsNode(BridgeContext ctx, org.w3c.dom.Element e, org.apache.batik.util.ParsedURL purl) { + String uri = resolveUri(purl.toString(), uac); + return super.createImageGraphicsNode(ctx, e, new ParsedURL(uri)); + }; + } +}