Fix #1211 - SVG: support namespace prefix

This commit is contained in:
Vincent Privat
2025-10-31 02:49:59 +01:00
committed by Harald Kuhr
parent 8af219e669
commit 6c8b0cdc2f
3 changed files with 54 additions and 7 deletions
@@ -36,9 +36,11 @@ import com.twelvemonkeys.lang.SystemUtil;
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.ByteArrayOutputStream;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import java.util.function.Predicate;
import static com.twelvemonkeys.imageio.util.IIOUtil.deregisterProvider; import static com.twelvemonkeys.imageio.util.IIOUtil.deregisterProvider;
@@ -134,10 +136,40 @@ public final class SVGImageReaderSpi extends ImageReaderSpiBase {
if (buffer[0] == 's' && buffer[1] == 'v' && buffer[2] == 'g' if (buffer[0] == 's' && buffer[1] == 'v' && buffer[2] == 'g'
&& (Character.isWhitespace((char) buffer[3]) || buffer[3] == ':')) { && (Character.isWhitespace((char) buffer[3]) || buffer[3] == ':')) {
// It's SVG, identified by root tag // It's SVG, identified by root tag
// TODO: Support svg with prefix + recognize namespace (http://www.w3.org/2000/svg)!
return true; return true;
} }
// Read the full tag name (may contain a prefix of any length)
ByteArrayOutputStream nameBuf = new ByteArrayOutputStream();
// We already have 4 bytes in 'buffer' (from input.readFully(buffer))
int consumedFromBuffer = 0;
for (; consumedFromBuffer < buffer.length; consumedFromBuffer++) {
byte bb = buffer[consumedFromBuffer];
if (bb == '>' || Character.isWhitespace((char) bb) || bb == '/') {
break;
}
nameBuf.write(bb);
}
// If tag name not terminated yet, keep reading bytes (within limit)
final int MAX_TAG_NAME = 256;
final boolean incompleteTagName = consumedFromBuffer == buffer.length;
readBuffer(input, nameBuf, x -> incompleteTagName && x.size() < MAX_TAG_NAME,
bb -> bb == '>' || Character.isWhitespace(bb) || bb == '/');
if (nameBuf.toString("US-ASCII").toLowerCase(Locale.ENGLISH).endsWith(":svg")) {
// Scan the rest of the tag attributes until '>' to find the SVG namespace URI
ByteArrayOutputStream attrBuf = new ByteArrayOutputStream();
final int MAX_ATTR_SCAN = 1024; // safe upper bound to keep it fast
readBuffer(input, attrBuf, x -> x.size() < MAX_ATTR_SCAN, bb -> bb == '>');
// If the tag contains the SVG namespace, it's SVG.
if (attrBuf.toString("US-ASCII").contains("http://www.w3.org/2000/svg")) {
return true;
}
}
// If the tag is not "svg", this isn't SVG // If the tag is not "svg", this isn't SVG
return false; return false;
} }
@@ -157,6 +189,21 @@ public final class SVGImageReaderSpi extends ImageReaderSpiBase {
} }
} }
private static void readBuffer(final ImageInputStream input, final ByteArrayOutputStream buffer,
final Predicate<ByteArrayOutputStream> loopCondition, Predicate<Byte> breakCondition) throws IOException {
while (loopCondition.test(buffer)) {
int r = input.read();
if (r == -1) {
throw new EOFException();
}
byte bb = (byte) r;
if (breakCondition.test(bb)) {
break;
}
buffer.write(bb);
}
}
public ImageReader createReaderInstance(final Object extension) throws IOException { public ImageReader createReaderInstance(final Object extension) throws IOException {
return new SVGImageReader(this); return new SVGImageReader(this);
} }
@@ -56,7 +56,7 @@ public class SVGImageReaderSpiTest {
"/svg/Android_robot.svg", // Minimal, no xml dec, no namespace "/svg/Android_robot.svg", // Minimal, no xml dec, no namespace
"/svg/batikLogo.svg", // xml dec, comments, namespace "/svg/batikLogo.svg", // xml dec, comments, namespace
"/svg/blue-square.svg", // xml dec, namespace "/svg/blue-square.svg", // xml dec, namespace
"/svg/red-square.svg", "/svg/red-square.svg", // prefixed namespace
}; };
private static final String[] INVALID_INPUTS = { private static final String[] INVALID_INPUTS = {
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" id="red-square" version="1.1"> <ns0:svg xmlns:ns0="http://www.w3.org/2000/svg" width="100" height="100" id="red-square" version="1.1">
<g id="layer1"> <ns0:g id="layer1">
<rect id="rect2985" width="100" height="100" x="0" y="0" <ns0:rect id="rect2985" width="100" height="100" x="0" y="0"
style="color:#000000;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> style="color:#000000;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g> </ns0:g>
</svg> </ns0:svg>

Before

Width:  |  Height:  |  Size: 441 B

After

Width:  |  Height:  |  Size: 465 B