/* * Copyright 2014-2015 Robin Stuart, Daniel Gredler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package uk.org.okapibarcode.output; import uk.org.okapibarcode.backend.Hexagon; import uk.org.okapibarcode.backend.Symbol; import uk.org.okapibarcode.backend.TextBox; import java.awt.*; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.OutputStream; /** * Renders symbologies to SVG (Scalable Vector Graphics). * * @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> * @author Daniel Gredler */ public class SvgRenderer implements SymbolRenderer { /** The output stream to render to. */ private final OutputStream out; /** The magnification factor to apply. */ private final double magnification; /** The paper (background) color. */ private final Color paper; /** The ink (foreground) color. */ private final Color ink; /** * Creates a new SVG renderer. * * @param out the output stream to render to * @param magnification the magnification factor to apply * @param paper the paper (background) color * @param ink the ink (foreground) color */ public SvgRenderer(OutputStream out, double magnification, Color paper, Color ink) { this.out = out; this.magnification = magnification; this.paper = paper; this.ink = ink; } /** {@inheritDoc} */ @Override public void render(Symbol symbol) throws IOException { String content = symbol.getContent(); int width = (int) (symbol.getWidth() * magnification); int height = (int) (symbol.getHeight() * magnification); int marginX = (int) (symbol.getQuietZoneHorizontal() * magnification); int marginY = (int) (symbol.getQuietZoneVertical() * magnification); String title; if (content == null || content.isEmpty()) { title = "OkapiBarcode Generated Symbol"; } else { title = content.replaceAll("[\u0000-\u001f]", ""); } String fgColour = String.format("%02X", ink.getRed()) + String.format("%02X", ink.getGreen()) + String.format("%02X", ink.getBlue()); String bgColour = String.format("%02X", paper.getRed()) + String.format("%02X", paper.getGreen()) + String.format("%02X", paper.getBlue()); try (ExtendedOutputStreamWriter writer = new ExtendedOutputStreamWriter(out, "%.2f")) { // Header writer.append("<?xml version=\"1.0\" standalone=\"no\"?>\n"); writer.append("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n"); writer.append(" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); writer.append("<svg width=\"").appendInt(width) .append("\" height=\"").appendInt(height) .append("\" version=\"1.1") .append("\" xmlns=\"http://www.w3.org/2000/svg\">\n"); writer.append(" <desc>").append(title).append("</desc>\n"); writer.append(" <g id=\"barcode\" fill=\"#").append(fgColour).append("\">\n"); writer.append(" <rect x=\"0\" y=\"0\" width=\"").appendInt(width) .append("\" height=\"").appendInt(height) .append("\" fill=\"#").append(bgColour).append("\" />\n"); // Rectangles for (int i = 0; i < symbol.rectangles.size(); i++) { Rectangle2D.Double rect = symbol.rectangles.get(i); writer.append(" <rect x=\"").append((rect.x * magnification) + marginX) .append("\" y=\"").append((rect.y * magnification) + marginY) .append("\" width=\"").append(rect.width * magnification) .append("\" height=\"").append(rect.height * magnification) .append("\" />\n"); } // Text for (int i = 0; i < symbol.texts.size(); i++) { TextBox text = symbol.texts.get(i); writer.append(" <text x=\"").append((text.x * magnification) + marginX) .append("\" y=\"").append((text.y * magnification) + marginY) .append("\" text-anchor=\"middle\"\n"); writer.append(" font-family=\"").append(symbol.getFontName()) .append("\" font-size=\"").append(symbol.getFontSize() * magnification) .append("\" fill=\"#").append(fgColour).append("\">\n"); writer.append(" ").append(text.text).append("\n"); writer.append(" </text>\n"); } // Circles for (int i = 0; i < symbol.target.size(); i++) { Ellipse2D.Double ellipse = symbol.target.get(i); String color; if ((i & 1) == 0) { color = fgColour; } else { color = bgColour; } writer.append(" <circle cx=\"").append(((ellipse.x + (ellipse.width / 2)) * magnification) + marginX) .append("\" cy=\"").append(((ellipse.y + (ellipse.width / 2)) * magnification) + marginY) .append("\" r=\"").append((ellipse.width / 2) * magnification) .append("\" fill=\"#").append(color).append("\" />\n"); } // Hexagons for (int i = 0; i < symbol.hexagons.size(); i++) { Hexagon hexagon = symbol.hexagons.get(i); writer.append(" <path d=\""); for (int j = 0; j < 6; j++) { if (j == 0) { writer.append("M "); } else { writer.append("L "); } writer.append((hexagon.pointX[j] * magnification) + marginX).append(" ") .append((hexagon.pointY[j] * magnification) + marginY).append(" "); } writer.append("Z\" />\n"); } // Footer writer.append(" </g>\n"); writer.append("</svg>\n"); } } }