diff --git a/uk/ac/babraham/FastQC/Utilities/ImageSaver/SVGGenerator.java b/uk/ac/babraham/FastQC/Utilities/ImageSaver/SVGGenerator.java index 4f6cebf..eec7360 100644 --- a/uk/ac/babraham/FastQC/Utilities/ImageSaver/SVGGenerator.java +++ b/uk/ac/babraham/FastQC/Utilities/ImageSaver/SVGGenerator.java @@ -134,6 +134,7 @@ private SVGGenerator (StringBuffer sb, Component c) { sb.append("\" height=\""); sb.append(c.getHeight()); sb.append("\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n"); + sb.append("\n"); c.paint(new SVGGraphics()); @@ -237,7 +238,7 @@ public void drawLine(int x1, int y1, int x2, int y2) { sb.append(y2); sb.append("\" stroke=\"rgb("); appendColor(); - sb.append(")\" stroke-width=\"1\"/>\n"); + sb.append(")\" stroke-width=\"2\"/>\n"); } /* (non-Javadoc) @@ -273,9 +274,9 @@ public void drawOval(int x, int y, int width, int height) { sb.append(width); sb.append("\" ry=\""); sb.append(height); - sb.append("\" style=\"fill:none;stroke:rgb("); + sb.append("\" fill=\"none\" stroke=\"rgb("); appendColor(); - sb.append(");stroke-width:1\"/>\n"); + sb.append(")\"/>\n"); } /* (non-Javadoc) @@ -311,9 +312,9 @@ public void fillOval(int x, int y, int width, int height) { sb.append(width); sb.append("\" ry=\""); sb.append(height); - sb.append("\" style=\"fill:rgb("); + sb.append("\" fill=\"rgb("); appendColor(); - sb.append(");stroke:none\"/>\n"); + sb.append(")\"/>\n"); } /* (non-Javadoc) @@ -344,7 +345,7 @@ public void drawString(String string, int x, int y) { sb.append(y); sb.append("\" fill=\"rgb("); appendColor(); - sb.append(")\" font-family=\"Arial\" font-size=\""); + sb.append(")\" font-size=\""); sb.append(font.getSize()); if (font.isBold()) { sb.append("\" font-weight=\"bold"); @@ -404,8 +405,7 @@ public void drawRoundRect(int x, int y, int width, int height, int arcWidth,int sb.append(arcWidth); sb.append("\" ry=\""); sb.append(arcHeight); - sb.append("\" style=\"fill:none"); - sb.append(";stroke-width:1;stroke:rgb("); + sb.append("\" fill=\"none\" stroke=\"rgb("); appendColor(); @@ -450,11 +450,11 @@ public void fillRoundRect(int x, int y, int width, int height, int arcWidth,int sb.append("\" ry=\""); sb.append(arcHeight); } - sb.append("\" style=\"fill:rgb("); + sb.append("\" fill=\"rgb("); appendColor(); - sb.append(");stroke:none\"/>\n"); + sb.append(")\"/>\n"); } /* (non-Javadoc) @@ -607,11 +607,11 @@ public void drawPolygon(int[] xPoints, int[] yPoints, int pointsToUse) { sb.append(correctedYPoints[i]); } - sb.append("\" style=\"stroke-width:1;stroke:rgb("); + sb.append("\" fill=\"none\" stroke=\"rgb("); appendColor(); - sb.append(");fill:none\"/>\n"); + sb.append(")\"/>\n"); } /* (non-Javadoc) @@ -661,11 +661,11 @@ public void fillPolygon(int[] xPoints, int[] yPoints, int pointsToUse) { sb.append(correctedYPoints[i]); } - sb.append("\" style=\"fill:rgb("); + sb.append("\" fill=\"rgb("); appendColor(); - sb.append(");stroke:none\"/>\n"); + sb.append(")\"/>\n"); } diff --git a/uk/ac/babraham/FastQC/Utilities/ImageSaver/SVGImageSaver.java b/uk/ac/babraham/FastQC/Utilities/ImageSaver/SVGImageSaver.java index 5e4443f..7dd244f 100644 --- a/uk/ac/babraham/FastQC/Utilities/ImageSaver/SVGImageSaver.java +++ b/uk/ac/babraham/FastQC/Utilities/ImageSaver/SVGImageSaver.java @@ -22,6 +22,9 @@ import java.awt.Component; import java.io.OutputStream; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** @@ -31,14 +34,135 @@ */ public class SVGImageSaver { + private static final Pattern LINE_PATTERN = Pattern.compile( + "" + ); + + private static final Pattern RECT_PATTERN = Pattern.compile( + "" + ); + public static String saveImage (Component c, OutputStream os) { PrintWriter pr = new PrintWriter(os); String svgData = SVGGenerator.writeSVG(c); + svgData = optimizeSvg(svgData); pr.write(svgData); pr.flush(); return(svgData); } - - - + + private static String optimizeSvg(String svg) { + svg = mergeLinesToPolylines(svg); + svg = mergeRects(svg); + return svg; + } + + /** Merge consecutive same-colour line elements into polyline elements. */ + private static String mergeLinesToPolylines(String svg) { + String[] lines = svg.split("\n"); + StringBuilder result = new StringBuilder(svg.length()); + ArrayList group = new ArrayList<>(); + String groupStroke = null; + String groupWidth = null; + + for (String line : lines) { + Matcher m = LINE_PATTERN.matcher(line.trim()); + if (m.matches()) { + String stroke = m.group(5); + String width = m.group(6); + if ("rgb(0,0,0)".equals(stroke) || "rgb(180,180,180)".equals(stroke)) { + flushLineGroup(result, group, groupStroke, groupWidth); + group.clear(); + groupStroke = null; + result.append(line).append("\n"); + } else if (stroke.equals(groupStroke) && width.equals(groupWidth)) { + group.add(new String[]{m.group(1), m.group(2), m.group(3), m.group(4)}); + } else { + flushLineGroup(result, group, groupStroke, groupWidth); + group.clear(); + groupStroke = stroke; + groupWidth = width; + group.add(new String[]{m.group(1), m.group(2), m.group(3), m.group(4)}); + } + } else { + flushLineGroup(result, group, groupStroke, groupWidth); + group.clear(); + groupStroke = null; + result.append(line).append("\n"); + } + } + flushLineGroup(result, group, groupStroke, groupWidth); + return result.toString(); + } + + private static void flushLineGroup(StringBuilder sb, ArrayList group, String stroke, String width) { + if (group.size() <= 2) { + for (String[] seg : group) { + sb.append("\n"); + } + return; + } + StringBuilder points = new StringBuilder(); + points.append(group.get(0)[0]).append(",").append(group.get(0)[1]); + for (String[] seg : group) { + points.append(" ").append(seg[2]).append(",").append(seg[3]); + } + sb.append("\n"); + } + + /** Merge consecutive same-colour filled rects on the same row into wider rects. */ + private static String mergeRects(String svg) { + String[] lines = svg.split("\n"); + StringBuilder result = new StringBuilder(svg.length()); + ArrayList group = new ArrayList<>(); + String groupFill = null; + + for (String line : lines) { + Matcher m = RECT_PATTERN.matcher(line.trim()); + if (m.matches()) { + int w = Integer.parseInt(m.group(1)); + int h = Integer.parseInt(m.group(2)); + int x = Integer.parseInt(m.group(3)); + int y = Integer.parseInt(m.group(4)); + String fill = m.group(5); + if (w > 100 || h > 100) { + flushRectGroup(result, group, groupFill); + group.clear(); + groupFill = null; + result.append(line).append("\n"); + } else if (fill.equals(groupFill) && !group.isEmpty() + && y == group.get(0)[3] && h == group.get(0)[1] + && x == group.get(0)[2] + group.get(0)[0] * group.size()) { + group.add(new int[]{w, h, x, y}); + } else { + flushRectGroup(result, group, groupFill); + group.clear(); + groupFill = fill; + group.add(new int[]{w, h, x, y}); + } + } else { + flushRectGroup(result, group, groupFill); + group.clear(); + groupFill = null; + result.append(line).append("\n"); + } + } + flushRectGroup(result, group, groupFill); + return result.toString(); + } + + private static void flushRectGroup(StringBuilder sb, ArrayList group, String fill) { + if (group.isEmpty()) return; + int mergedWidth = group.get(0)[0] * group.size(); + int[] first = group.get(0); + sb.append("\n"); + } + }