package net.sourceforge.plantuml.emoji; import java.awt.Color; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.sourceforge.plantuml.awt.geom.XDimension2D; import net.sourceforge.plantuml.graphic.AbstractTextBlock; import net.sourceforge.plantuml.graphic.FontConfiguration; import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.openiconic.SvgPath; import net.sourceforge.plantuml.sprite.Sprite; import net.sourceforge.plantuml.ugraphic.UEllipse; import net.sourceforge.plantuml.ugraphic.UFont; import net.sourceforge.plantuml.ugraphic.UGraphic; import net.sourceforge.plantuml.ugraphic.UImageSvg; import net.sourceforge.plantuml.ugraphic.UStroke; import net.sourceforge.plantuml.ugraphic.UText; import net.sourceforge.plantuml.ugraphic.UTranslate; import net.sourceforge.plantuml.ugraphic.color.ColorMapper; import net.sourceforge.plantuml.ugraphic.color.ColorUtils; import net.sourceforge.plantuml.ugraphic.color.HColor; import net.sourceforge.plantuml.ugraphic.color.HColorSet; import net.sourceforge.plantuml.ugraphic.color.HColorSimple; import net.sourceforge.plantuml.ugraphic.color.HColors; // Emojji from https://twemoji.twitter.com/ // Shorcut from https://api.github.com/emojis public class SvgNanoParser implements Sprite { private final List data = new ArrayList<>(); private int minGray = 999; private int maxGray = -1; private final String svgStart; private final boolean keepColors; private String extractData(String name, String s) { final Pattern p = Pattern.compile(name + "=\"([^\"]+)\""); final Matcher m = p.matcher(s); if (m.find()) return m.group(1); return null; } public SvgNanoParser(String svg, boolean keepColors) { this(Collections.singletonList(svg), keepColors); } public SvgNanoParser(List svg, boolean keepColors) { this.svgStart = svg.get(0); this.keepColors = keepColors; for (String singleLine : svg) { final Pattern p = Pattern .compile("(\\)|(\\<(svg|path|g|circle|ellipse)[^<>]*\\>)|(\\]*\\>)"); final Matcher m = p.matcher(singleLine); while (m.find()) { final String s = m.group(0); if (s.startsWith("") || s.startsWith("") || s.startsWith("")) { ugs = stack.remove(0); } else if (s.startsWith("")) { stack.add(0, ugs); } else if (s.startsWith("]*\\>(.*?)\\"); final Matcher m = p.matcher(s); if (m.find()) { final String text = m.group(1); HColor color = HColorSet.instance().getColorOrWhite(fill); final FontConfiguration fc = FontConfiguration.create(UFont.sansSerif(fontSize), color, color, null); final UText utext = new UText(text, fc); UGraphic ug = ugs.getUg(); ug = ug.apply(new UTranslate(x, y)); ug.draw(utext); } } private void drawPath(UGraphicWithScale ugs, String s, HColor colorForMonochrome) { s = s.replace("id=\"", "ID=\""); ugs = applyFill(ugs, s, colorForMonochrome); ugs = applyTransform(ugs, s); final int x1 = s.indexOf("d=\""); final int x2 = s.indexOf('"', x1 + 3); final String tmp = s.substring(x1 + 3, x2); final SvgPath svgPath = new SvgPath(tmp); svgPath.drawMe(ugs.getUg(), ugs.getAffineTransform()); } private UGraphicWithScale applyTransform(UGraphicWithScale ugs, String s) { final String transform = extractData("transform", s); if (transform == null) return ugs; if (transform.contains("rotate(")) return applyRotate(ugs, transform); if (transform.contains("matrix(")) return applyMatrix(ugs, transform); final double[] scale = getScale(transform); final UTranslate translate = getTranslate(transform); ugs = ugs.applyTranslate(translate.getDx(), translate.getDy()); return ugs.applyScale(scale[0], scale[1]); } private UGraphicWithScale applyMatrix(UGraphicWithScale ugs, final String transform) { final Pattern p3 = Pattern.compile( "matrix\\(([-.0-9]+)[ ,]+([-.0-9]+)[ ,]+([-.0-9]+)[ ,]+([-.0-9]+)[ ,]+([-.0-9]+)[ ,]+([-.0-9]+)\\)"); final Matcher m3 = p3.matcher(transform); if (m3.find()) { final double v1 = Double.parseDouble(m3.group(1)); final double v2 = Double.parseDouble(m3.group(2)); final double v3 = Double.parseDouble(m3.group(3)); final double v4 = Double.parseDouble(m3.group(4)); final double v5 = Double.parseDouble(m3.group(5)); final double v6 = Double.parseDouble(m3.group(6)); ugs = ugs.applyMatrix(v1, v2, v3, v4, v5, v6); } else System.err.println("WARNING: " + transform); return ugs; } private UGraphicWithScale applyRotate(UGraphicWithScale ugs, final String transform) { final Pattern p3 = Pattern.compile("rotate\\(([-.0-9]+)[ ,]+([-.0-9]+)[ ,]+([-.0-9]+)\\)"); final Matcher m3 = p3.matcher(transform); if (m3.find()) { final double angle = Double.parseDouble(m3.group(1)); final double x = Double.parseDouble(m3.group(2)); final double y = Double.parseDouble(m3.group(3)); ugs = ugs.applyRotate(angle, x, y); } else System.err.println("WARNING: " + transform); return ugs; } private UTranslate getTranslate(String transform) { double x = 0; double y = 0; final Pattern p3 = Pattern.compile("translate\\(([-.0-9]+)[ ,]+([-.0-9]+)\\)"); final Matcher m3 = p3.matcher(transform); if (m3.find()) { x = Double.parseDouble(m3.group(1)); y = Double.parseDouble(m3.group(2)); } else { final Pattern p4 = Pattern.compile("translate\\(([-.0-9]+)\\)"); final Matcher m4 = p4.matcher(transform); if (m4.find()) { x = Double.parseDouble(m4.group(1)); y = Double.parseDouble(m4.group(1)); } } return new UTranslate(x, y); } private double[] getScale(String transform) { final double scale[] = new double[] { 1, 1 }; final Pattern p1 = Pattern.compile("scale\\(([-.0-9]+)\\)"); final Matcher m1 = p1.matcher(transform); if (m1.find()) { scale[0] = Double.parseDouble(m1.group(1)); scale[1] = scale[0]; } else { final Pattern p2 = Pattern.compile("scale\\(([-.0-9]+)[ ,]+([-.0-9]+)\\)"); final Matcher m2 = p2.matcher(transform); if (m2.find()) { scale[0] = Double.parseDouble(m2.group(1)); scale[1] = Double.parseDouble(m2.group(2)); } } return scale; } @Override public TextBlock asTextBlock(final HColor color, final double scale) { final UImageSvg data = new UImageSvg(svgStart, scale); final double width = data.getWidth(); final double height = data.getHeight(); return new AbstractTextBlock() { public void drawU(UGraphic ug) { SvgNanoParser.this.drawU(ug, scale, keepColors ? null : color); } public XDimension2D calculateDimension(StringBounder stringBounder) { return new XDimension2D(width, height); } }; } }