/* * Copyright 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 static uk.org.okapibarcode.util.Doubles.roughlyEqual; import java.awt.Color; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.OutputStream; import uk.org.okapibarcode.backend.Hexagon; import uk.org.okapibarcode.backend.Symbol; import uk.org.okapibarcode.backend.TextBox; /** * Renders symbologies to EPS (Encapsulated PostScript). * * @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> * @author Daniel Gredler */ public class PostScriptRenderer 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 PostScript 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 PostScriptRenderer(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 { // All y dimensions are reversed because EPS origin (0,0) is at the bottom left, not top left 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; } try (ExtendedOutputStreamWriter writer = new ExtendedOutputStreamWriter(out, "%.2f")) { // Header writer.append("%!PS-Adobe-3.0 EPSF-3.0\n"); writer.append("%%Creator: OkapiBarcode\n"); writer.append("%%Title: ").append(title).append('\n'); writer.append("%%Pages: 0\n"); writer.append("%%BoundingBox: 0 0 ").appendInt(width).append(" ").appendInt(height).append("\n"); writer.append("%%EndComments\n"); // Definitions writer.append("/TL { setlinewidth moveto lineto stroke } bind def\n"); writer.append("/TC { moveto 0 360 arc 360 0 arcn fill } bind def\n"); writer.append("/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def\n"); writer.append("/TB { 2 copy } bind def\n"); writer.append("/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def\n"); writer.append("/TE { pop pop } bind def\n"); // Background writer.append("newpath\n"); writer.append(ink.getRed() / 255.0).append(" ") .append(ink.getGreen() / 255.0).append(" ") .append(ink.getBlue() / 255.0).append(" setrgbcolor\n"); writer.append(paper.getRed() / 255.0).append(" ") .append(paper.getGreen() / 255.0).append(" ") .append(paper.getBlue() / 255.0).append(" setrgbcolor\n"); writer.append(height).append(" 0.00 TB 0.00 ").append(width).append(" TR\n"); // Rectangles for (int i = 0; i < symbol.rectangles.size(); i++) { Rectangle2D.Double rect = symbol.rectangles.get(i); if (i == 0) { writer.append("TE\n"); writer.append(ink.getRed() / 255.0).append(" ") .append(ink.getGreen() / 255.0).append(" ") .append(ink.getBlue() / 255.0).append(" setrgbcolor\n"); writer.append(rect.height * magnification).append(" ") .append(height - ((rect.y + rect.height) * magnification) - marginY).append(" TB ") .append((rect.x * magnification) + marginX).append(" ") .append(rect.width * magnification).append(" TR\n"); } else { Rectangle2D.Double prev = symbol.rectangles.get(i - 1); if (!roughlyEqual(rect.height, prev.height) || !roughlyEqual(rect.y, prev.y)) { writer.append("TE\n"); writer.append(ink.getRed() / 255.0).append(" ") .append(ink.getGreen() / 255.0).append(" ") .append(ink.getBlue() / 255.0).append(" setrgbcolor\n"); writer.append(rect.height * magnification).append(" ") .append(height - ((rect.y + rect.height) * magnification) - marginY).append(" "); } writer.append("TB ").append((rect.x * magnification) + marginX).append(" ").append(rect.width * magnification).append(" TR\n"); } } // Text for (int i = 0; i < symbol.texts.size(); i++) { TextBox text = symbol.texts.get(i); if (i == 0) { writer.append("TE\n");; writer.append(ink.getRed() / 255.0).append(" ") .append(ink.getGreen() / 255.0).append(" ") .append(ink.getBlue() / 255.0).append(" setrgbcolor\n"); } writer.append("matrix currentmatrix\n"); writer.append("/").append(symbol.getFontName()).append(" findfont\n"); writer.append(symbol.getFontSize() * magnification).append(" scalefont setfont\n"); writer.append(" 0 0 moveto ").append((text.x * magnification) + marginX).append(" ") .append(height - (text.y * magnification) - marginY).append(" translate 0.00 rotate 0 0 moveto\n"); writer.append(" (").append(text.text).append(") stringwidth\n"); writer.append("pop\n"); writer.append("-2 div 0 rmoveto\n"); writer.append(" (").append(text.text).append(") show\n"); writer.append("setmatrix\n"); } // Circles // Because MaxiCode size is fixed, this ignores magnification for (int i = 0; i < symbol.target.size(); i += 2) { Ellipse2D.Double ellipse1 = symbol.target.get(i); Ellipse2D.Double ellipse2 = symbol.target.get(i + 1); if (i == 0) { writer.append("TE\n"); writer.append(ink.getRed() / 255.0).append(" ") .append(ink.getGreen() / 255.0).append(" ") .append(ink.getBlue() / 255.0).append(" setrgbcolor\n"); writer.append(ink.getRed() / 255.0).append(" ") .append(ink.getGreen() / 255.0).append(" ") .append(ink.getBlue() / 255.0).append(" setrgbcolor\n"); } double x1 = ellipse1.x + (ellipse1.width / 2); double x2 = ellipse2.x + (ellipse2.width / 2); double y1 = height - ellipse1.y - (ellipse1.width / 2); double y2 = height - ellipse2.y - (ellipse2.width / 2); double r1 = ellipse1.width / 2; double r2 = ellipse2.width / 2; writer.append(x1 + marginX) .append(" ").append(y1 - marginY) .append(" ").append(r1) .append(" ").append(x2 + marginX) .append(" ").append(y2 - marginY) .append(" ").append(r2) .append(" ").append(x2 + r2 + marginX) .append(" ").append(y2 - marginY) .append(" TC\n"); } // Hexagons // Because MaxiCode size is fixed, this ignores magnification for (int i = 0; i < symbol.hexagons.size(); i++) { Hexagon hexagon = symbol.hexagons.get(i); for (int j = 0; j < 6; j++) { writer.append(hexagon.pointX[j] + marginX).append(" ").append((height - hexagon.pointY[j]) - marginY).append(" "); } writer.append(" TH\n"); } // Footer writer.append("\nshowpage\n"); } } }