1
2 package org.freehep.graphicsio.emf;
3
4 import java.awt.BasicStroke;
5 import java.awt.Color;
6 import java.awt.Component;
7 import java.awt.Dimension;
8 import java.awt.Font;
9 import java.awt.GradientPaint;
10 import java.awt.Graphics;
11 import java.awt.GraphicsConfiguration;
12 import java.awt.Paint;
13 import java.awt.Point;
14 import java.awt.Rectangle;
15 import java.awt.Shape;
16 import java.awt.Stroke;
17 import java.awt.TexturePaint;
18 import java.awt.Toolkit;
19 import java.awt.geom.AffineTransform;
20 import java.awt.geom.GeneralPath;
21 import java.awt.image.BufferedImage;
22 import java.awt.image.RenderedImage;
23 import java.io.BufferedOutputStream;
24 import java.io.File;
25 import java.io.FileNotFoundException;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.OutputStream;
29 import java.util.HashMap;
30 import java.util.Map;
31 import java.util.Properties;
32
33 import org.freehep.graphics2d.PrintColor;
34 import org.freehep.graphics2d.VectorGraphics;
35 import org.freehep.graphics2d.font.FontEncoder;
36 import org.freehep.graphics2d.font.FontUtilities;
37 import org.freehep.graphicsio.AbstractVectorGraphicsIO;
38 import org.freehep.graphicsio.PageConstants;
39 import org.freehep.graphicsio.emf.gdi.AlphaBlend;
40 import org.freehep.graphicsio.emf.gdi.BeginPath;
41 import org.freehep.graphicsio.emf.gdi.CreateBrushIndirect;
42 import org.freehep.graphicsio.emf.gdi.DeleteObject;
43 import org.freehep.graphicsio.emf.gdi.EOF;
44 import org.freehep.graphicsio.emf.gdi.EndPath;
45 import org.freehep.graphicsio.emf.gdi.ExtCreateFontIndirectW;
46 import org.freehep.graphicsio.emf.gdi.ExtCreatePen;
47 import org.freehep.graphicsio.emf.gdi.ExtLogFontW;
48 import org.freehep.graphicsio.emf.gdi.ExtLogPen;
49 import org.freehep.graphicsio.emf.gdi.ExtTextOutW;
50 import org.freehep.graphicsio.emf.gdi.FillPath;
51 import org.freehep.graphicsio.emf.gdi.LogBrush32;
52 import org.freehep.graphicsio.emf.gdi.ModifyWorldTransform;
53 import org.freehep.graphicsio.emf.gdi.RestoreDC;
54 import org.freehep.graphicsio.emf.gdi.SaveDC;
55 import org.freehep.graphicsio.emf.gdi.SelectClipPath;
56 import org.freehep.graphicsio.emf.gdi.SelectObject;
57 import org.freehep.graphicsio.emf.gdi.SetBkMode;
58 import org.freehep.graphicsio.emf.gdi.SetMapMode;
59 import org.freehep.graphicsio.emf.gdi.SetMiterLimit;
60 import org.freehep.graphicsio.emf.gdi.SetPolyFillMode;
61 import org.freehep.graphicsio.emf.gdi.SetTextAlign;
62 import org.freehep.graphicsio.emf.gdi.SetTextColor;
63 import org.freehep.graphicsio.emf.gdi.SetViewportExtEx;
64 import org.freehep.graphicsio.emf.gdi.SetViewportOrgEx;
65 import org.freehep.graphicsio.emf.gdi.SetWindowExtEx;
66 import org.freehep.graphicsio.emf.gdi.SetWindowOrgEx;
67 import org.freehep.graphicsio.emf.gdi.SetWorldTransform;
68 import org.freehep.graphicsio.emf.gdi.StrokeAndFillPath;
69 import org.freehep.graphicsio.emf.gdi.StrokePath;
70 import org.freehep.graphicsio.emf.gdi.TextW;
71 import org.freehep.graphicsio.font.FontTable;
72 import org.freehep.util.UserProperties;
73 import org.freehep.util.images.ImageUtilities;
74
75
76
77
78
79
80
81 public class EMFGraphics2D extends AbstractVectorGraphicsIO implements
82 EMFConstants {
83 public static final String version = "$Revision: 10516 $";
84
85 private EMFHandleManager handleManager;
86
87 private int penHandle;
88
89 private int brushHandle;
90
91 private Rectangle imageBounds;
92
93 private OutputStream ros;
94
95 private EMFOutputStream os;
96
97 private Color textColor = null;
98
99 private Color penColor = null;
100
101 private Color brushColor = null;
102
103 private Map fontTable;
104
105 private Map unitFontTable;
106
107 private EMFPathConstructor pathConstructor;
108
109 private boolean evenOdd;
110
111 private static final Rectangle dummy = new Rectangle(0, 0, 0, 0);
112
113
114
115
116
117
118
119
120
121
122
123
124
125 private static final String rootKey = EMFGraphics2D.class.getName();
126
127 public static final String TRANSPARENT = rootKey + "."
128 + PageConstants.TRANSPARENT;
129
130 public static final String BACKGROUND = rootKey + "."
131 + PageConstants.BACKGROUND;
132
133 public static final String BACKGROUND_COLOR = rootKey + "."
134 + PageConstants.BACKGROUND_COLOR;
135
136 private static final UserProperties defaultProperties = new UserProperties();
137 static {
138 defaultProperties.setProperty(TRANSPARENT, true);
139 defaultProperties.setProperty(BACKGROUND, false);
140 defaultProperties.setProperty(BACKGROUND_COLOR, Color.GRAY);
141 defaultProperties.setProperty(CLIP, true);
142
143 defaultProperties.setProperty(TEXT_AS_SHAPES, false);
144 }
145
146 public static Properties getDefaultProperties() {
147 return defaultProperties;
148 }
149
150 public static void setDefaultProperties(Properties newProperties) {
151 defaultProperties.setProperties(newProperties);
152 }
153
154
155
156
157
158
159 public EMFGraphics2D(File file, Dimension size)
160 throws FileNotFoundException {
161 this(new FileOutputStream(file), size);
162 }
163
164 public EMFGraphics2D(File file, Component component)
165 throws FileNotFoundException {
166 this(new FileOutputStream(file), component);
167 }
168
169 public EMFGraphics2D(OutputStream os, Dimension size) {
170 super(size, false);
171 this.imageBounds = new Rectangle(0, 0, size.width, size.height);
172 init(os);
173 }
174
175 public EMFGraphics2D(OutputStream os, Component component) {
176 super(component, false);
177 this.imageBounds = new Rectangle(0, 0, getSize().width,
178 getSize().height);
179 init(os);
180 }
181
182 private void init(OutputStream os) {
183 fontTable = new HashMap();
184 unitFontTable = new HashMap();
185 evenOdd = false;
186
187 handleManager = new EMFHandleManager();
188 ros = os;
189 initProperties(defaultProperties);
190 }
191
192 protected EMFGraphics2D(EMFGraphics2D graphics, boolean doRestoreOnDispose) {
193 super(graphics, doRestoreOnDispose);
194
195
196
197
198
199 os = graphics.os;
200 imageBounds = graphics.imageBounds;
201 handleManager = graphics.handleManager;
202 fontTable = graphics.fontTable;
203 unitFontTable = graphics.unitFontTable;
204 pathConstructor = graphics.pathConstructor;
205 evenOdd = graphics.evenOdd;
206 textColor = graphics.textColor;
207 penColor = graphics.penColor;
208 brushColor = graphics.brushColor;
209 }
210
211
212
213
214
215
216
217
218
219
220
221
222
223 public void writeHeader() throws IOException {
224 ros = new BufferedOutputStream(ros);
225 Dimension device = isDeviceIndependent() ? new Dimension(1024, 768)
226 : Toolkit.getDefaultToolkit().getScreenSize();
227 String producer = getClass().getName();
228 if (!isDeviceIndependent()) {
229 producer += " " + version.substring(1, version.length() - 1);
230 }
231 os = new EMFOutputStream(ros, imageBounds, handleManager, getCreator(),
232 producer, device);
233 pathConstructor = new EMFPathConstructor(os, imageBounds);
234
235 Point orig = new Point(imageBounds.x, imageBounds.y);
236 Dimension size = new Dimension(imageBounds.width, imageBounds.height);
237
238 os.writeTag(new SetMapMode(MM_ANISOTROPIC));
239 os.writeTag(new SetWindowOrgEx(orig));
240 os.writeTag(new SetWindowExtEx(size));
241 os.writeTag(new SetViewportOrgEx(orig));
242 os.writeTag(new SetViewportExtEx(size));
243 os.writeTag(new SetTextAlign(TA_BASELINE));
244 os.writeTag(new SetTextColor(getColor()));
245 os.writeTag(new SetPolyFillMode(EMFConstants.WINDING));
246
247 }
248
249 public void writeGraphicsState() throws IOException {
250 super.writeGraphicsState();
251
252
253 AffineTransform n = AffineTransform.getScaleInstance(1.0 / TWIPS,
254 1.0 / TWIPS);
255 os.writeTag(new SetWorldTransform(n));
256 }
257
258 public void writeBackground() throws IOException {
259 if (isProperty(TRANSPARENT)) {
260 setBackground(null);
261 os.writeTag(new SetBkMode(BKG_TRANSPARENT));
262 } else if (isProperty(BACKGROUND)) {
263 os.writeTag(new SetBkMode(BKG_OPAQUE));
264 setBackground(getPropertyColor(BACKGROUND_COLOR));
265 clearRect(0.0, 0.0, getSize().width, getSize().height);
266 } else {
267 os.writeTag(new SetBkMode(BKG_OPAQUE));
268 setBackground(getComponent() != null ? getComponent()
269 .getBackground() : Color.WHITE);
270 clearRect(0.0, 0.0, getSize().width, getSize().height);
271 }
272 }
273
274 public void writeTrailer() throws IOException {
275
276 for (;;) {
277 int handle = handleManager.highestHandleInUse();
278 if (handle < 0)
279 break;
280 os.writeTag(new DeleteObject(handle));
281 handleManager.freeHandle(handle);
282 }
283 os.writeTag(new EOF());
284 }
285
286 public void closeStream() throws IOException {
287 os.close();
288 }
289
290
291
292
293
294
295
296
297
298 public Graphics create() {
299
300 try {
301
302 writeGraphicsSave();
303 } catch (IOException e) {
304 handleException(e);
305 }
306
307 return new EMFGraphics2D(this, true);
308 }
309
310 public Graphics create(double x, double y, double width, double height) {
311
312 try {
313
314 writeGraphicsSave();
315 } catch (IOException e) {
316 handleException(e);
317 }
318
319 VectorGraphics graphics = new EMFGraphics2D(this, true);
320 graphics.translate(x, y);
321 graphics.clipRect(0, 0, width, height);
322 return graphics;
323 }
324
325 protected void writeGraphicsSave() throws IOException {
326 os.writeTag(new SaveDC());
327 }
328
329 protected void writeGraphicsRestore() throws IOException {
330 if (penHandle != 0)
331 os.writeTag(new DeleteObject(handleManager.freeHandle(penHandle)));
332 if (brushHandle != 0)
333 os
334 .writeTag(new DeleteObject(handleManager
335 .freeHandle(brushHandle)));
336 os.writeTag(new RestoreDC());
337 }
338
339
340
341
342
343
344
345 public void draw(Shape shape) {
346 try {
347 if (getStroke() instanceof BasicStroke) {
348 writePen((BasicStroke) getStroke(), getColor());
349 writePath(shape);
350 os.writeTag(new StrokePath(imageBounds));
351 } else {
352 writeBrush(getColor());
353 writePath(getStroke().createStrokedShape(shape));
354 os.writeTag(new FillPath(imageBounds));
355 }
356 } catch (IOException e) {
357 handleException(e);
358 }
359 }
360
361 public void fill(Shape shape) {
362 try {
363 if (getPaint() instanceof Color) {
364 writeBrush(getColor());
365 writePath(shape);
366 os.writeTag(new FillPath(imageBounds));
367 } else {
368
369 fill(shape, getPaint());
370 }
371 } catch (IOException e) {
372 handleException(e);
373 }
374 }
375
376 public void fillAndDraw(Shape shape, Color fillColor) {
377 try {
378 if (getPaint() instanceof Color) {
379 writePen((BasicStroke) getStroke(), getColor());
380 writeBrush(fillColor);
381 writePath(shape);
382 os.writeTag(new StrokeAndFillPath(imageBounds));
383 } else {
384
385 fill(shape, getPaint());
386
387 draw(shape);
388 }
389 } catch (IOException e) {
390 handleException(e);
391 }
392 }
393
394
395 public void copyArea(int x, int y, int width, int height, int dx, int dy) {
396 writeWarning(getClass()
397 + ": copyArea(int, int, int, int, int, int) not implemented.");
398
399 }
400
401
402
403 protected void writeImage(RenderedImage image, AffineTransform xform,
404 Color bkg) throws IOException {
405 os.writeTag(new SaveDC());
406
407 AffineTransform imageTransform = new AffineTransform(
408 1.0, 0.0, 0.0, -1.0, 0.0, image.getHeight());
409 imageTransform.preConcatenate(xform);
410 writeTransform(imageTransform);
411
412 BufferedImage bufferedImage = ImageUtilities.createBufferedImage(
413 image, null, null);
414 AlphaBlend alphaBlend = new AlphaBlend(
415 imageBounds,
416 toUnit(0),
417 toUnit(0),
418 toUnit(image.getWidth()),
419 toUnit(image.getHeight()),
420 new AffineTransform(),
421 bufferedImage,
422 bkg);
423
424 os.writeTag(alphaBlend);
425 os.writeTag(new RestoreDC());
426 }
427
428
429 public void writeString(String string, double x, double y)
430 throws IOException {
431
432 Color color;
433 Paint paint = getPaint();
434 if (paint instanceof Color) {
435 color = (Color) paint;
436 } else if (paint instanceof GradientPaint) {
437 GradientPaint gp = (GradientPaint) paint;
438 color = PrintColor.mixColor(gp.getColor1(), gp.getColor2());
439 } else {
440 Color bkg = getBackground();
441 if (bkg == null) {
442 color = Color.BLACK;
443 } else {
444 color = PrintColor.invert(bkg);
445 }
446 }
447 if (!color.equals(textColor)) {
448 textColor = color;
449 os.writeTag(new SetTextColor(textColor));
450 }
451
452
453 Map attributes = FontUtilities.getAttributes(getFont());
454 FontTable.normalize(attributes);
455 Font font = new Font(attributes);
456
457 Font unitFont = (Font) unitFontTable.get(font);
458
459 Integer fontIndex = (Integer) fontTable.get(font);
460 if (fontIndex == null) {
461
462
463
464 String fontName = font.getName();
465 string = FontEncoder.getEncodedString(string, fontName);
466
467 String windowsFontName = FontUtilities
468 .getWindowsFontName(fontName);
469
470 unitFont = new Font(windowsFontName, font.getStyle(), font
471 .getSize());
472 unitFont = unitFont.deriveFont(font.getSize2D()
473 * UNITS_PER_PIXEL * TWIPS);
474 unitFontTable.put(font, unitFont);
475
476 ExtLogFontW logFontW = new ExtLogFontW(unitFont);
477 int handle = handleManager.getHandle();
478 os.writeTag(new ExtCreateFontIndirectW(handle, logFontW));
479
480 fontIndex = new Integer(handle);
481 fontTable.put(font, fontIndex);
482 }
483 os.writeTag(new SelectObject(fontIndex.intValue()));
484
485 int[] widths = new int[string.length()];
486 for (int i = 0; i < widths.length; i++) {
487 double w = unitFont.getStringBounds(string, i, i + 1,
488 getFontRenderContext()).getWidth();
489 widths[i] = (int) w;
490 }
491
492
493 translate(x, y);
494
495
496 AffineTransform t = font.getTransform();
497 if (!t.isIdentity()) {
498 writeGraphicsSave();
499 writeTransform(t);
500 }
501
502 TextW text = new TextW(new Point(0, 0), string, 0, dummy, widths);
503 os.writeTag(new ExtTextOutW(imageBounds, EMFConstants.GM_ADVANCED, 1, 1, text));
504
505
506 if (!t.isIdentity()) {
507 writeGraphicsRestore();
508 }
509
510
511 translate(-x, -y);
512 }
513
514
515
516
517
518
519 protected void writeTransform(AffineTransform t) throws IOException {
520 AffineTransform n = new AffineTransform(t.getScaleX(), t.getShearY(), t
521 .getShearX(), t.getScaleY(), t.getTranslateX()
522 * UNITS_PER_PIXEL * TWIPS, t.getTranslateY() * UNITS_PER_PIXEL
523 * TWIPS);
524 os.writeTag(new ModifyWorldTransform(n, EMFConstants.MWT_LEFTMULTIPLY));
525 }
526
527 protected void writeSetTransform(AffineTransform t) throws IOException {
528
529 AffineTransform n = AffineTransform.getScaleInstance(1.0/TWIPS, 1.0/TWIPS);
530 os.writeTag(new SetWorldTransform(n));
531
532 writeTransform(t);
533 }
534
535
536
537
538
539
540 protected void writeSetClip(Shape s) throws IOException {
541 if (!isProperty(CLIP)) {
542 return;
543 }
544
545
546 if (s == null && imageBounds != null) {
547 s = new Rectangle(imageBounds);
548 AffineTransform at = getTransform();
549 if (at != null) {
550 s = at.createTransformedShape(s);
551 }
552 }
553
554 writePath(s);
555 os.writeTag(new SelectClipPath(EMFConstants.RGN_COPY));
556 }
557
558 protected void writeClip(Shape s) throws IOException {
559
560 if (s == null || !isProperty(CLIP)) {
561 return;
562 }
563
564 writePath(s);
565 os.writeTag(new SelectClipPath(EMFConstants.RGN_AND));
566 }
567
568
569
570
571
572
573 public void writeStroke(Stroke stroke) throws IOException {
574 if (stroke instanceof BasicStroke) {
575 writePen((BasicStroke) stroke, getColor());
576 }
577 }
578
579
580 public void setPaintMode() {
581 writeWarning(getClass() + ": setPaintMode() not implemented.");
582
583 }
584
585 public void setXORMode(Color c1) {
586 writeWarning(getClass() + ": setXORMode(Color) not implemented.");
587
588 }
589
590 protected void writePaint(Color p) throws IOException {
591
592 }
593
594 protected void writePaint(GradientPaint p) throws IOException {
595
596 }
597
598 protected void writePaint(TexturePaint p) throws IOException {
599
600 }
601
602 protected void writePaint(Paint p) throws IOException {
603
604 }
605
606
607 protected void writeFont(Font font) throws IOException {
608
609 }
610
611
612
613
614
615
616
617
618 public GraphicsConfiguration getDeviceConfiguration() {
619 writeWarning(getClass() + ": getDeviceConfiguration() not implemented.");
620
621 return null;
622 }
623
624 public void writeComment(String comment) throws IOException {
625 writeWarning(getClass() + ": writeComment(String) not implemented.");
626
627 }
628
629 public String toString() {
630 return "EMFGraphics2D";
631 }
632
633
634
635
636
637 protected Shape createShape(double[] xPoints, double[] yPoints,
638 int nPoints, boolean close) {
639 GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
640 if (nPoints > 0) {
641 path.moveTo((float) xPoints[0], (float) yPoints[0]);
642 double lastX = xPoints[0];
643 double lastY = yPoints[0];
644 if (close && (Math.abs(xPoints[nPoints - 1] - lastX) < 1)
645 && (Math.abs(yPoints[nPoints - 1] - lastY) < 1)) {
646 nPoints--;
647 }
648 for (int i = 1; i < nPoints; i++) {
649 if ((Math.abs(xPoints[i] - lastX) > 1)
650 || (Math.abs(yPoints[i] - lastY) > 1)) {
651 path.lineTo((float) xPoints[i], (float) yPoints[i]);
652 lastX = xPoints[i];
653 lastY = yPoints[i];
654 }
655 }
656 if (close)
657 path.closePath();
658 }
659 return path;
660 }
661
662
663
664
665 private boolean writePath(Shape shape) throws IOException {
666 boolean eo = EMFPathConstructor.isEvenOdd(shape);
667 if (eo != evenOdd) {
668 evenOdd = eo;
669 os.writeTag(new SetPolyFillMode((evenOdd) ? EMFConstants.ALTERNATE
670 : EMFConstants.WINDING));
671 }
672 os.writeTag(new BeginPath());
673 pathConstructor.addPath(shape);
674 os.writeTag(new EndPath());
675 return evenOdd;
676 }
677
678 private void writePen(BasicStroke stroke, Color color) throws IOException {
679 if (color.equals(penColor) && stroke.equals(getStroke()))
680 return;
681 penColor = color;
682
683 int style = EMFConstants.PS_GEOMETRIC;
684
685 switch (stroke.getEndCap()) {
686 case BasicStroke.CAP_BUTT:
687 style |= EMFConstants.PS_ENDCAP_FLAT;
688 break;
689 case BasicStroke.CAP_ROUND:
690 style |= EMFConstants.PS_ENDCAP_ROUND;
691 break;
692 case BasicStroke.CAP_SQUARE:
693 style |= EMFConstants.PS_ENDCAP_SQUARE;
694 break;
695 }
696
697 switch (stroke.getLineJoin()) {
698 case BasicStroke.JOIN_MITER:
699 style |= EMFConstants.PS_JOIN_MITER;
700 break;
701 case BasicStroke.JOIN_ROUND:
702 style |= EMFConstants.PS_JOIN_ROUND;
703 break;
704 case BasicStroke.JOIN_BEVEL:
705 style |= EMFConstants.PS_JOIN_BEVEL;
706 break;
707 }
708
709
710
711 float[] dashArray = stroke.getDashArray();
712 int[] dash = new int[(dashArray != null) ? dashArray.length : 0];
713 style |= (dash.length == 0) ? EMFConstants.PS_SOLID
714 : EMFConstants.PS_USERSTYLE;
715 for (int i = 0; i < dash.length; i++) {
716 dash[i] = toUnit(dashArray[i]);
717 }
718
719 int brushStyle = (color.getAlpha() == 0) ? EMFConstants.BS_NULL
720 : EMFConstants.BS_SOLID;
721
722 ExtLogPen pen = new ExtLogPen(style, toUnit(stroke.getLineWidth()),
723 brushStyle, getPrintColor(color), 0, dash);
724 if (penHandle != 0) {
725 os.writeTag(new DeleteObject(penHandle));
726 } else {
727 penHandle = handleManager.getHandle();
728 }
729 os.writeTag(new ExtCreatePen(penHandle, pen));
730 os.writeTag(new SelectObject(penHandle));
731
732 if (!(getStroke() instanceof BasicStroke)
733 || (((BasicStroke) getStroke()).getMiterLimit() != stroke
734 .getMiterLimit())) {
735 os.writeTag(new SetMiterLimit(toUnit(stroke.getMiterLimit())));
736 }
737 }
738
739 private void writeBrush(Color color) throws IOException {
740 if (color.equals(brushColor))
741 return;
742 brushColor = color;
743
744 int brushStyle = (color.getAlpha() == 0) ? EMFConstants.BS_NULL
745 : EMFConstants.BS_SOLID;
746
747 LogBrush32 brush = new LogBrush32(brushStyle, getPrintColor(color), 0);
748 if (brushHandle != 0) {
749 os.writeTag(new DeleteObject(brushHandle));
750 } else {
751 brushHandle = handleManager.getHandle();
752 }
753 os.writeTag(new CreateBrushIndirect(brushHandle, brush));
754 os.writeTag(new SelectObject(brushHandle));
755 }
756
757 private int toUnit(double d) {
758 return (int) Math.floor(d * UNITS_PER_PIXEL * TWIPS);
759 }
760 }