1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package de.erichseifert.vectorgraphics2d;
23
24 import java.awt.BasicStroke;
25 import java.awt.Color;
26 import java.awt.Font;
27 import java.awt.Image;
28 import java.awt.Shape;
29 import java.awt.Stroke;
30 import java.awt.geom.AffineTransform;
31 import java.awt.geom.Arc2D;
32 import java.awt.geom.Ellipse2D;
33 import java.awt.geom.Line2D;
34 import java.awt.geom.PathIterator;
35 import java.awt.geom.Rectangle2D;
36 import java.awt.image.BufferedImage;
37 import java.io.UnsupportedEncodingException;
38 import java.util.Arrays;
39 import java.util.Map;
40
41
42
43
44
45 public class EPSGraphics2D extends VectorGraphics2D {
46
47 protected static final double MM_IN_UNITS = 72.0 / 25.4;
48
49
50 private static final Map<Integer, Integer> STROKE_ENDCAPS = DataUtils.map(
51 new Integer[] { BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND, BasicStroke.CAP_SQUARE },
52 new Integer[] { 0, 1, 2 }
53 );
54
55
56 private static final Map<Integer, Integer> STROKE_LINEJOIN = DataUtils.map(
57 new Integer[] { BasicStroke.JOIN_MITER, BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL },
58 new Integer[] { 0, 1, 2 }
59 );
60
61
62
63
64
65 public EPSGraphics2D(double x, double y, double width, double height) {
66 super(x, y, width, height);
67 writeHeader();
68 }
69
70 @Override
71 protected void writeString(String str, double x, double y) {
72
73
74
75
76 str = str.replaceAll("\\\\", "\\\\\\\\")
77 .replaceAll("\t", "\\\\t").replaceAll("\b", "\\\\b").replaceAll("\f", "\\\\f")
78 .replaceAll("\\(", "\\\\(").replaceAll("\\)", "\\\\)");
79
80
81
82
83 write("gsave 1 -1 scale ");
84
85
86
87
88
89
90
91
92
93
94
95 str = str.replaceAll("[\r\n]", "");
96 write(x, " -", y, " M (", str, ") show ");
97
98 writeln("grestore");
99 }
100
101 @Override
102 public void setStroke(Stroke s) {
103 BasicStroke bsPrev;
104 if (getStroke() instanceof BasicStroke) {
105 bsPrev = (BasicStroke) getStroke();
106 } else {
107 bsPrev = new BasicStroke();
108 }
109
110 super.setStroke(s);
111
112 if (s instanceof BasicStroke) {
113 BasicStroke bs = (BasicStroke) s;
114 if (bs.getLineWidth() != bsPrev.getLineWidth()) {
115 writeln(bs.getLineWidth(), " setlinewidth");
116 }
117 if (bs.getLineJoin() != bsPrev.getLineJoin()) {
118 writeln(STROKE_LINEJOIN.get(bs.getLineJoin()), " setlinejoin");
119 }
120 if (bs.getEndCap() != bsPrev.getEndCap()) {
121 writeln(STROKE_ENDCAPS.get(bs.getEndCap()), " setlinecap");
122 }
123 if ((!Arrays.equals(bs.getDashArray(), bsPrev.getDashArray())) ||
124 (bs.getDashPhase() != bsPrev.getDashPhase())) {
125 writeln("[", DataUtils.join(" ", bs.getDashArray()), "] ",
126 bs.getDashPhase(), " setdash");
127 }
128 }
129 }
130
131 @Override
132 protected void writeImage(Image img, int imgWidth, int imgHeight, double x, double y, double width, double height) {
133 BufferedImage bufferedImg = GraphicsUtils.toBufferedImage(img);
134 String imgData = getEps(bufferedImg);
135 int bands = bufferedImg.getSampleModel().getNumBands();
136 int bitsPerSample = bufferedImg.getColorModel().getPixelSize() / bands;
137 if (bands > 3) {
138 bands = 3;
139 }
140 writeln("gsave");
141 writeln(x, " ", y, " ", width, " ", height, " ",
142 imgWidth, " ", imgHeight, " ", bitsPerSample, " img false ", bands, " colorimage"
143 );
144 writeln(imgData, ">");
145 writeln("grestore");
146 }
147
148 @Override
149 public void setColor(Color c) {
150 Color color = getColor();
151 if (c != null) {
152 super.setColor(c);
153
154
155
156
157
158 if (color.getRed() != c.getRed() || color.getGreen() != c.getGreen() || color.getBlue() != c.getBlue()) {
159 double r = c.getRed()/255.0;
160 double g = c.getGreen()/255.0;
161 double b = c.getBlue()/255.0;
162 writeln(r, " ", g, " ", b, " rgb");
163 }
164 }
165 }
166
167 @Override
168 public void setFont(Font font) {
169 if (!getFont().equals(font)) {
170 super.setFont(font);
171 writeln("/", font.getPSName(), " ", font.getSize2D(), " selectfont");
172
173 }
174 }
175
176 @Override
177 public void setClip(Shape clip) {
178 if (getClip() != null) {
179 writeln("cliprestore");
180 }
181 super.setClip(clip);
182 if (getClip() != null) {
183 writeShape(getClip());
184 writeln(" clip");
185 }
186 }
187
188 @Override
189 public void setTransform(AffineTransform tx) {
190 super.setTransform(tx);
191 double[] matrix = new double[6];
192 getTransform().getMatrix(matrix);
193 writeln("basematrix setmatrix [", DataUtils.join(" ", matrix), "] concat");
194 }
195
196 @Override
197 public void translate(double tx, double ty) {
198 super.translate(tx, ty);
199 writeln(tx, " ", ty, " translate");
200 }
201
202 @Override
203 public void scale(double tx, double ty) {
204 super.scale(tx, ty);
205 writeln(tx, " ", ty, " scale");
206 }
207
208 @Override
209 public void rotate(double theta) {
210 super.rotate(theta);
211 writeln(theta/Math.PI*180.0, " rotate");
212 }
213
214 @Override
215 public void rotate(double theta, double x, double y) {
216 super.rotate(theta, x, y);
217 writeln(x, " ", y, " translate ", theta/Math.PI*180.0, " rotate ", -x, " ", -y, " translate");
218 }
219
220 @Override
221 public void shear(double sx, double sy) {
222 super.shear(sx, sy);
223 setTransform(getTransform());
224 }
225
226 @Override
227 protected void writeHeader() {
228 Rectangle2D bounds = getBounds();
229 double x = bounds.getX() * MM_IN_UNITS;
230 double y = bounds.getY() * MM_IN_UNITS;
231 double w = bounds.getWidth() * MM_IN_UNITS;
232 double h = bounds.getHeight() * MM_IN_UNITS;
233
234 writeln("%!PS-Adobe-3.0 EPSF-3.0");
235 writeln("%%BoundingBox: ",
236 (int)Math.floor(x), " ", (int)Math.floor(y), " ",
237 (int)Math.ceil(x + w), " ", (int)Math.ceil(y + h));
238 writeln("%%HiResBoundingBox: ", x, " ", y, " ", x + w, " ", y + h);
239 writeln("%%LanguageLevel: 3");
240 writeln("%%Pages: 1");
241 writeln("%%Page: 1 1");
242
243
244 writeln("/M /moveto load def");
245 writeln("/L /lineto load def");
246 writeln("/C /curveto load def");
247 writeln("/Z /closepath load def");
248 writeln("/RL /rlineto load def");
249 writeln("/rgb /setrgbcolor load def");
250 writeln("/rect { ",
251 "/height exch def /width exch def /y exch def /x exch def ",
252 "x y M width 0 RL 0 height RL width neg 0 RL ",
253 "} bind def");
254 writeln("/ellipse { ",
255 "/endangle exch def /startangle exch def /ry exch def /rx exch def /y exch def /x exch def ",
256 "/savematrix matrix currentmatrix def ",
257 "x y translate rx ry scale 0 0 1 startangle endangle arcn ",
258 "savematrix setmatrix ",
259 "} bind def");
260 writeln("/img { ",
261 "/bits exch def /imgheight exch def /imgwidth exch def /height exch def /width exch def /y exch def /x exch def ",
262 "x y translate width height scale ",
263 "imgwidth imgheight bits [imgwidth 0 0 imgheight 0 0] currentfile /ASCIIHexDecode filter ",
264 "} bind def");
265
266 writeln("/", getFont().getPSName(), " ", getFont().getSize2D(), " selectfont");
267
268
269 writeln("gsave");
270
271 writeln("clipsave");
272
273 writeln("/DeviceRGB setcolorspace");
274
275 writeln("0 ", h, " translate");
276 writeln(MM_IN_UNITS, " -", MM_IN_UNITS, " scale");
277 writeln("/basematrix matrix currentmatrix def");
278 }
279
280
281
282
283 @Override
284 protected void writeClosingDraw() {
285 writeln(" stroke");
286 }
287
288
289
290
291 @Override
292 protected void writeClosingFill() {
293 writeln(" fill");
294 }
295
296
297
298
299
300
301 @Override
302 protected void writeShape(Shape s) {
303 write("newpath ");
304 if (s instanceof Line2D) {
305 Line2D l = (Line2D) s;
306 double x1 = l.getX1();
307 double y1 = l.getY1();
308 double x2 = l.getX2();
309 double y2 = l.getY2();
310 write(x1, " ", y1, " M ", x2, " ", y2, " L");
311 return;
312 } else if (s instanceof Rectangle2D) {
313 Rectangle2D r = (Rectangle2D) s;
314 double x = r.getX();
315 double y = r.getY();
316 double width = r.getWidth();
317 double height = r.getHeight();
318 write(x, " ", y, " ", width, " ", height, " rect Z");
319 return;
320 } else if (s instanceof Ellipse2D) {
321 Ellipse2D e = (Ellipse2D) s;
322 double x = e.getX() + e.getWidth()/2.0;
323 double y = e.getY() + e.getHeight()/2.0;
324 double rx = e.getWidth()/2.0;
325 double ry = e.getHeight()/2.0;
326 write(x, " ", y, " ", rx, " ", ry, " ", 360.0, " ", 0.0, " ellipse Z");
327 return;
328 } else if (s instanceof Arc2D) {
329 Arc2D e = (Arc2D) s;
330 double x = (e.getX() + e.getWidth()/2.0);
331 double y = (e.getY() + e.getHeight()/2.0);
332 double rx = e.getWidth()/2.0;
333 double ry = e.getHeight()/2.0;
334 double startAngle = -e.getAngleStart();
335 double endAngle = -(e.getAngleStart() + e.getAngleExtent());
336 write(x, " ", y, " ", rx, " ", ry, " ", startAngle, " ", endAngle, " ellipse");
337 if (e.getArcType() == Arc2D.CHORD) {
338 write(" Z");
339 } else if (e.getArcType() == Arc2D.PIE) {
340 write(" ", x, " ", y, " L Z");
341 }
342 return;
343 } else {
344 PathIterator segments = s.getPathIterator(null);
345 double[] coordsCur = new double[6];
346 double[] pointPrev = new double[2];
347 for (int i = 0; !segments.isDone(); i++, segments.next()) {
348 if (i > 0) {
349 write(" ");
350 }
351 int segmentType = segments.currentSegment(coordsCur);
352 switch (segmentType) {
353 case PathIterator.SEG_MOVETO:
354 write(coordsCur[0], " ", coordsCur[1], " M");
355 pointPrev[0] = coordsCur[0];
356 pointPrev[1] = coordsCur[1];
357 break;
358 case PathIterator.SEG_LINETO:
359 write(coordsCur[0], " ", coordsCur[1], " L");
360 pointPrev[0] = coordsCur[0];
361 pointPrev[1] = coordsCur[1];
362 break;
363 case PathIterator.SEG_CUBICTO:
364 write(coordsCur[0], " ", coordsCur[1], " ", coordsCur[2], " ", coordsCur[3], " ", coordsCur[4], " ", coordsCur[5], " C");
365 pointPrev[0] = coordsCur[4];
366 pointPrev[1] = coordsCur[5];
367 break;
368 case PathIterator.SEG_QUADTO:
369 double x1 = pointPrev[0] + 2.0/3.0*(coordsCur[0] - pointPrev[0]);
370 double y1 = pointPrev[1] + 2.0/3.0*(coordsCur[1] - pointPrev[1]);
371 double x2 = coordsCur[0] + 1.0/3.0*(coordsCur[2] - coordsCur[0]);
372 double y2 = coordsCur[1] + 1.0/3.0*(coordsCur[3] - coordsCur[1]);
373 double x3 = coordsCur[2];
374 double y3 = coordsCur[3];
375 write(x1, " ", y1, " ", x2, " ", y2, " ", x3, " ", y3, " C");
376 pointPrev[0] = x3;
377 pointPrev[1] = y3;
378 break;
379 case PathIterator.SEG_CLOSE:
380 write("Z");
381 break;
382 }
383 }
384 }
385 }
386
387 public static String getEps(BufferedImage bufferedImg) {
388 int width = bufferedImg.getWidth();
389 int height = bufferedImg.getHeight();
390 int bands = bufferedImg.getSampleModel().getNumBands();
391 StringBuffer str = new StringBuffer(width*height*bands*2);
392 for (int y = 0; y < height; y++) {
393 for (int x = 0; x < width; x++) {
394 int pixel = bufferedImg.getRGB(x, y) & 0xffffff;
395 if (bands >= 3) {
396 String hex = String.format("%06x", pixel);
397 str.append(hex);
398 } else if (bands == 1) {
399 str.append(String.format("%02x", pixel));
400 }
401 }
402 str.append("\n");
403 }
404 return str.toString();
405 }
406
407 @Override
408 protected String getFooter() {
409 return "grestore % Restore state\n%%EOF\n";
410 }
411
412 @Override
413 public byte[] getBytes() {
414 try {
415 return toString().getBytes("ISO-8859-1");
416 } catch (UnsupportedEncodingException e) {
417 return super.getBytes();
418 }
419 }
420 }