public abstract class SVGComponent implements Iterable { protected final Map properties; protected final List children; protected final SVGComponent parent; protected SVGComponent (SVGComponent parent) { this.properties = new HashMap(); this.children = new ArrayList(); this.parent = parent; } protected SVGComponent (SVGComponent parent, HashMap properties, List children) { this.properties = properties; this.children = children; this.parent = parent; } public final T[] getProperties (String name, Class c) { c = c.arrayType(); if (this.properties.containsKey(name) && c.isInstance(this.properties.get(name))) return c.cast(this.properties.get(name)); return null; } public final Object[] getProperties (String name) { return this.getProperties(name, Object.class); } public final T[] setProperties (String name, T[] ts) { return this.properties.put(name, ts); } public final List getChildren () { return this.children; } public final Iterator iterator () { return this.children.iterator(); } public abstract void draw (Graphics g, int subpixelCount); } public class SVGComponents { private static final HashMap> map = new HashMap>(); @CallerSensitive void register (String s) { map.put(s, StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass()); } public Class getClass (String s) { return map.get(s); } } public class SVGImageReaderSPI extends ImageReaderSpi { private static SVGImageReaderSPI instance; public SVGImageReaderSPI getInstance () { return instance; } public boolean canDecodeInput (Object source) { DocumentBuilder db = DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder(); Document dom = null; if (source instanceof Document d) dom = d; else if (source instanceof String s) dom = db.parse(new InputSource(new CharArrayReader(s.toCharArray()))); else if (source instanceof Reader r) dom = db.parse(new InputSource(r)); else if (source instanceof InputStream s) dom = db.parse(s); if (dom == null) return false; return dom.getElementsByTagName("svg").getLength() != null; } public ImageReader createInstance (Object extension) { return new SVGImageReader(); } public String[] getImageWriterSpiNames () { return new String[] { "com.sforzando.svg.spi.SVGImageWriterSPI" }; } public Class[] getInputTypes () { return new Class[] { Document.class, String.class, Reader.class, InputStream.class }; } public boolean isOwnReader (ImageReader reader) { return ImageReader instanceof SVGImageReader; } } public class SVGImageReader extends ImageReader { private SVGImage result = null; public SVGImageReader () { super (SVGImageReaderSPI.getInstance()); } public String getFormatName () { return "svg"; } public int getHeight (int image) { if (image != 0) throw new IndexOutOfBoundsException("svg image reader queried image " + image + " when svg only supports one image"); Object lock = new Object(); synchonized (lock) { while (this.result == null) try { lock.wait(5); } catch (InterruptedException ie) {} } return this.result.getPreferredHeight(); } public int getNumImages (boolean bool) { return 1; } public int getWidth (int image) { if (image != 0) throw new IndexOutOfBoundsException("svg image reader queried image " + image + " when svg only supports one image"); Object lock = new Object(); synchonized (lock) { while (this.result == null) try { lock.wait(5); } catch (InterruptedException ie) {} } return this.result.getPreferredWidth(); } public BufferedImage read (int image, ImageReadParam param) { if (image != 0) throw new IndexOutOfBoundsException("svg image reader queried image " + image + " when svg only supports one image"); Object lock = new Object(); synchonized (lock) { while (this.result == null) try { lock.wait(5); } catch (InterruptedException ie) {} } BufferedImage img = new BufferedImage(this.result.getPreferredWidth(), this.result.getPreferredHeight(), BufferedImage.TYPE_INT_ARGB); return this.result.draw(img.createGraphics(), 4); } public void setInput (Object input, boolean sfo, boolean im) { super.setInput(input, sfo, im); Thread.startVirtualThread(this::_read); } private void _read () { Object source = this.getInput(); Document dom = null; if (source instanceof Document d) dom = d; else if (source instanceof String s) dom = db.parse(new InputSource(new CharArrayReader(s.toCharArray()))); else if (source instanceof Reader r) dom = db.parse(new InputSource(r)); else if (source instanceof InputStream s) dom = db.parse(s); if (dom == null) return; NodeList svgs = dom.getElementsByTagName("svg"); if (svgs.getLength() != 1 || !(svgs.item(0) instanceof Element)) return; SVGImage img = SVGImage.parse((Element) svgs.item(0)); if (this.getInput() == source) this.result = img; } }