#191 Support for SVG files without XML declaration

This commit is contained in:
Harald Kuhr 2015-11-13 13:39:04 +01:00
parent 8346c4148e
commit 65f3bbbccd
3 changed files with 79 additions and 35 deletions

View File

@ -34,6 +34,7 @@ import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageReader; import javax.imageio.ImageReader;
import javax.imageio.spi.ServiceRegistry; import javax.imageio.spi.ServiceRegistry;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
@ -61,8 +62,7 @@ public final class SVGImageReaderSpi extends ImageReaderSpiBase {
private static boolean canDecode(final ImageInputStream pInput) throws IOException { private static boolean canDecode(final ImageInputStream pInput) throws IOException {
// NOTE: This test is quite quick as it does not involve any parsing, // NOTE: This test is quite quick as it does not involve any parsing,
// however it requires the doctype to be "svg", which may not be correct // however it may not recognize all kinds of SVG documents.
// in all cases...
try { try {
pInput.mark(); pInput.mark();
@ -74,51 +74,76 @@ public final class SVGImageReaderSpi extends ImageReaderSpiBase {
// Skip over leading WS // Skip over leading WS
} }
if (!((b == '<') && (pInput.read() == '?') && (pInput.read() == 'x') && (pInput.read() == 'm') // If it's not a tag, this can't be valid XML
&& (pInput.read() == 'l'))) { if (b != '<') {
return false; return false;
} }
// Okay, we have XML. But, is it really SVG? // Algorithm for detecting SVG:
boolean docTypeFound = false; // - Skip until begin tag '<' and read 4 bytes
while (!docTypeFound) { // - if next is "?" skip until "?>" and start over
while (pInput.read() != '<') { // - else if next is "!--" skip until "-->" and start over
// Skip over, until begin tag // - else if next is "!DOCTYPE " skip any whitespace
} // - compare next 3 bytes against "svg", return result
// - else
// - compare next 3 bytes against "svg", return result
if ((b = pInput.read()) != '!') { byte[] buffer = new byte[4];
if (b == 's' && pInput.read() == 'v' && pInput.read() == 'g' while (true) {
&& (Character.isWhitespace((char) (b = pInput.read())) || b == ':')) { pInput.readFully(buffer);
if (buffer[0] == '?') {
// This is the XML declaration or a processing instruction
while (!(pInput.read() == '?' && pInput.read() == '>')) {
// Skip until end of XML declaration or processing instruction
}
}
else if (buffer[0] == '!') {
if (buffer[1] == '-' && buffer[2] == '-') {
// This is a comment
while (!(pInput.read() == '-' && pInput.read() == '-' && pInput.read() == '>')) {
// Skip until end of comment
}
}
else if (buffer[1] == 'D' && buffer[2] == 'O' && buffer[3] == 'C'
&& pInput.read() == 'T' && pInput.read() == 'Y'
&& pInput.read() == 'P' && pInput.read() == 'E') {
// This is the DOCTYPE declaration
while (Character.isWhitespace((char) (b = pInput.read()))) {
// Skip over WS
}
if (b == 's' && pInput.read() == 'v' && pInput.read() == 'g') {
// It's SVG, identified by DOCTYPE
return true;
}
// DOCTYPE found, but not SVG
return false;
}
// Something else, we'll skip
}
else {
// This is a normal tag
if (buffer[0] == 's' && buffer[1] == 'v' && buffer[2] == 'g'
&& (Character.isWhitespace((char) buffer[3]) || buffer[3] == ':')) {
// It's SVG, identified by root tag
// TODO: Support svg with prefix + recognize namespace (http://www.w3.org/2000/svg)! // TODO: Support svg with prefix + recognize namespace (http://www.w3.org/2000/svg)!
return true; return true;
} }
// If this is not a comment, or the DOCTYPE declaration, the doc has no DOCTYPE and it can't be svg // If the tag is not "svg", this isn't SVG
return false; return false;
} }
// There might be comments before the doctype, unfortunately... while (pInput.read() != '<') {
// If next is "--", this is a comment // Skip over, until next begin tag
if ((b = pInput.read()) == '-' && pInput.read() == '-') {
while (!(pInput.read() == '-' && pInput.read() == '-' && pInput.read() == '>')) {
// Skip until end of comment
}
}
// If we are lucky, this is DOCTYPE declaration
if (b == 'D' && pInput.read() == 'O' && pInput.read() == 'C' && pInput.read() == 'T'
&& pInput.read() == 'Y' && pInput.read() == 'P' && pInput.read() == 'E') {
docTypeFound = true;
while (Character.isWhitespace((char) (b = pInput.read()))) {
// Skip over WS
}
if (b == 's' && pInput.read() == 'v' && pInput.read() == 'g') {
return true;
}
} }
} }
}
catch (EOFException ignore) {
// Possible for small files...
return false; return false;
} }
finally { finally {

View File

@ -59,7 +59,8 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
return Arrays.asList( return Arrays.asList(
new TestData(getClassLoaderResource("/svg/batikLogo.svg"), new Dimension(450, 500)), new TestData(getClassLoaderResource("/svg/batikLogo.svg"), new Dimension(450, 500)),
new TestData(getClassLoaderResource("/svg/red-square.svg"), new Dimension(100, 100)), new TestData(getClassLoaderResource("/svg/red-square.svg"), new Dimension(100, 100)),
new TestData(getClassLoaderResource("/svg/blue-square.svg"), new Dimension(100, 100)) new TestData(getClassLoaderResource("/svg/blue-square.svg"), new Dimension(100, 100)),
new TestData(getClassLoaderResource("/svg/Android_robot.svg"), new Dimension(400, 400))
); );
} }

View File

@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-147 -70 294 345">
<g fill="#a4c639">
<use stroke-width="14.4" xlink:href="#b" stroke="#FFF"/>
<use xlink:href="#a" transform="scale(-1,1)"/>
<g id="a" stroke="#FFF" stroke-width="7.2">
<rect rx="6.5" transform="rotate(29)" height="86" width="13" y="-86" x="14"/>
<rect id="c" rx="24" height="133" width="48" y="41" x="-143"/>
<use y="97" x="85" xlink:href="#c"/>
</g>
<g id="b">
<ellipse cy="41" rx="91" ry="84"/>
<rect rx="22" height="182" width="182" y="20" x="-91"/>
</g>
</g>
<g stroke="#FFF" stroke-width="7.2" fill="#FFF">
<path d="m-95 44.5h190"/><circle cx="-42" r="4"/><circle cx="42" r="4"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 706 B