#442 SVG BaseURI issue fix

This commit is contained in:
Harald Kuhr 2018-10-11 21:38:10 +02:00
parent 62538c3db9
commit cc5af0134c
4 changed files with 311 additions and 35 deletions

View File

@ -110,24 +110,25 @@ public class SVGImageReader extends ImageReaderBase {
public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException { public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
checkBounds(pIndex); checkBounds(pIndex);
String baseURI = null;
if (pParam instanceof SVGReadParam) { if (pParam instanceof SVGReadParam) {
SVGReadParam svgParam = (SVGReadParam) pParam; SVGReadParam svgParam = (SVGReadParam) pParam;
// Set IIOParams as hints
// Get the base URI
// This must be done before converting the params to hints
String baseURI = svgParam.getBaseURI();
rasterizer.transcoderInput.setURI(baseURI);
// Set ImageReadParams as hints
// Note: The cast to Map invokes a different method that preserves // Note: The cast to Map invokes a different method that preserves
// unset defaults, DO NOT REMOVE! // unset defaults, DO NOT REMOVE!
rasterizer.setTranscodingHints((Map) paramsToHints(svgParam)); rasterizer.setTranscodingHints((Map) paramsToHints(svgParam));
// Get the base URI (not a hint)
baseURI = svgParam.getBaseURI();
} }
Dimension size; Dimension size = null;
if (pParam != null && (size = pParam.getSourceRenderSize()) != null) { if (pParam != null) {
// Use size... size = pParam.getSourceRenderSize();
} }
else { if (size == null) {
size = new Dimension(getWidth(pIndex), getHeight(pIndex)); size = new Dimension(getWidth(pIndex), getHeight(pIndex));
} }
@ -137,7 +138,6 @@ public class SVGImageReader extends ImageReaderBase {
try { try {
processImageStarted(pIndex); processImageStarted(pIndex);
rasterizer.transcoderInput.setURI(baseURI);
BufferedImage image = rasterizer.getImage(); BufferedImage image = rasterizer.getImage();
Graphics2D g = destination.createGraphics(); Graphics2D g = destination.createGraphics();
@ -155,10 +155,16 @@ public class SVGImageReader extends ImageReaderBase {
return destination; return destination;
} }
catch (TranscoderException e) { catch (TranscoderException e) {
throw new IIOException(e.getMessage(), e); Throwable cause = unwrapException(e);
throw new IIOException(cause.getMessage(), cause);
} }
} }
private static Throwable unwrapException(TranscoderException ex) {
// The TranscoderException is generally useless...
return ex.getException() != null ? ex.getException() : ex;
}
private TranscodingHints paramsToHints(SVGReadParam pParam) throws IOException { private TranscodingHints paramsToHints(SVGReadParam pParam) throws IOException {
TranscodingHints hints = new TranscodingHints(); TranscodingHints hints = new TranscodingHints();
// Note: We must allow generic ImageReadParams, so converting to // Note: We must allow generic ImageReadParams, so converting to
@ -174,8 +180,8 @@ public class SVGImageReader extends ImageReaderBase {
} }
if (size != null) { if (size != null) {
hints.put(ImageTranscoder.KEY_WIDTH, new Float(size.getWidth())); hints.put(ImageTranscoder.KEY_WIDTH, (float) size.getWidth());
hints.put(ImageTranscoder.KEY_HEIGHT, new Float(size.getHeight())); hints.put(ImageTranscoder.KEY_HEIGHT, (float) size.getHeight());
} }
// Set area of interest // Set area of interest
@ -185,16 +191,16 @@ public class SVGImageReader extends ImageReaderBase {
// Avoid that the batik transcoder scales the AOI up to original image size // Avoid that the batik transcoder scales the AOI up to original image size
if (size == null) { if (size == null) {
hints.put(ImageTranscoder.KEY_WIDTH, new Float(region.getWidth())); hints.put(ImageTranscoder.KEY_WIDTH, (float) region.getWidth());
hints.put(ImageTranscoder.KEY_HEIGHT, new Float(region.getHeight())); hints.put(ImageTranscoder.KEY_HEIGHT, (float) region.getHeight());
} }
else { else {
// Need to resize here... // Need to resize here...
double xScale = size.getWidth() / origSize.getWidth(); double xScale = size.getWidth() / origSize.getWidth();
double yScale = size.getHeight() / origSize.getHeight(); double yScale = size.getHeight() / origSize.getHeight();
hints.put(ImageTranscoder.KEY_WIDTH, new Float(region.getWidth() * xScale)); hints.put(ImageTranscoder.KEY_WIDTH, (float) (region.getWidth() * xScale));
hints.put(ImageTranscoder.KEY_HEIGHT, new Float(region.getHeight() * yScale)); hints.put(ImageTranscoder.KEY_HEIGHT, (float) (region.getHeight() * yScale));
} }
} }
else if (size != null) { else if (size != null) {
@ -219,7 +225,7 @@ public class SVGImageReader extends ImageReaderBase {
return null; return null;
} }
public ImageReadParam getDefaultReadParam() { public SVGReadParam getDefaultReadParam() {
return new SVGReadParam(); return new SVGReadParam();
} }
@ -255,7 +261,7 @@ public class SVGImageReader extends ImageReaderBase {
*/ */
private class Rasterizer extends SVGAbstractTranscoder /*ImageTranscoder*/ { private class Rasterizer extends SVGAbstractTranscoder /*ImageTranscoder*/ {
BufferedImage image = null; private BufferedImage image;
private TranscoderInput transcoderInput; private TranscoderInput transcoderInput;
private float defaultWidth; private float defaultWidth;
private float defaultHeight; private float defaultHeight;
@ -266,17 +272,19 @@ public class SVGImageReader extends ImageReaderBase {
private TranscoderException exception; private TranscoderException exception;
private BridgeContext context; private BridgeContext context;
public BufferedImage createImage(final int width, final int height) { private BufferedImage createImage(final int width, final int height) {
return ImageUtil.createTransparent(width, height);//, BufferedImage.TYPE_INT_ARGB); return ImageUtil.createTransparent(width, height); // BufferedImage.TYPE_INT_ARGB
} }
// This is cheating... We don't fully transcode after all // This is cheating... We don't fully transcode after all
protected void transcode(Document document, final String uri, final TranscoderOutput output) throws TranscoderException { protected void transcode(Document document, final String uri, final TranscoderOutput output) throws TranscoderException {
// Sets up root, curTxf & curAoi // Sets up root, curTxf & curAoi
// ---- // ----
if ((document != null) && !(document.getImplementation() instanceof SVGDOMImplementation)) { if (document != null) {
DOMImplementation impl = (DOMImplementation) hints.get(KEY_DOM_IMPLEMENTATION); if (!(document.getImplementation() instanceof SVGDOMImplementation)) {
document = DOMUtilities.deepCloneDocument(document, impl); DOMImplementation impl = (DOMImplementation) hints.get(KEY_DOM_IMPLEMENTATION);
document = DOMUtilities.deepCloneDocument(document, impl);
}
if (uri != null) { if (uri != null) {
try { try {
@ -346,8 +354,8 @@ public class SVGImageReader extends ImageReaderBase {
processReadAborted(); processReadAborted();
return null; return null;
} }
processImageProgress(10f);
processImageProgress(10f);
// Hacky workaround below... // Hacky workaround below...
if (gvtRoot == null) { if (gvtRoot == null) {
@ -369,6 +377,7 @@ public class SVGImageReader extends ImageReaderBase {
} }
ctx = context; ctx = context;
// /Hacky // /Hacky
if (abortRequested()) { if (abortRequested()) {
processReadAborted(); processReadAborted();
return null; return null;
@ -489,11 +498,9 @@ public class SVGImageReader extends ImageReaderBase {
// now we are sure that the aoi is the image size // now we are sure that the aoi is the image size
Shape raoi = new Rectangle2D.Float(0, 0, width, height); Shape raoi = new Rectangle2D.Float(0, 0, width, height);
// Warning: the renderer's AOI must be in user space // Warning: the renderer's AOI must be in user space
renderer.repaint(curTxf.createInverse(). renderer.repaint(curTxf.createInverse().createTransformedShape(raoi));
createTransformedShape(raoi));
// NOTE: repaint above cause nullpointer exception with fonts..??? // NOTE: repaint above cause nullpointer exception with fonts..???
BufferedImage rend = renderer.getOffScreen(); BufferedImage rend = renderer.getOffScreen();
renderer = null; // We're done with it... renderer = null; // We're done with it...
@ -558,18 +565,40 @@ public class SVGImageReader extends ImageReaderBase {
return image; return image;
} }
protected int getDefaultWidth() throws TranscoderException { int getDefaultWidth() throws TranscoderException {
init(); init();
return (int) (defaultWidth + 0.5); return (int) Math.ceil(defaultWidth);
} }
protected int getDefaultHeight() throws TranscoderException { int getDefaultHeight() throws TranscoderException {
init(); init();
return (int) (defaultHeight + 0.5); return (int) Math.ceil(defaultHeight);
} }
public void setInput(final TranscoderInput pInput) { void setInput(final TranscoderInput pInput) {
transcoderInput = pInput; transcoderInput = pInput;
} }
@Override
protected UserAgent createUserAgent() {
return new SVGImageReaderUserAgent();
}
private class SVGImageReaderUserAgent extends SVGAbstractTranscoderUserAgent {
@Override
public void displayError(Exception e) {
displayError(e.getMessage());
}
@Override
public void displayError(String message) {
displayMessage(message);
}
@Override
public void displayMessage(String message) {
processWarningOccurred(message.replaceAll("[\\r\\n]+", " "));
}
}
} }
} }

View File

@ -34,18 +34,26 @@ import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import javax.imageio.IIOException;
import javax.imageio.ImageReadParam; import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader; import javax.imageio.ImageReader;
import javax.imageio.event.IIOReadWarningListener;
import javax.imageio.spi.ImageReaderSpi; import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.ImagingOpException; import java.awt.image.ImagingOpException;
import java.io.IOException; import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
/** /**
* SVGImageReaderTest * SVGImageReaderTest
@ -146,4 +154,97 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
BufferedImage imageBlue = reader.read(0, param); BufferedImage imageBlue = reader.read(0, param);
assertEquals(0x0000FF, imageBlue.getRGB(50, 50) & 0xFFFFFF); assertEquals(0x0000FF, imageBlue.getRGB(50, 50) & 0xFFFFFF);
} }
@Test
public void testEmbeddedBaseURI() throws URISyntaxException, IOException {
URL resource = getClassLoaderResource("/svg/barChart.svg");
SVGImageReader reader = createReader();
TestData data = new TestData(resource, (Dimension) null);
try (ImageInputStream stream = data.getInputStream()) {
reader.setInput(stream);
IIOReadWarningListener listener = mock(IIOReadWarningListener.class);
reader.addIIOReadWarningListener(listener);
SVGReadParam param = reader.getDefaultReadParam();
param.setBaseURI(resource.toURI().toASCIIString());
BufferedImage image = reader.read(0, param);
assertNotNull(image);
assertEquals(450, image.getWidth());
assertEquals(500, image.getHeight());
// CSS and embedded resources all go!
verifyZeroInteractions(listener);
}
finally {
reader.dispose();
}
}
@Test
public void testEmbeddedBeforeBaseURI() throws URISyntaxException, IOException {
// Asking for metadata, width, height etc, before attempting to read using a param,
// will cause the document to be parsed without a base URI.
// This will work, but may not use the CSS...
URL resource = getClassLoaderResource("/svg/barChart.svg");
SVGImageReader reader = createReader();
TestData data = new TestData(resource, (Dimension) null);
try (ImageInputStream stream = data.getInputStream()) {
reader.setInput(stream);
IIOReadWarningListener listener = mock(IIOReadWarningListener.class);
reader.addIIOReadWarningListener(listener);
assertEquals(450, reader.getWidth(0));
assertEquals(500, reader.getHeight(0));
// Expect the warning about the missing CSS
verify(listener, atMost(1)).warningOccurred(any(ImageReader.class), anyString());
reset(listener);
SVGReadParam param = reader.getDefaultReadParam();
param.setBaseURI(resource.toURI().toASCIIString());
BufferedImage image = reader.read(0, param);
assertNotNull(image);
assertEquals(450, image.getWidth());
assertEquals(500, image.getHeight());
// No more warnings now that the base URI is set
verifyZeroInteractions(listener);
}
finally {
reader.dispose();
}
}
@Test
public void testEmbeddedNoBaseURI() throws IOException {
// With no base URI, we will throw an exception, about the missing embedded resource
URL resource = getClassLoaderResource("/svg/barChart.svg");
SVGImageReader reader = createReader();
TestData data = new TestData(resource, (Dimension) null);
try (ImageInputStream stream = data.getInputStream()) {
reader.setInput(stream);
BufferedImage image = reader.read(0);
assertNotNull(image);
assertEquals(450, image.getWidth());
assertEquals(500, image.getHeight());
}
catch (IIOException allowed) {
assertTrue(allowed.getMessage().contains("batikLogo.svg")); // The embedded resource we don't find
}
finally {
reader.dispose();
}
}
} }

View File

@ -0,0 +1,117 @@
<?xml version="1.0" standalone="no"?>
<?xml-stylesheet type="text/css" href="style/test.css" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- ========================================================================= -->
<!-- Illustrates how SVG can be used for high quality graphs. -->
<!-- -->
<!-- @author vincent.hardy@eng.sun.com -->
<!-- @author neeme.praks@one.lv -->
<!-- @version $Id: barChart.svg 1733420 2016-03-03 07:41:59Z gadams $ -->
<!-- ========================================================================= -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="body" width="450" height="500" xml:space="preserve" viewBox="0 0 450 500">
<title>Bar Chart</title>
<g id="barChart" transform="translate(40, 100)" fill-rule="evenodd" clip-rule="evenodd" stroke="none" class="legend"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" style="text-anchor:start">
<g id="GridAndLegend" style="stroke:none;">
<g stroke="black">
<!-- "floor" and "wall" -->
<path fill="lightgray" stroke="darkgray" d="M 27,240 l 15,-15 v -224 l -15,15" />
<path fill="lightgray" stroke="darkgray" d="M 41,225 v -224 h 316 v 224" />
<path fill="darkgray" stroke="none" d="M 27,240 l 15,-15 h 316 l -15,15" />
<!-- axis lines -->
<path d="M 27,240 h 316"/>
<path d="M 27,240 v -224"/>
<!-- value axis major gridlines -->
<g style="fill:none;">
<path d="M 27,202 l 15,-15 h 316" />
<path d="M 27,165 l 15,-15 h 316" />
<path d="M 27,127 l 15,-15 h 316" />
<path d="M 27, 90 l 15,-15 h 316" />
<path d="M 27, 53 l 15,-15 h 316" />
</g>
<!-- category axis major ticks -->
<path d="M 27,245 v -5"/>
<path d="M 106,245 v -5"/>
<path d="M 185,245 v -5"/>
<path d="M 264,245 v -5"/>
<!-- value axis minor ticks -->
<path d="M 22,240 h 5"/>
<path d="M 22,202 h 5"/>
<path d="M 22,165 h 5"/>
<path d="M 22,127 h 5"/>
<path d="M 22, 90 h 5"/>
<path d="M 22, 53 h 5"/>
<path d="M 22, 15 h 5"/>
</g>
<text transform="matrix(1 0 0 1 54 256)">Shoe</text>
<text transform="matrix(1 0 0 1 142 256)">Car</text>
<text transform="matrix(1 0 0 1 211 256)">Travel</text>
<text transform="matrix(1 0 0 1 285 256)">Computer</text>
<text transform="matrix(1 0 0 1 13 247)"><tspan x="0" y="0">0</tspan></text>
<text transform="matrix(1 0 0 1 6 209)"><tspan x="0" y="0">10</tspan></text>
<text transform="matrix(1 0 0 1 6 171)"><tspan x="0" y="0">20</tspan></text>
<text transform="matrix(1 0 0 1 6 134)"><tspan x="0" y="0">30</tspan></text>
<text transform="matrix(1 0 0 1 6 96)"><tspan x="0" y="0">40</tspan></text>
<text transform="matrix(1 0 0 1 6 60)"><tspan x="0" y="0">50</tspan></text>
<text transform="matrix(1 0 0 1 6 22)"><tspan x="0" y="0">60</tspan></text>
</g>
<g id="ShoeBar">
<path style="fill:#8686E0;" d="M 86,240 v -37 l 15 -15 v 37 l -15,15 z"/>
<path style="fill:#5B5B97;" d="M 86,203 h -39 l 15 -15 h 39 l -15,15 z"/>
<path style="fill:#7575C3;" d="M 47,203 v 37 h 39 v -37 H 47 z"/>
</g>
<g id="CarBar">
<path style="fill:#8686E0;" d="M 165,240 v -74 l 15 -15 v 74 l -15,15 z"/>
<path style="fill:#5B5B97;" d="M 165,166 h -39 l 15 -15 h 39 l -15,15 z"/>
<path style="fill:#7575C3;" d="M 126,166 v 74 h 39 v -74 h -39 z"/>
</g>
<g id="TravelBar">
<path style="fill:#8686E0;" d="M 244,240 v -37 l 15 -15 v 37 l -15,15 z"/>
<path style="fill:#5B5B97;" d="M 244,203 h -39 l 15 -15 h 39 l -15,15 z"/>
<path style="fill:#7575C3;" d="M 205,203 v 37 h 39 v -37 h -39 z"/>
</g>
<g id="ComputerBar">
<path style="fill:#8686E0;" d="M 323,240 v -224 l 15 -15 v 224 l -15,15 z"/>
<path style="fill:#5B5B97;" d="M 323, 16 h -39 l 15 -15 h 39 l -15,15 z"/>
<path style="fill:#7575C3;" d="M 284, 16 v 224 h 39 v -224 h -39 z"/>
</g>
</g>
<!-- ============================================================= -->
<!-- Batik sample mark -->
<!-- ============================================================= -->
<use xlink:href="batikLogo.svg#Batik_Tag_Box" />
</svg>

View File

@ -0,0 +1,29 @@
/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.title {
font-family: Arial, Helvetica;
font-size: 16pt;
text-anchor: middle;
}
.legend {
font-family: Arial, Helvetica;
font-size: 10pt;
text-anchor: middle;
}