Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
EpsGraphics2D | 121 | 387 | 152 |
1 | /* | |
2 | * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) | |
3 | * Copyright (C) $$Year-Rel$$ The Jalview Authors | |
4 | * | |
5 | * This file is part of Jalview. | |
6 | * | |
7 | * Jalview is free software: you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * as published by the Free Software Foundation, either version 3 | |
10 | * of the License, or (at your option) any later version. | |
11 | * | |
12 | * Jalview is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty | |
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
15 | * PURPOSE. See the GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with Jalview. If not, see <http://www.gnu.org/licenses/>. | |
19 | * The Jalview Authors are detailed in the 'AUTHORS' file. | |
20 | */ | |
21 | package org.jibble.epsgraphics; | |
22 | ||
23 | import jalview.util.MessageManager; | |
24 | ||
25 | import java.awt.AlphaComposite; | |
26 | import java.awt.BasicStroke; | |
27 | import java.awt.Color; | |
28 | import java.awt.Composite; | |
29 | import java.awt.Font; | |
30 | import java.awt.FontMetrics; | |
31 | import java.awt.Graphics; | |
32 | import java.awt.GraphicsConfiguration; | |
33 | import java.awt.GraphicsDevice; | |
34 | import java.awt.GraphicsEnvironment; | |
35 | import java.awt.Image; | |
36 | import java.awt.Paint; | |
37 | import java.awt.Polygon; | |
38 | import java.awt.Rectangle; | |
39 | import java.awt.RenderingHints; | |
40 | import java.awt.Shape; | |
41 | import java.awt.Stroke; | |
42 | import java.awt.font.FontRenderContext; | |
43 | import java.awt.font.GlyphVector; | |
44 | import java.awt.font.TextAttribute; | |
45 | import java.awt.font.TextLayout; | |
46 | import java.awt.geom.AffineTransform; | |
47 | import java.awt.geom.Arc2D; | |
48 | import java.awt.geom.Area; | |
49 | import java.awt.geom.Ellipse2D; | |
50 | import java.awt.geom.GeneralPath; | |
51 | import java.awt.geom.Line2D; | |
52 | import java.awt.geom.PathIterator; | |
53 | import java.awt.geom.Point2D; | |
54 | import java.awt.geom.Rectangle2D; | |
55 | import java.awt.geom.RoundRectangle2D; | |
56 | import java.awt.image.BufferedImage; | |
57 | import java.awt.image.BufferedImageOp; | |
58 | import java.awt.image.ColorModel; | |
59 | import java.awt.image.ImageObserver; | |
60 | import java.awt.image.PixelGrabber; | |
61 | import java.awt.image.RenderedImage; | |
62 | import java.awt.image.WritableRaster; | |
63 | import java.awt.image.renderable.RenderableImage; | |
64 | import java.io.File; | |
65 | import java.io.FileOutputStream; | |
66 | import java.io.IOException; | |
67 | import java.io.OutputStream; | |
68 | import java.io.StringWriter; | |
69 | import java.text.AttributedCharacterIterator; | |
70 | import java.text.AttributedString; | |
71 | import java.text.CharacterIterator; | |
72 | import java.util.Hashtable; | |
73 | import java.util.Map; | |
74 | ||
75 | /** | |
76 | * EpsGraphics2D is suitable for creating high quality EPS graphics for use in | |
77 | * documents and papers, and can be used just like a standard Graphics2D object. | |
78 | * <p> | |
79 | * Many Java programs use Graphics2D to draw stuff on the screen, and while it | |
80 | * is easy to save the output as a png or jpeg file, it is a little harder to | |
81 | * export it as an EPS for including in a document or paper. | |
82 | * <p> | |
83 | * This class makes the whole process extremely easy, because you can use it as | |
84 | * if it's a Graphics2D object. The only difference is that all of the | |
85 | * implemented methods create EPS output, which means the diagrams you draw can | |
86 | * be resized without leading to any of the jagged edges you may see when | |
87 | * resizing pixel-based images, such as jpeg and png files. | |
88 | * <p> | |
89 | * Example usage: | |
90 | * <p> | |
91 | * | |
92 | * <pre> | |
93 | * Graphics2D g = new EpsGraphics2D(); | |
94 | * g.setColor(Color.black); | |
95 | * | |
96 | * // Line thickness 2. | |
97 | * g.setStroke(new BasicStroke(2.0f)); | |
98 | * | |
99 | * // Draw a line. | |
100 | * g.drawLine(10, 10, 50, 10); | |
101 | * | |
102 | * // Fill a rectangle in blue | |
103 | * g.setColor(Color.blue); | |
104 | * g.fillRect(10, 0, 20, 20); | |
105 | * | |
106 | * // Get the EPS output. | |
107 | * String output = g.toString(); | |
108 | * </pre> | |
109 | * | |
110 | * <p> | |
111 | * You do not need to worry about the size of the canvas when drawing on a | |
112 | * EpsGraphics2D object. The bounding box of the EPS document will automatically | |
113 | * resize to accomodate new items that you draw. | |
114 | * <p> | |
115 | * Not all methods are implemented yet. Those that are not are clearly labelled. | |
116 | * <p> | |
117 | * Copyright Paul Mutton, | |
118 | * <a href="http://www.jibble.org/">http://www.jibble.org/</a> | |
119 | * | |
120 | */ | |
121 | public class EpsGraphics2D extends java.awt.Graphics2D | |
122 | implements AutoCloseable | |
123 | { | |
124 | ||
125 | public static final String VERSION = "0.8.8"; | |
126 | ||
127 | /** | |
128 | * Constructs a new EPS document that is initially empty and can be drawn on | |
129 | * like a Graphics2D object. The EPS document is stored in memory. | |
130 | */ | |
131 | 0 | public EpsGraphics2D() |
132 | { | |
133 | 0 | this("Untitled"); |
134 | } | |
135 | ||
136 | /** | |
137 | * Constructs a new EPS document that is initially empty and can be drawn on | |
138 | * like a Graphics2D object. The EPS document is stored in memory. | |
139 | */ | |
140 | 10 | public EpsGraphics2D(String title) |
141 | { | |
142 | 10 | _document = new EpsDocument(title); |
143 | 10 | _backgroundColor = Color.white; |
144 | 10 | _clip = null; |
145 | 10 | _transform = new AffineTransform(); |
146 | 10 | _clipTransform = new AffineTransform(); |
147 | 10 | _accurateTextMode = true; |
148 | 10 | setColor(Color.black); |
149 | 10 | setPaint(Color.black); |
150 | 10 | setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR)); |
151 | 10 | setFont(Font.decode(null)); |
152 | 10 | setStroke(new BasicStroke()); |
153 | } | |
154 | ||
155 | /** | |
156 | * Constructs a new EPS document that is initially empty and can be drawn on | |
157 | * like a Graphics2D object. The EPS document is written to the file as it | |
158 | * goes, which reduces memory usage. The bounding box of the document is fixed | |
159 | * and specified at construction time by minX,minY,maxX,maxY. The file is | |
160 | * flushed and closed when the close() method is called. | |
161 | */ | |
162 | 0 | public EpsGraphics2D(String title, File file, int minX, int minY, |
163 | int maxX, int maxY) throws IOException | |
164 | { | |
165 | 0 | this(title, new FileOutputStream(file), minX, minY, maxX, maxY); |
166 | } | |
167 | ||
168 | /** | |
169 | * Constructs a new EPS document that is initially empty and can be drawn on | |
170 | * like a Graphics2D object. The EPS document is written to the output stream | |
171 | * as it goes, which reduces memory usage. The bounding box of the document is | |
172 | * fixed and specified at construction time by minX,minY,maxX,maxY. The output | |
173 | * stream is flushed and closed when the close() method is called. | |
174 | */ | |
175 | 10 | public EpsGraphics2D(String title, OutputStream outputStream, int minX, |
176 | int minY, int maxX, int maxY) throws IOException | |
177 | { | |
178 | 10 | this(title); |
179 | 10 | _document = new EpsDocument(title, outputStream, minX, minY, maxX, |
180 | maxY); | |
181 | } | |
182 | ||
183 | /** | |
184 | * Constructs a new EpsGraphics2D instance that is a copy of the supplied | |
185 | * argument and points at the same EpsDocument. | |
186 | */ | |
187 | 20 | protected EpsGraphics2D(EpsGraphics2D g) |
188 | { | |
189 | 20 | _document = g._document; |
190 | 20 | _backgroundColor = g._backgroundColor; |
191 | 20 | _clip = g._clip; |
192 | 20 | _clipTransform = (AffineTransform) g._clipTransform.clone(); |
193 | 20 | _transform = (AffineTransform) g._transform.clone(); |
194 | 20 | _color = g._color; |
195 | 20 | _paint = g._paint; |
196 | 20 | _composite = g._composite; |
197 | 20 | _font = g._font; |
198 | 20 | _stroke = g._stroke; |
199 | 20 | _accurateTextMode = g._accurateTextMode; |
200 | } | |
201 | ||
202 | /** | |
203 | * This method is called to indicate that a particular method is not supported | |
204 | * yet. The stack trace is printed to the standard output. | |
205 | */ | |
206 | 0 | private void methodNotSupported() |
207 | { | |
208 | 0 | EpsException e = new EpsException(MessageManager.formatMessage( |
209 | "exception.eps_method_not_supported", new String[] | |
210 | { VERSION })); | |
211 | 0 | e.printStackTrace(System.err); |
212 | } | |
213 | ||
214 | // ///////////// Specialist methods /////////////////////// | |
215 | ||
216 | /** | |
217 | * Sets whether to use accurate text mode when rendering text in EPS. This is | |
218 | * enabled (true) by default. When accurate text mode is used, all text will | |
219 | * be rendered in EPS to appear exactly the same as it would do when drawn | |
220 | * with a Graphics2D context. With accurate text mode enabled, it is not | |
221 | * necessary for the EPS viewer to have the required font installed. | |
222 | * <p> | |
223 | * Turning off accurate text mode will require the EPS viewer to have the | |
224 | * necessary fonts installed. If you are using a lot of text, you will find | |
225 | * that this significantly reduces the file size of your EPS documents. | |
226 | * AffineTransforms can only affect the starting point of text using this | |
227 | * simpler text mode - all text will be horizontal. | |
228 | */ | |
229 | 10 | public void setAccurateTextMode(boolean b) |
230 | { | |
231 | 10 | _accurateTextMode = b; |
232 | } | |
233 | ||
234 | /** | |
235 | * Returns whether accurate text mode is being used. | |
236 | */ | |
237 | 32580 | public boolean getAccurateTextMode() |
238 | { | |
239 | 32580 | return _accurateTextMode; |
240 | } | |
241 | ||
242 | /** | |
243 | * Flushes the buffered contents of this EPS document to the underlying | |
244 | * OutputStream it is being written to. | |
245 | */ | |
246 | 20 | public void flush() throws IOException |
247 | { | |
248 | 20 | _document.flush(); |
249 | } | |
250 | ||
251 | /** | |
252 | * Closes the EPS file being output to the underlying OutputStream. The | |
253 | * OutputStream is automatically flushed before being closed. If you forget to | |
254 | * do this, the file may be incomplete. | |
255 | */ | |
256 | 10 | @Override |
257 | public void close() throws IOException | |
258 | { | |
259 | 10 | flush(); |
260 | 10 | _document.close(); |
261 | } | |
262 | ||
263 | /** | |
264 | * Appends a line to the EpsDocument. | |
265 | */ | |
266 | 264080 | private void append(String line) |
267 | { | |
268 | 264080 | _document.append(this, line); |
269 | } | |
270 | ||
271 | /** | |
272 | * Returns the point after it has been transformed by the transformation. | |
273 | */ | |
274 | 32580 | private Point2D transform(float x, float y) |
275 | { | |
276 | 32580 | Point2D result = new Point2D.Float(x, y); |
277 | 32580 | result = _transform.transform(result, result); |
278 | 32580 | result.setLocation(result.getX(), -result.getY()); |
279 | 32580 | return result; |
280 | } | |
281 | ||
282 | /** | |
283 | * Appends the commands required to draw a shape on the EPS document. | |
284 | */ | |
285 | 7010 | private void draw(Shape s, String action) |
286 | { | |
287 | ||
288 | 7010 | if (s != null) |
289 | { | |
290 | ||
291 | // Rectangle2D userBounds = s.getBounds2D(); | |
292 | 7010 | if (!_transform.isIdentity()) |
293 | { | |
294 | 6990 | s = _transform.createTransformedShape(s); |
295 | } | |
296 | ||
297 | // Update the bounds. | |
298 | 7010 | if (!action.equals("clip")) |
299 | { | |
300 | 7010 | Rectangle2D shapeBounds = s.getBounds2D(); |
301 | 7010 | Rectangle2D visibleBounds = shapeBounds; |
302 | 7010 | if (_clip != null) |
303 | { | |
304 | 0 | Rectangle2D clipBounds = _clip.getBounds2D(); |
305 | 0 | visibleBounds = shapeBounds.createIntersection(clipBounds); |
306 | } | |
307 | 7010 | float lineRadius = _stroke.getLineWidth() / 2; |
308 | 7010 | float minX = (float) visibleBounds.getMinX() - lineRadius; |
309 | 7010 | float minY = (float) visibleBounds.getMinY() - lineRadius; |
310 | 7010 | float maxX = (float) visibleBounds.getMaxX() + lineRadius; |
311 | 7010 | float maxY = (float) visibleBounds.getMaxY() + lineRadius; |
312 | 7010 | _document.updateBounds(minX, -minY); |
313 | 7010 | _document.updateBounds(maxX, -maxY); |
314 | } | |
315 | ||
316 | 7010 | append("newpath"); |
317 | 7010 | int type = 0; |
318 | 7010 | float[] coords = new float[6]; |
319 | 7010 | PathIterator it = s.getPathIterator(null); |
320 | 7010 | float x0 = 0; |
321 | 7010 | float y0 = 0; |
322 | 7010 | int count = 0; |
323 | 47670 | while (!it.isDone()) |
324 | { | |
325 | 40660 | type = it.currentSegment(coords); |
326 | 40660 | float x1 = coords[0]; |
327 | 40660 | float y1 = -coords[1]; |
328 | 40660 | float x2 = coords[2]; |
329 | 40660 | float y2 = -coords[3]; |
330 | 40660 | float x3 = coords[4]; |
331 | 40660 | float y3 = -coords[5]; |
332 | ||
333 | 40660 | if (type == PathIterator.SEG_CLOSE) |
334 | { | |
335 | 6660 | append("closepath"); |
336 | 6660 | count++; |
337 | } | |
338 | 34000 | else if (type == PathIterator.SEG_CUBICTO) |
339 | { | |
340 | 0 | append(x1 + " " + y1 + " " + x2 + " " + y2 + " " + x3 + " " + y3 |
341 | + " curveto"); | |
342 | 0 | count++; |
343 | 0 | x0 = x3; |
344 | 0 | y0 = y3; |
345 | } | |
346 | 34000 | else if (type == PathIterator.SEG_LINETO) |
347 | { | |
348 | 26990 | append(x1 + " " + y1 + " lineto"); |
349 | 26990 | count++; |
350 | 26990 | x0 = x1; |
351 | 26990 | y0 = y1; |
352 | } | |
353 | 7010 | else if (type == PathIterator.SEG_MOVETO) |
354 | { | |
355 | 7010 | append(x1 + " " + y1 + " moveto"); |
356 | 7010 | count++; |
357 | 7010 | x0 = x1; |
358 | 7010 | y0 = y1; |
359 | } | |
360 | 0 | else if (type == PathIterator.SEG_QUADTO) |
361 | { | |
362 | // Convert the quad curve into a cubic. | |
363 | 0 | float _x1 = x0 + 2 / 3f * (x1 - x0); |
364 | 0 | float _y1 = y0 + 2 / 3f * (y1 - y0); |
365 | 0 | float _x2 = x1 + 1 / 3f * (x2 - x1); |
366 | 0 | float _y2 = y1 + 1 / 3f * (y2 - y1); |
367 | 0 | float _x3 = x2; |
368 | 0 | float _y3 = y2; |
369 | 0 | append(_x1 + " " + _y1 + " " + _x2 + " " + _y2 + " " + _x3 + " " |
370 | + _y3 + " curveto"); | |
371 | 0 | count++; |
372 | 0 | x0 = _x3; |
373 | 0 | y0 = _y3; |
374 | } | |
375 | 0 | else if (type == PathIterator.WIND_EVEN_ODD) |
376 | { | |
377 | // Ignore. | |
378 | } | |
379 | 0 | else if (type == PathIterator.WIND_NON_ZERO) |
380 | { | |
381 | // Ignore. | |
382 | } | |
383 | 40660 | it.next(); |
384 | } | |
385 | 7010 | append(action); |
386 | 7010 | append("newpath"); |
387 | } | |
388 | } | |
389 | ||
390 | /** | |
391 | * Returns a hex string that always contains two characters. | |
392 | */ | |
393 | 0 | private String toHexString(int n) |
394 | { | |
395 | 0 | String result = Integer.toString(n, 16); |
396 | 0 | while (result.length() < 2) |
397 | { | |
398 | 0 | result = "0" + result; |
399 | } | |
400 | 0 | return result; |
401 | } | |
402 | ||
403 | // ///////////// Graphics2D methods /////////////////////// | |
404 | ||
405 | /** | |
406 | * Draws a 3D rectangle outline. If it is raised, light appears to come from | |
407 | * the top left. | |
408 | */ | |
409 | 0 | @Override |
410 | public void draw3DRect(int x, int y, int width, int height, | |
411 | boolean raised) | |
412 | { | |
413 | 0 | Color originalColor = getColor(); |
414 | 0 | Stroke originalStroke = getStroke(); |
415 | ||
416 | 0 | setStroke(new BasicStroke(1.0f)); |
417 | ||
418 | 0 | if (raised) |
419 | { | |
420 | 0 | setColor(originalColor.brighter()); |
421 | } | |
422 | else | |
423 | { | |
424 | 0 | setColor(originalColor.darker()); |
425 | } | |
426 | ||
427 | 0 | drawLine(x, y, x + width, y); |
428 | 0 | drawLine(x, y, x, y + height); |
429 | ||
430 | 0 | if (raised) |
431 | { | |
432 | 0 | setColor(originalColor.darker()); |
433 | } | |
434 | else | |
435 | { | |
436 | 0 | setColor(originalColor.brighter()); |
437 | } | |
438 | ||
439 | 0 | drawLine(x + width, y + height, x, y + height); |
440 | 0 | drawLine(x + width, y + height, x + width, y); |
441 | ||
442 | 0 | setColor(originalColor); |
443 | 0 | setStroke(originalStroke); |
444 | } | |
445 | ||
446 | /** | |
447 | * Fills a 3D rectangle. If raised, it has bright fill and light appears to | |
448 | * come from the top left. | |
449 | */ | |
450 | 0 | @Override |
451 | public void fill3DRect(int x, int y, int width, int height, | |
452 | boolean raised) | |
453 | { | |
454 | 0 | Color originalColor = getColor(); |
455 | ||
456 | 0 | if (raised) |
457 | { | |
458 | 0 | setColor(originalColor.brighter()); |
459 | } | |
460 | else | |
461 | { | |
462 | 0 | setColor(originalColor.darker()); |
463 | } | |
464 | 0 | draw(new Rectangle(x, y, width, height), "fill"); |
465 | 0 | setColor(originalColor); |
466 | 0 | draw3DRect(x, y, width, height, raised); |
467 | } | |
468 | ||
469 | /** | |
470 | * Draws a Shape on the EPS document. | |
471 | */ | |
472 | 350 | @Override |
473 | public void draw(Shape s) | |
474 | { | |
475 | 350 | draw(s, "stroke"); |
476 | } | |
477 | ||
478 | /** | |
479 | * Draws an Image on the EPS document. | |
480 | */ | |
481 | 0 | @Override |
482 | public boolean drawImage(Image img, AffineTransform xform, | |
483 | ImageObserver obs) | |
484 | { | |
485 | 0 | AffineTransform at = getTransform(); |
486 | 0 | transform(xform); |
487 | 0 | boolean st = drawImage(img, 0, 0, obs); |
488 | 0 | setTransform(at); |
489 | 0 | return st; |
490 | } | |
491 | ||
492 | /** | |
493 | * Draws a BufferedImage on the EPS document. | |
494 | */ | |
495 | 0 | @Override |
496 | public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) | |
497 | { | |
498 | 0 | BufferedImage img1 = op.filter(img, null); |
499 | 0 | drawImage(img1, new AffineTransform(1f, 0f, 0f, 1f, x, y), null); |
500 | } | |
501 | ||
502 | /** | |
503 | * Draws a RenderedImage on the EPS document. | |
504 | */ | |
505 | 0 | @Override |
506 | public void drawRenderedImage(RenderedImage img, AffineTransform xform) | |
507 | { | |
508 | 0 | Hashtable properties = new Hashtable(); |
509 | 0 | String[] names = img.getPropertyNames(); |
510 | 0 | for (int i = 0; i < names.length; i++) |
511 | { | |
512 | 0 | properties.put(names[i], img.getProperty(names[i])); |
513 | } | |
514 | ||
515 | 0 | ColorModel cm = img.getColorModel(); |
516 | 0 | WritableRaster wr = img.copyData(null); |
517 | 0 | BufferedImage img1 = new BufferedImage(cm, wr, |
518 | cm.isAlphaPremultiplied(), properties); | |
519 | 0 | AffineTransform at = AffineTransform.getTranslateInstance(img.getMinX(), |
520 | img.getMinY()); | |
521 | 0 | at.preConcatenate(xform); |
522 | 0 | drawImage(img1, at, null); |
523 | } | |
524 | ||
525 | /** | |
526 | * Draws a RenderableImage by invoking its createDefaultRendering method. | |
527 | */ | |
528 | 0 | @Override |
529 | public void drawRenderableImage(RenderableImage img, | |
530 | AffineTransform xform) | |
531 | { | |
532 | 0 | drawRenderedImage(img.createDefaultRendering(), xform); |
533 | } | |
534 | ||
535 | /** | |
536 | * Draws a string at (x,y) | |
537 | */ | |
538 | 32580 | @Override |
539 | public void drawString(String str, int x, int y) | |
540 | { | |
541 | 32580 | drawString(str, (float) x, (float) y); |
542 | } | |
543 | ||
544 | /** | |
545 | * Draws a string at (x,y) | |
546 | */ | |
547 | 32580 | @Override |
548 | public void drawString(String s, float x, float y) | |
549 | { | |
550 | 32580 | if (s != null && s.length() > 0) |
551 | { | |
552 | 32580 | AttributedString as = new AttributedString(s); |
553 | 32580 | as.addAttribute(TextAttribute.FONT, getFont()); |
554 | 32580 | drawString(as.getIterator(), x, y); |
555 | } | |
556 | } | |
557 | ||
558 | /** | |
559 | * Draws the characters of an AttributedCharacterIterator, starting from | |
560 | * (x,y). | |
561 | */ | |
562 | 0 | @Override |
563 | public void drawString(AttributedCharacterIterator iterator, int x, int y) | |
564 | { | |
565 | 0 | drawString(iterator, (float) x, (float) y); |
566 | } | |
567 | ||
568 | /** | |
569 | * Draws the characters of an AttributedCharacterIterator, starting from | |
570 | * (x,y). | |
571 | */ | |
572 | 32580 | @Override |
573 | public void drawString(AttributedCharacterIterator iterator, float x, | |
574 | float y) | |
575 | { | |
576 | 32580 | if (getAccurateTextMode()) |
577 | { | |
578 | 0 | TextLayout layout = new TextLayout(iterator, getFontRenderContext()); |
579 | 0 | Shape shape = layout |
580 | .getOutline(AffineTransform.getTranslateInstance(x, y)); | |
581 | 0 | draw(shape, "fill"); |
582 | } | |
583 | else | |
584 | { | |
585 | 32580 | append("newpath"); |
586 | 32580 | Point2D location = transform(x, y); |
587 | 32580 | append(location.getX() + " " + location.getY() + " moveto"); |
588 | 32580 | StringBuffer buffer = new StringBuffer(); |
589 | 32580 | for (char ch = iterator |
590 | 67950 | .first(); ch != CharacterIterator.DONE; ch = iterator.next()) |
591 | { | |
592 | 35370 | if (ch == '(' || ch == ')') |
593 | { | |
594 | 0 | buffer.append('\\'); |
595 | } | |
596 | 35370 | buffer.append(ch); |
597 | } | |
598 | 32580 | append("(" + buffer.toString() + ") show"); |
599 | } | |
600 | } | |
601 | ||
602 | /** | |
603 | * Draws a GlyphVector at (x,y) | |
604 | */ | |
605 | 0 | @Override |
606 | public void drawGlyphVector(GlyphVector g, float x, float y) | |
607 | { | |
608 | 0 | Shape shape = g.getOutline(x, y); |
609 | 0 | draw(shape, "fill"); |
610 | } | |
611 | ||
612 | /** | |
613 | * Fills a Shape on the EPS document. | |
614 | */ | |
615 | 0 | @Override |
616 | public void fill(Shape s) | |
617 | { | |
618 | 0 | draw(s, "fill"); |
619 | } | |
620 | ||
621 | /** | |
622 | * Checks whether or not the specified Shape intersects the specified | |
623 | * Rectangle, which is in device space. | |
624 | */ | |
625 | 0 | @Override |
626 | public boolean hit(Rectangle rect, Shape s, boolean onStroke) | |
627 | { | |
628 | 0 | return s.intersects(rect); |
629 | } | |
630 | ||
631 | /** | |
632 | * Returns the device configuration associated with this EpsGraphics2D object. | |
633 | */ | |
634 | 0 | @Override |
635 | public GraphicsConfiguration getDeviceConfiguration() | |
636 | { | |
637 | 0 | GraphicsConfiguration gc = null; |
638 | 0 | GraphicsEnvironment ge = GraphicsEnvironment |
639 | .getLocalGraphicsEnvironment(); | |
640 | 0 | GraphicsDevice[] gds = ge.getScreenDevices(); |
641 | 0 | for (int i = 0; i < gds.length; i++) |
642 | { | |
643 | 0 | GraphicsDevice gd = gds[i]; |
644 | 0 | GraphicsConfiguration[] gcs = gd.getConfigurations(); |
645 | 0 | if (gcs.length > 0) |
646 | { | |
647 | 0 | return gcs[0]; |
648 | } | |
649 | } | |
650 | 0 | return gc; |
651 | } | |
652 | ||
653 | /** | |
654 | * Sets the Composite to be used by this EpsGraphics2D. EpsGraphics2D does not | |
655 | * make use of these. | |
656 | */ | |
657 | 10 | @Override |
658 | public void setComposite(Composite comp) | |
659 | { | |
660 | 10 | _composite = comp; |
661 | } | |
662 | ||
663 | /** | |
664 | * Sets the Paint attribute for the EpsGraphics2D object. Only Paint objects | |
665 | * of type Color are respected by EpsGraphics2D. | |
666 | */ | |
667 | 10 | @Override |
668 | public void setPaint(Paint paint) | |
669 | { | |
670 | 10 | _paint = paint; |
671 | 10 | if (paint instanceof Color) |
672 | { | |
673 | 10 | setColor((Color) paint); |
674 | } | |
675 | } | |
676 | ||
677 | /** | |
678 | * Sets the stroke. Only accepts BasicStroke objects (or subclasses of | |
679 | * BasicStroke). | |
680 | */ | |
681 | 9540 | @Override |
682 | public void setStroke(Stroke s) | |
683 | { | |
684 | 9540 | if (s instanceof BasicStroke) |
685 | { | |
686 | 9540 | _stroke = (BasicStroke) s; |
687 | ||
688 | 9540 | append(_stroke.getLineWidth() + " setlinewidth"); |
689 | 9540 | float miterLimit = _stroke.getMiterLimit(); |
690 | 9540 | if (miterLimit < 1.0f) |
691 | { | |
692 | 0 | miterLimit = 1; |
693 | } | |
694 | 9540 | append(miterLimit + " setmiterlimit"); |
695 | 9540 | append(_stroke.getLineJoin() + " setlinejoin"); |
696 | 9540 | append(_stroke.getEndCap() + " setlinecap"); |
697 | ||
698 | 9540 | StringBuffer dashes = new StringBuffer(); |
699 | 9540 | dashes.append("[ "); |
700 | 9540 | float[] dashArray = _stroke.getDashArray(); |
701 | 9540 | if (dashArray != null) |
702 | { | |
703 | 0 | for (int i = 0; i < dashArray.length; i++) |
704 | { | |
705 | 0 | dashes.append((dashArray[i]) + " "); |
706 | } | |
707 | } | |
708 | 9540 | dashes.append("]"); |
709 | 9540 | append(dashes.toString() + " 0 setdash"); |
710 | } | |
711 | } | |
712 | ||
713 | /** | |
714 | * Sets a rendering hint. These are not used by EpsGraphics2D. | |
715 | */ | |
716 | 30 | @Override |
717 | public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) | |
718 | { | |
719 | // Do nothing. | |
720 | } | |
721 | ||
722 | /** | |
723 | * Returns the value of a single preference for the rendering algorithms. | |
724 | * Rendering hints are not used by EpsGraphics2D. | |
725 | */ | |
726 | 0 | @Override |
727 | public Object getRenderingHint(RenderingHints.Key hintKey) | |
728 | { | |
729 | 0 | return null; |
730 | } | |
731 | ||
732 | /** | |
733 | * Sets the rendering hints. These are ignored by EpsGraphics2D. | |
734 | */ | |
735 | 0 | @Override |
736 | public void setRenderingHints(Map hints) | |
737 | { | |
738 | // Do nothing. | |
739 | } | |
740 | ||
741 | /** | |
742 | * Adds rendering hints. These are ignored by EpsGraphics2D. | |
743 | */ | |
744 | 0 | @Override |
745 | public void addRenderingHints(Map hints) | |
746 | { | |
747 | // Do nothing. | |
748 | } | |
749 | ||
750 | /** | |
751 | * Returns the preferences for the rendering algorithms. | |
752 | */ | |
753 | 0 | @Override |
754 | public RenderingHints getRenderingHints() | |
755 | { | |
756 | 0 | return new RenderingHints(null); |
757 | } | |
758 | ||
759 | /** | |
760 | * Translates the origin of the EpsGraphics2D context to the point (x,y) in | |
761 | * the current coordinate system. | |
762 | */ | |
763 | 9530 | @Override |
764 | public void translate(int x, int y) | |
765 | { | |
766 | 9530 | translate((double) x, (double) y); |
767 | } | |
768 | ||
769 | /** | |
770 | * Concatenates the current EpsGraphics2D Transformation with a translation | |
771 | * transform. | |
772 | */ | |
773 | 9530 | @Override |
774 | public void translate(double tx, double ty) | |
775 | { | |
776 | 9530 | transform(AffineTransform.getTranslateInstance(tx, ty)); |
777 | } | |
778 | ||
779 | /** | |
780 | * Concatenates the current EpsGraphics2D Transform with a rotation transform. | |
781 | */ | |
782 | 0 | @Override |
783 | public void rotate(double theta) | |
784 | { | |
785 | 0 | rotate(theta, 0, 0); |
786 | } | |
787 | ||
788 | /** | |
789 | * Concatenates the current EpsGraphics2D Transform with a translated rotation | |
790 | * transform. | |
791 | */ | |
792 | 0 | @Override |
793 | public void rotate(double theta, double x, double y) | |
794 | { | |
795 | 0 | transform(AffineTransform.getRotateInstance(theta, x, y)); |
796 | } | |
797 | ||
798 | /** | |
799 | * Concatenates the current EpsGraphics2D Transform with a scaling | |
800 | * transformation. | |
801 | */ | |
802 | 0 | @Override |
803 | public void scale(double sx, double sy) | |
804 | { | |
805 | 0 | transform(AffineTransform.getScaleInstance(sx, sy)); |
806 | } | |
807 | ||
808 | /** | |
809 | * Concatenates the current EpsGraphics2D Transform with a shearing transform. | |
810 | */ | |
811 | 0 | @Override |
812 | public void shear(double shx, double shy) | |
813 | { | |
814 | 0 | transform(AffineTransform.getShearInstance(shx, shy)); |
815 | } | |
816 | ||
817 | /** | |
818 | * Composes an AffineTransform object with the Transform in this EpsGraphics2D | |
819 | * according to the rule last-specified-first-applied. | |
820 | */ | |
821 | 9530 | @Override |
822 | public void transform(AffineTransform Tx) | |
823 | { | |
824 | 9530 | _transform.concatenate(Tx); |
825 | 9530 | setTransform(getTransform()); |
826 | } | |
827 | ||
828 | /** | |
829 | * Sets the AffineTransform to be used by this EpsGraphics2D. | |
830 | */ | |
831 | 9530 | @Override |
832 | public void setTransform(AffineTransform Tx) | |
833 | { | |
834 | 9530 | if (Tx == null) |
835 | { | |
836 | 0 | _transform = new AffineTransform(); |
837 | } | |
838 | else | |
839 | { | |
840 | 9530 | _transform = new AffineTransform(Tx); |
841 | } | |
842 | // Need to update the stroke and font so they know the scale changed | |
843 | 9530 | setStroke(getStroke()); |
844 | 9530 | setFont(getFont()); |
845 | } | |
846 | ||
847 | /** | |
848 | * Gets the AffineTransform used by this EpsGraphics2D. | |
849 | */ | |
850 | 9530 | @Override |
851 | public AffineTransform getTransform() | |
852 | { | |
853 | 9530 | return new AffineTransform(_transform); |
854 | } | |
855 | ||
856 | /** | |
857 | * Returns the current Paint of the EpsGraphics2D object. | |
858 | */ | |
859 | 40 | @Override |
860 | public Paint getPaint() | |
861 | { | |
862 | 40 | return _paint; |
863 | } | |
864 | ||
865 | /** | |
866 | * returns the current Composite of the EpsGraphics2D object. | |
867 | */ | |
868 | 80 | @Override |
869 | public Composite getComposite() | |
870 | { | |
871 | 80 | return _composite; |
872 | } | |
873 | ||
874 | /** | |
875 | * Sets the background color to be used by the clearRect method. | |
876 | */ | |
877 | 0 | @Override |
878 | public void setBackground(Color color) | |
879 | { | |
880 | 0 | if (color == null) |
881 | { | |
882 | 0 | color = Color.black; |
883 | } | |
884 | 0 | _backgroundColor = color; |
885 | } | |
886 | ||
887 | /** | |
888 | * Gets the background color that is used by the clearRect method. | |
889 | */ | |
890 | 40 | @Override |
891 | public Color getBackground() | |
892 | { | |
893 | 40 | return _backgroundColor; |
894 | } | |
895 | ||
896 | /** | |
897 | * Returns the Stroke currently used. Guaranteed to be an instance of | |
898 | * BasicStroke. | |
899 | */ | |
900 | 9570 | @Override |
901 | public Stroke getStroke() | |
902 | { | |
903 | 9570 | return _stroke; |
904 | } | |
905 | ||
906 | /** | |
907 | * Intersects the current clip with the interior of the specified Shape and | |
908 | * sets the clip to the resulting intersection. | |
909 | */ | |
910 | 0 | @Override |
911 | public void clip(Shape s) | |
912 | { | |
913 | 0 | if (_clip == null) |
914 | { | |
915 | 0 | setClip(s); |
916 | } | |
917 | else | |
918 | { | |
919 | 0 | Area area = new Area(_clip); |
920 | 0 | area.intersect(new Area(s)); |
921 | 0 | setClip(area); |
922 | } | |
923 | } | |
924 | ||
925 | /** | |
926 | * Returns the FontRenderContext. | |
927 | */ | |
928 | 20 | @Override |
929 | public FontRenderContext getFontRenderContext() | |
930 | { | |
931 | 20 | return _fontRenderContext; |
932 | } | |
933 | ||
934 | // ///////////// Graphics methods /////////////////////// | |
935 | ||
936 | /** | |
937 | * Returns a new Graphics object that is identical to this EpsGraphics2D. | |
938 | */ | |
939 | 20 | @Override |
940 | public Graphics create() | |
941 | { | |
942 | 20 | return new EpsGraphics2D(this); |
943 | } | |
944 | ||
945 | /** | |
946 | * Returns an EpsGraphics2D object based on this Graphics object, but with a | |
947 | * new translation and clip area. | |
948 | */ | |
949 | 0 | @Override |
950 | public Graphics create(int x, int y, int width, int height) | |
951 | { | |
952 | 0 | Graphics g = create(); |
953 | 0 | g.translate(x, y); |
954 | 0 | g.clipRect(0, 0, width, height); |
955 | 0 | return g; |
956 | } | |
957 | ||
958 | /** | |
959 | * Returns the current Color. This will be a default value (black) until it is | |
960 | * changed using the setColor method. | |
961 | */ | |
962 | 40 | @Override |
963 | public Color getColor() | |
964 | { | |
965 | 40 | return _color; |
966 | } | |
967 | ||
968 | /** | |
969 | * Sets the Color to be used when drawing all future shapes, text, etc. | |
970 | */ | |
971 | 39380 | @Override |
972 | public void setColor(Color c) | |
973 | { | |
974 | 39380 | if (c == null) |
975 | { | |
976 | 0 | c = Color.black; |
977 | } | |
978 | 39380 | _color = c; |
979 | 39380 | append((c.getRed() / 255f) + " " + (c.getGreen() / 255f) + " " |
980 | + (c.getBlue() / 255f) + " setrgbcolor"); | |
981 | } | |
982 | ||
983 | /** | |
984 | * Sets the paint mode of this EpsGraphics2D object to overwrite the | |
985 | * destination EpsDocument with the current color. | |
986 | */ | |
987 | 0 | @Override |
988 | public void setPaintMode() | |
989 | { | |
990 | // Do nothing - paint mode is the only method supported anyway. | |
991 | } | |
992 | ||
993 | /** | |
994 | * <b><i><font color="red">Not implemented</font></i></b> - performs no | |
995 | * action. | |
996 | */ | |
997 | 0 | @Override |
998 | public void setXORMode(Color c1) | |
999 | { | |
1000 | 0 | methodNotSupported(); |
1001 | } | |
1002 | ||
1003 | /** | |
1004 | * Returns the Font currently being used. | |
1005 | */ | |
1006 | 42280 | @Override |
1007 | public Font getFont() | |
1008 | { | |
1009 | 42280 | return _font; |
1010 | } | |
1011 | ||
1012 | /** | |
1013 | * Sets the Font to be used in future text. | |
1014 | */ | |
1015 | 17570 | @Override |
1016 | public void setFont(Font font) | |
1017 | { | |
1018 | 17570 | if (font == null) |
1019 | { | |
1020 | 0 | font = Font.decode(null); |
1021 | } | |
1022 | 17570 | _font = font; |
1023 | 17570 | append("/" + _font.getPSName() + " findfont " + (_font.getSize()) |
1024 | + " scalefont setfont"); | |
1025 | } | |
1026 | ||
1027 | /** | |
1028 | * Gets the font metrics of the current font. | |
1029 | */ | |
1030 | 30 | @Override |
1031 | public FontMetrics getFontMetrics() | |
1032 | { | |
1033 | 30 | return getFontMetrics(getFont()); |
1034 | } | |
1035 | ||
1036 | /** | |
1037 | * Gets the font metrics for the specified font. | |
1038 | */ | |
1039 | 60 | @Override |
1040 | public FontMetrics getFontMetrics(Font f) | |
1041 | { | |
1042 | 60 | BufferedImage image = new BufferedImage(1, 1, |
1043 | BufferedImage.TYPE_INT_RGB); | |
1044 | 60 | Graphics g = image.getGraphics(); |
1045 | 60 | return g.getFontMetrics(f); |
1046 | } | |
1047 | ||
1048 | /** | |
1049 | * Returns the bounding rectangle of the current clipping area. | |
1050 | */ | |
1051 | 0 | @Override |
1052 | public Rectangle getClipBounds() | |
1053 | { | |
1054 | 0 | if (_clip == null) |
1055 | { | |
1056 | 0 | return null; |
1057 | } | |
1058 | 0 | Rectangle rect = getClip().getBounds(); |
1059 | 0 | return rect; |
1060 | } | |
1061 | ||
1062 | /** | |
1063 | * Intersects the current clip with the specified rectangle. | |
1064 | */ | |
1065 | 0 | @Override |
1066 | public void clipRect(int x, int y, int width, int height) | |
1067 | { | |
1068 | 0 | clip(new Rectangle(x, y, width, height)); |
1069 | } | |
1070 | ||
1071 | /** | |
1072 | * Sets the current clip to the rectangle specified by the given coordinates. | |
1073 | */ | |
1074 | 0 | @Override |
1075 | public void setClip(int x, int y, int width, int height) | |
1076 | { | |
1077 | 0 | setClip(new Rectangle(x, y, width, height)); |
1078 | } | |
1079 | ||
1080 | /** | |
1081 | * Gets the current clipping area. | |
1082 | */ | |
1083 | 40 | @Override |
1084 | public Shape getClip() | |
1085 | { | |
1086 | 40 | if (_clip == null) |
1087 | { | |
1088 | 40 | return null; |
1089 | } | |
1090 | else | |
1091 | { | |
1092 | 0 | try |
1093 | { | |
1094 | 0 | AffineTransform t = _transform.createInverse(); |
1095 | 0 | t.concatenate(_clipTransform); |
1096 | 0 | return t.createTransformedShape(_clip); |
1097 | } catch (Exception e) | |
1098 | { | |
1099 | 0 | throw new EpsException(MessageManager.formatMessage( |
1100 | "exception.eps_unable_to_get_inverse_matrix", new String[] | |
1101 | { _transform.toString() })); | |
1102 | } | |
1103 | } | |
1104 | } | |
1105 | ||
1106 | /** | |
1107 | * Sets the current clipping area to an arbitrary clip shape. | |
1108 | */ | |
1109 | 0 | @Override |
1110 | public void setClip(Shape clip) | |
1111 | { | |
1112 | 0 | if (clip != null) |
1113 | { | |
1114 | 0 | if (_document.isClipSet()) |
1115 | { | |
1116 | 0 | append("grestore"); |
1117 | 0 | append("gsave"); |
1118 | } | |
1119 | else | |
1120 | { | |
1121 | 0 | _document.setClipSet(true); |
1122 | 0 | append("gsave"); |
1123 | } | |
1124 | 0 | draw(clip, "clip"); |
1125 | 0 | _clip = clip; |
1126 | 0 | _clipTransform = (AffineTransform) _transform.clone(); |
1127 | } | |
1128 | else | |
1129 | { | |
1130 | 0 | if (_document.isClipSet()) |
1131 | { | |
1132 | 0 | append("grestore"); |
1133 | 0 | _document.setClipSet(false); |
1134 | } | |
1135 | 0 | _clip = null; |
1136 | } | |
1137 | } | |
1138 | ||
1139 | /** | |
1140 | * <b><i><font color="red">Not implemented</font></i></b> - performs no | |
1141 | * action. | |
1142 | */ | |
1143 | 0 | @Override |
1144 | public void copyArea(int x, int y, int width, int height, int dx, int dy) | |
1145 | { | |
1146 | 0 | methodNotSupported(); |
1147 | } | |
1148 | ||
1149 | /** | |
1150 | * Draws a straight line from (x1,y1) to (x2,y2). | |
1151 | */ | |
1152 | 350 | @Override |
1153 | public void drawLine(int x1, int y1, int x2, int y2) | |
1154 | { | |
1155 | 350 | Shape shape = new Line2D.Float(x1, y1, x2, y2); |
1156 | 350 | draw(shape); |
1157 | } | |
1158 | ||
1159 | /** | |
1160 | * Fills a rectangle with top-left corner placed at (x,y). | |
1161 | */ | |
1162 | 6660 | @Override |
1163 | public void fillRect(int x, int y, int width, int height) | |
1164 | { | |
1165 | 6660 | Shape shape = new Rectangle(x, y, width, height); |
1166 | 6660 | draw(shape, "fill"); |
1167 | } | |
1168 | ||
1169 | /** | |
1170 | * Draws a rectangle with top-left corner placed at (x,y). | |
1171 | */ | |
1172 | 0 | @Override |
1173 | public void drawRect(int x, int y, int width, int height) | |
1174 | { | |
1175 | 0 | Shape shape = new Rectangle(x, y, width, height); |
1176 | 0 | draw(shape); |
1177 | } | |
1178 | ||
1179 | /** | |
1180 | * Clears a rectangle with top-left corner placed at (x,y) using the current | |
1181 | * background color. | |
1182 | */ | |
1183 | 0 | @Override |
1184 | public void clearRect(int x, int y, int width, int height) | |
1185 | { | |
1186 | 0 | Color originalColor = getColor(); |
1187 | ||
1188 | 0 | setColor(getBackground()); |
1189 | 0 | Shape shape = new Rectangle(x, y, width, height); |
1190 | 0 | draw(shape, "fill"); |
1191 | ||
1192 | 0 | setColor(originalColor); |
1193 | } | |
1194 | ||
1195 | /** | |
1196 | * Draws a rounded rectangle. | |
1197 | */ | |
1198 | 0 | @Override |
1199 | public void drawRoundRect(int x, int y, int width, int height, | |
1200 | int arcWidth, int arcHeight) | |
1201 | { | |
1202 | 0 | Shape shape = new RoundRectangle2D.Float(x, y, width, height, arcWidth, |
1203 | arcHeight); | |
1204 | 0 | draw(shape); |
1205 | } | |
1206 | ||
1207 | /** | |
1208 | * Fills a rounded rectangle. | |
1209 | */ | |
1210 | 0 | @Override |
1211 | public void fillRoundRect(int x, int y, int width, int height, | |
1212 | int arcWidth, int arcHeight) | |
1213 | { | |
1214 | 0 | Shape shape = new RoundRectangle2D.Float(x, y, width, height, arcWidth, |
1215 | arcHeight); | |
1216 | 0 | draw(shape, "fill"); |
1217 | } | |
1218 | ||
1219 | /** | |
1220 | * Draws an oval. | |
1221 | */ | |
1222 | 0 | @Override |
1223 | public void drawOval(int x, int y, int width, int height) | |
1224 | { | |
1225 | 0 | Shape shape = new Ellipse2D.Float(x, y, width, height); |
1226 | 0 | draw(shape); |
1227 | } | |
1228 | ||
1229 | /** | |
1230 | * Fills an oval. | |
1231 | */ | |
1232 | 0 | @Override |
1233 | public void fillOval(int x, int y, int width, int height) | |
1234 | { | |
1235 | 0 | Shape shape = new Ellipse2D.Float(x, y, width, height); |
1236 | 0 | draw(shape, "fill"); |
1237 | } | |
1238 | ||
1239 | /** | |
1240 | * Draws an arc. | |
1241 | */ | |
1242 | 0 | @Override |
1243 | public void drawArc(int x, int y, int width, int height, int startAngle, | |
1244 | int arcAngle) | |
1245 | { | |
1246 | 0 | Shape shape = new Arc2D.Float(x, y, width, height, startAngle, arcAngle, |
1247 | Arc2D.OPEN); | |
1248 | 0 | draw(shape); |
1249 | } | |
1250 | ||
1251 | /** | |
1252 | * Fills an arc. | |
1253 | */ | |
1254 | 0 | @Override |
1255 | public void fillArc(int x, int y, int width, int height, int startAngle, | |
1256 | int arcAngle) | |
1257 | { | |
1258 | 0 | Shape shape = new Arc2D.Float(x, y, width, height, startAngle, arcAngle, |
1259 | Arc2D.PIE); | |
1260 | 0 | draw(shape, "fill"); |
1261 | } | |
1262 | ||
1263 | /** | |
1264 | * Draws a polyline. | |
1265 | */ | |
1266 | 0 | @Override |
1267 | public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) | |
1268 | { | |
1269 | 0 | if (nPoints > 0) |
1270 | { | |
1271 | 0 | GeneralPath path = new GeneralPath(); |
1272 | 0 | path.moveTo(xPoints[0], yPoints[0]); |
1273 | 0 | for (int i = 1; i < nPoints; i++) |
1274 | { | |
1275 | 0 | path.lineTo(xPoints[i], yPoints[i]); |
1276 | } | |
1277 | 0 | draw(path); |
1278 | } | |
1279 | } | |
1280 | ||
1281 | /** | |
1282 | * Draws a polygon made with the specified points. | |
1283 | */ | |
1284 | 0 | @Override |
1285 | public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) | |
1286 | { | |
1287 | 0 | Shape shape = new Polygon(xPoints, yPoints, nPoints); |
1288 | 0 | draw(shape); |
1289 | } | |
1290 | ||
1291 | /** | |
1292 | * Draws a polygon. | |
1293 | */ | |
1294 | 0 | @Override |
1295 | public void drawPolygon(Polygon p) | |
1296 | { | |
1297 | 0 | draw(p); |
1298 | } | |
1299 | ||
1300 | /** | |
1301 | * Fills a polygon made with the specified points. | |
1302 | */ | |
1303 | 0 | @Override |
1304 | public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) | |
1305 | { | |
1306 | 0 | Shape shape = new Polygon(xPoints, yPoints, nPoints); |
1307 | 0 | draw(shape, "fill"); |
1308 | } | |
1309 | ||
1310 | /** | |
1311 | * Fills a polygon. | |
1312 | */ | |
1313 | 0 | @Override |
1314 | public void fillPolygon(Polygon p) | |
1315 | { | |
1316 | 0 | draw(p, "fill"); |
1317 | } | |
1318 | ||
1319 | /** | |
1320 | * Draws the specified characters, starting from (x,y) | |
1321 | */ | |
1322 | 3980 | @Override |
1323 | public void drawChars(char[] data, int offset, int length, int x, int y) | |
1324 | { | |
1325 | 3980 | String string = new String(data, offset, length); |
1326 | 3980 | drawString(string, x, y); |
1327 | } | |
1328 | ||
1329 | /** | |
1330 | * Draws the specified bytes, starting from (x,y) | |
1331 | */ | |
1332 | 0 | @Override |
1333 | public void drawBytes(byte[] data, int offset, int length, int x, int y) | |
1334 | { | |
1335 | 0 | String string = new String(data, offset, length); |
1336 | 0 | drawString(string, x, y); |
1337 | } | |
1338 | ||
1339 | /** | |
1340 | * Draws an image. | |
1341 | */ | |
1342 | 0 | @Override |
1343 | public boolean drawImage(Image img, int x, int y, ImageObserver observer) | |
1344 | { | |
1345 | 0 | return drawImage(img, x, y, Color.white, observer); |
1346 | } | |
1347 | ||
1348 | /** | |
1349 | * Draws an image. | |
1350 | */ | |
1351 | 0 | @Override |
1352 | public boolean drawImage(Image img, int x, int y, int width, int height, | |
1353 | ImageObserver observer) | |
1354 | { | |
1355 | 0 | return drawImage(img, x, y, width, height, Color.white, observer); |
1356 | } | |
1357 | ||
1358 | /** | |
1359 | * Draws an image. | |
1360 | */ | |
1361 | 0 | @Override |
1362 | public boolean drawImage(Image img, int x, int y, Color bgcolor, | |
1363 | ImageObserver observer) | |
1364 | { | |
1365 | 0 | int width = img.getWidth(null); |
1366 | 0 | int height = img.getHeight(null); |
1367 | 0 | return drawImage(img, x, y, width, height, bgcolor, observer); |
1368 | } | |
1369 | ||
1370 | /** | |
1371 | * Draws an image. | |
1372 | */ | |
1373 | 0 | @Override |
1374 | public boolean drawImage(Image img, int x, int y, int width, int height, | |
1375 | Color bgcolor, ImageObserver observer) | |
1376 | { | |
1377 | 0 | return drawImage(img, x, y, x + width, y + height, 0, 0, width, height, |
1378 | bgcolor, observer); | |
1379 | } | |
1380 | ||
1381 | /** | |
1382 | * Draws an image. | |
1383 | */ | |
1384 | 0 | @Override |
1385 | public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, | |
1386 | int sx1, int sy1, int sx2, int sy2, ImageObserver observer) | |
1387 | { | |
1388 | 0 | return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, |
1389 | Color.white, observer); | |
1390 | } | |
1391 | ||
1392 | /** | |
1393 | * Draws an image. | |
1394 | */ | |
1395 | 0 | @Override |
1396 | public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, | |
1397 | int sx1, int sy1, int sx2, int sy2, Color bgcolor, | |
1398 | ImageObserver observer) | |
1399 | { | |
1400 | 0 | if (dx1 >= dx2) |
1401 | { | |
1402 | 0 | throw new IllegalArgumentException("dx1 >= dx2"); |
1403 | } | |
1404 | 0 | if (sx1 >= sx2) |
1405 | { | |
1406 | 0 | throw new IllegalArgumentException("sx1 >= sx2"); |
1407 | } | |
1408 | 0 | if (dy1 >= dy2) |
1409 | { | |
1410 | 0 | throw new IllegalArgumentException("dy1 >= dy2"); |
1411 | } | |
1412 | 0 | if (sy1 >= sy2) |
1413 | { | |
1414 | 0 | throw new IllegalArgumentException("sy1 >= sy2"); |
1415 | } | |
1416 | ||
1417 | 0 | append("gsave"); |
1418 | ||
1419 | 0 | int width = sx2 - sx1; |
1420 | 0 | int height = sy2 - sy1; |
1421 | 0 | int destWidth = dx2 - dx1; |
1422 | 0 | int destHeight = dy2 - dy1; |
1423 | ||
1424 | 0 | int[] pixels = new int[width * height]; |
1425 | 0 | PixelGrabber pg = new PixelGrabber(img, sx1, sy1, sx2 - sx1, sy2 - sy1, |
1426 | pixels, 0, width); | |
1427 | 0 | try |
1428 | { | |
1429 | 0 | pg.grabPixels(); |
1430 | } catch (InterruptedException e) | |
1431 | { | |
1432 | 0 | return false; |
1433 | } | |
1434 | ||
1435 | 0 | AffineTransform matrix = new AffineTransform(_transform); |
1436 | 0 | matrix.translate(dx1, dy1); |
1437 | 0 | matrix.scale(destWidth / (double) width, destHeight / (double) height); |
1438 | 0 | double[] m = new double[6]; |
1439 | 0 | try |
1440 | { | |
1441 | 0 | matrix = matrix.createInverse(); |
1442 | } catch (Exception e) | |
1443 | { | |
1444 | 0 | throw new EpsException(MessageManager.formatMessage( |
1445 | "exception.eps_unable_to_get_inverse_matrix", new String[] | |
1446 | { matrix.toString() })); | |
1447 | } | |
1448 | 0 | matrix.scale(1, -1); |
1449 | 0 | matrix.getMatrix(m); |
1450 | 0 | append(width + " " + height + " 8 [" + m[0] + " " + m[1] + " " + m[2] |
1451 | + " " + m[3] + " " + m[4] + " " + m[5] + "]"); | |
1452 | // Fill the background to update the bounding box. | |
1453 | 0 | Color oldColor = getColor(); |
1454 | 0 | setColor(getBackground()); |
1455 | 0 | fillRect(dx1, dy1, destWidth, destHeight); |
1456 | 0 | setColor(oldColor); |
1457 | 0 | append("{currentfile 3 " + width |
1458 | + " mul string readhexstring pop} bind"); | |
1459 | 0 | append("false 3 colorimage"); |
1460 | 0 | StringBuffer line = new StringBuffer(); |
1461 | 0 | for (int y = 0; y < height; y++) |
1462 | { | |
1463 | 0 | for (int x = 0; x < width; x++) |
1464 | { | |
1465 | 0 | Color color = new Color(pixels[x + width * y]); |
1466 | 0 | line.append( |
1467 | toHexString(color.getRed()) + toHexString(color.getGreen()) | |
1468 | + toHexString(color.getBlue())); | |
1469 | 0 | if (line.length() > 64) |
1470 | { | |
1471 | 0 | append(line.toString()); |
1472 | 0 | line = new StringBuffer(); |
1473 | } | |
1474 | } | |
1475 | } | |
1476 | 0 | if (line.length() > 0) |
1477 | { | |
1478 | 0 | append(line.toString()); |
1479 | } | |
1480 | ||
1481 | 0 | append("grestore"); |
1482 | ||
1483 | 0 | return true; |
1484 | } | |
1485 | ||
1486 | /** | |
1487 | * Disposes of all resources used by this EpsGraphics2D object. If this is the | |
1488 | * only remaining EpsGraphics2D instance pointing at a EpsDocument object, | |
1489 | * then the EpsDocument object shall become eligible for garbage collection. | |
1490 | */ | |
1491 | 10 | @Override |
1492 | public void dispose() | |
1493 | { | |
1494 | 10 | _document = null; |
1495 | } | |
1496 | ||
1497 | /* bsoares 2019-03-20 | |
1498 | * finalize is now deprecated. Implementing AutoCloseable instead | |
1499 | /** | |
1500 | * Finalizes the object. | |
1501 | @Override | |
1502 | public void finalize() | |
1503 | { | |
1504 | super.finalize(); | |
1505 | } | |
1506 | */ | |
1507 | ||
1508 | /** | |
1509 | * Returns the entire contents of the EPS document, complete with headers and | |
1510 | * bounding box. The returned String is suitable for being written directly to | |
1511 | * disk as an EPS file. | |
1512 | */ | |
1513 | 0 | @Override |
1514 | public String toString() | |
1515 | { | |
1516 | 0 | StringWriter writer = new StringWriter(); |
1517 | 0 | try |
1518 | { | |
1519 | 0 | _document.write(writer); |
1520 | 0 | _document.flush(); |
1521 | 0 | _document.close(); |
1522 | } catch (IOException e) | |
1523 | { | |
1524 | 0 | throw new EpsException(e.toString()); |
1525 | } | |
1526 | 0 | return writer.toString(); |
1527 | } | |
1528 | ||
1529 | /** | |
1530 | * Returns true if the specified rectangular area might intersect the current | |
1531 | * clipping area. | |
1532 | */ | |
1533 | 0 | @Override |
1534 | public boolean hitClip(int x, int y, int width, int height) | |
1535 | { | |
1536 | 0 | if (_clip == null) |
1537 | { | |
1538 | 0 | return true; |
1539 | } | |
1540 | 0 | Rectangle rect = new Rectangle(x, y, width, height); |
1541 | 0 | return hit(rect, _clip, true); |
1542 | } | |
1543 | ||
1544 | /** | |
1545 | * Returns the bounding rectangle of the current clipping area. | |
1546 | */ | |
1547 | 0 | @Override |
1548 | public Rectangle getClipBounds(Rectangle r) | |
1549 | { | |
1550 | 0 | if (_clip == null) |
1551 | { | |
1552 | 0 | return r; |
1553 | } | |
1554 | 0 | Rectangle rect = getClipBounds(); |
1555 | 0 | r.setLocation((int) rect.getX(), (int) rect.getY()); |
1556 | 0 | r.setSize((int) rect.getWidth(), (int) rect.getHeight()); |
1557 | 0 | return r; |
1558 | } | |
1559 | ||
1560 | private Color _color; | |
1561 | ||
1562 | private Color _backgroundColor; | |
1563 | ||
1564 | private Paint _paint; | |
1565 | ||
1566 | private Composite _composite; | |
1567 | ||
1568 | private BasicStroke _stroke; | |
1569 | ||
1570 | private Font _font; | |
1571 | ||
1572 | private Shape _clip; | |
1573 | ||
1574 | private AffineTransform _clipTransform; | |
1575 | ||
1576 | private AffineTransform _transform; | |
1577 | ||
1578 | private boolean _accurateTextMode; | |
1579 | ||
1580 | private EpsDocument _document; | |
1581 | ||
1582 | private static FontRenderContext _fontRenderContext = new FontRenderContext( | |
1583 | null, false, true); | |
1584 | } |