OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
181 ilm 1
/*
2
 * Copyright 2015 Robin Stuart, Daniel Gredler
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5
 * in compliance with the License. You may obtain a copy of the License at
6
 *
7
 * http://www.apache.org/licenses/LICENSE-2.0
8
 *
9
 * Unless required by applicable law or agreed to in writing, software distributed under the License
10
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11
 * or implied. See the License for the specific language governing permissions and limitations under
12
 * the License.
13
 */
14
 
15
package uk.org.okapibarcode.output;
16
 
17
import static uk.org.okapibarcode.backend.HumanReadableAlignment.CENTER;
18
import static uk.org.okapibarcode.backend.HumanReadableAlignment.JUSTIFY;
19
import static uk.org.okapibarcode.util.Doubles.roughlyEqual;
20
 
21
import java.awt.Color;
22
import java.awt.geom.Ellipse2D;
23
import java.awt.geom.Rectangle2D;
24
import java.io.IOException;
25
import java.io.OutputStream;
26
 
27
import uk.org.okapibarcode.backend.Hexagon;
28
import uk.org.okapibarcode.backend.HumanReadableAlignment;
29
import uk.org.okapibarcode.backend.Symbol;
30
import uk.org.okapibarcode.backend.TextBox;
31
 
32
/**
33
 * Renders symbologies to EPS (Encapsulated PostScript).
34
 *
35
 * @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
36
 * @author Daniel Gredler
37
 */
38
public class PostScriptRenderer implements SymbolRenderer {
39
 
40
    /** The output stream to render to. */
41
    private final OutputStream out;
42
 
43
    /** The magnification factor to apply. */
44
    private final double magnification;
45
 
46
    /** The paper (background) color. */
47
    private final Color paper;
48
 
49
    /** The ink (foreground) color. */
50
    private final Color ink;
51
 
52
    /**
53
     * Creates a new PostScript renderer.
54
     *
55
     * @param out the output stream to render to
56
     * @param magnification the magnification factor to apply
57
     * @param paper the paper (background) color
58
     * @param ink the ink (foreground) color
59
     */
60
    public PostScriptRenderer(final OutputStream out, final double magnification, final Color paper, final Color ink) {
61
        this.out = out;
62
        this.magnification = magnification;
63
        this.paper = paper;
64
        this.ink = ink;
65
    }
66
 
67
    /** {@inheritDoc} */
68
    @Override
69
    public void render(final Symbol symbol) throws IOException {
70
 
71
        // All y dimensions are reversed because EPS origin (0,0) is at the bottom left, not top
72
        // left
73
 
74
        final String content = symbol.getContent();
75
        final int width = (int) (symbol.getWidth() * this.magnification);
76
        final int height = (int) (symbol.getHeight() * this.magnification);
77
        final int marginX = (int) (symbol.getQuietZoneHorizontal() * this.magnification);
78
        final int marginY = (int) (symbol.getQuietZoneVertical() * this.magnification);
79
 
80
        String title;
81
        if (content == null || content.isEmpty()) {
82
            title = "OkapiBarcode Generated Symbol";
83
        } else {
84
            title = content;
85
        }
86
 
87
        try (ExtendedOutputStreamWriter writer = new ExtendedOutputStreamWriter(this.out, "%.2f")) {
88
 
89
            // Header
90
            writer.append("%!PS-Adobe-3.0 EPSF-3.0\n");
91
            writer.append("%%Creator: OkapiBarcode\n");
92
            writer.append("%%Title: ").append(title).append('\n');
93
            writer.append("%%Pages: 0\n");
94
            writer.append("%%BoundingBox: 0 0 ").appendInt(width).append(" ").appendInt(height).append("\n");
95
            writer.append("%%EndComments\n");
96
 
97
            // Definitions
98
            writer.append("/TL { setlinewidth moveto lineto stroke } bind def\n");
99
            writer.append("/TC { moveto 0 360 arc 360 0 arcn fill } bind def\n");
100
            writer.append("/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def\n");
101
            writer.append("/TB { 2 copy } bind def\n");
102
            writer.append("/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def\n");
103
            writer.append("/TE { pop pop } bind def\n");
104
 
105
            // Background
106
            writer.append("newpath\n");
107
            writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
108
            writer.append(this.paper.getRed() / 255.0).append(" ").append(this.paper.getGreen() / 255.0).append(" ").append(this.paper.getBlue() / 255.0).append(" setrgbcolor\n");
109
            writer.append(height).append(" 0.00 TB 0.00 ").append(width).append(" TR\n");
110
 
111
            // Rectangles
112
            for (int i = 0; i < symbol.getRectangles().size(); i++) {
113
                final Rectangle2D.Double rect = symbol.getRectangles().get(i);
114
                if (i == 0) {
115
                    writer.append("TE\n");
116
                    writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
117
                    writer.append(rect.height * this.magnification).append(" ").append(height - (rect.y + rect.height) * this.magnification - marginY).append(" TB ")
118
                            .append(rect.x * this.magnification + marginX).append(" ").append(rect.width * this.magnification).append(" TR\n");
119
                } else {
120
                    final Rectangle2D.Double prev = symbol.getRectangles().get(i - 1);
121
                    if (!roughlyEqual(rect.height, prev.height) || !roughlyEqual(rect.y, prev.y)) {
122
                        writer.append("TE\n");
123
                        writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
124
                        writer.append(rect.height * this.magnification).append(" ").append(height - (rect.y + rect.height) * this.magnification - marginY).append(" ");
125
                    }
126
                    writer.append("TB ").append(rect.x * this.magnification + marginX).append(" ").append(rect.width * this.magnification).append(" TR\n");
127
                }
128
            }
129
 
130
            // Text
131
            for (int i = 0; i < symbol.getTexts().size(); i++) {
132
                final TextBox text = symbol.getTexts().get(i);
133
                final HumanReadableAlignment alignment = text.alignment == JUSTIFY && text.text.length() == 1 ? CENTER : text.alignment;
134
                if (i == 0) {
135
                    writer.append("TE\n");
136
                    ;
137
                    writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
138
                }
139
                writer.append("matrix currentmatrix\n");
140
                writer.append("/").append(symbol.getFontName()).append(" findfont\n");
141
                writer.append(symbol.getFontSize() * this.magnification).append(" scalefont setfont\n");
142
                final double y = height - text.y * this.magnification - marginY;
143
                switch (alignment) {
144
                case LEFT:
145
                    final double leftX = this.magnification * text.x + marginX;
146
                    writer.append(" 0 0 moveto ").append(leftX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
147
                    writer.append(" (").append(text.text).append(") show\n");
148
                    break;
149
                case JUSTIFY:
150
                    final double textX = this.magnification * text.x + marginX;
151
                    final double textW = this.magnification * text.width;
152
                    writer.append(" 0 0 moveto ").append(textX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
153
                    writer.append(" (").append(text.text).append(") dup stringwidth pop ").append(textW).append(" sub neg 1 index length 1 sub div 0").append(" 3 -1 roll ashow\n");
154
                    break;
155
                case RIGHT:
156
                    final double rightX = this.magnification * text.x + this.magnification * text.width + marginX;
157
                    writer.append(" 0 0 moveto ").append(rightX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
158
                    writer.append(" (").append(text.text).append(") stringwidth\n");
159
                    writer.append("pop\n");
160
                    writer.append("-1 mul 0 rmoveto\n");
161
                    writer.append(" (").append(text.text).append(") show\n");
162
                    break;
163
                case CENTER:
164
                    final double centerX = this.magnification * text.x + this.magnification * text.width / 2 + marginX;
165
                    writer.append(" 0 0 moveto ").append(centerX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
166
                    writer.append(" (").append(text.text).append(") stringwidth\n");
167
                    writer.append("pop\n");
168
                    writer.append("-2 div 0 rmoveto\n");
169
                    writer.append(" (").append(text.text).append(") show\n");
170
                    break;
171
                default:
172
                    throw new IllegalStateException("Unknown alignment: " + alignment);
173
                }
174
                writer.append("setmatrix\n");
175
            }
176
 
177
            // Circles
178
            // Because MaxiCode size is fixed, this ignores magnification
179
            for (int i = 0; i < symbol.getTarget().size(); i += 2) {
180
                final Ellipse2D.Double ellipse1 = symbol.getTarget().get(i);
181
                final Ellipse2D.Double ellipse2 = symbol.getTarget().get(i + 1);
182
                if (i == 0) {
183
                    writer.append("TE\n");
184
                    writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
185
                    writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
186
                }
187
                final double x1 = ellipse1.x + ellipse1.width / 2;
188
                final double x2 = ellipse2.x + ellipse2.width / 2;
189
                final double y1 = height - ellipse1.y - ellipse1.width / 2;
190
                final double y2 = height - ellipse2.y - ellipse2.width / 2;
191
                final double r1 = ellipse1.width / 2;
192
                final double r2 = ellipse2.width / 2;
193
                writer.append(x1 + marginX).append(" ").append(y1 - marginY).append(" ").append(r1).append(" ").append(x2 + marginX).append(" ").append(y2 - marginY).append(" ").append(r2).append(" ")
194
                        .append(x2 + r2 + marginX).append(" ").append(y2 - marginY).append(" TC\n");
195
            }
196
 
197
            // Hexagons
198
            // Because MaxiCode size is fixed, this ignores magnification
199
            for (int i = 0; i < symbol.getHexagons().size(); i++) {
200
                final Hexagon hexagon = symbol.getHexagons().get(i);
201
                for (int j = 0; j < 6; j++) {
202
                    writer.append(hexagon.pointX[j] + marginX).append(" ").append(height - hexagon.pointY[j] - marginY).append(" ");
203
                }
204
                writer.append(" TH\n");
205
            }
206
 
207
            // Footer
208
            writer.append("\nshowpage\n");
209
        }
210
    }
211
}