1 package org.freehep.graphicsio.emf;
2
3 import java.io.IOException;
4 import java.awt.Graphics2D;
5 import java.awt.Dimension;
6 import java.awt.Point;
7 import java.awt.BasicStroke;
8 import java.awt.Stroke;
9 import java.awt.Paint;
10 import java.awt.Color;
11 import java.awt.Shape;
12 import java.awt.AlphaComposite;
13 import java.awt.Image;
14 import java.awt.RenderingHints;
15 import java.awt.Font;
16 import java.awt.image.BufferedImage;
17 import java.awt.font.TextLayout;
18 import java.awt.geom.AffineTransform;
19 import java.awt.geom.GeneralPath;
20 import java.util.logging.Logger;
21 import java.util.Stack;
22 import java.util.Vector;
23 import java.util.Map;
24
25 import org.freehep.util.io.Tag;
26 import org.freehep.graphicsio.emf.gdi.GDIObject;
27
28
29
30
31
32
33
34 public class EMFRenderer {
35 private static final Logger logger = Logger.getLogger("org.freehep.graphicsio.emf");
36
37
38
39
40 private EMFHeader header;
41
42
43
44
45
46 public static double TWIP_SCALE = 1d / 1440 * 254;
47
48
49
50
51
52 private GeneralPath figure = null;
53
54
55
56
57
58 private AffineTransform initialTransform;
59
60
61
62
63 private Point windowOrigin = null;
64
65
66
67
68 private Point viewportOrigin = null;
69
70
71
72
73 private Dimension windowSize = null;
74
75
76
77
78 private Dimension viewportSize = null;
79
80
81
82
83
84
85 private boolean mapModeIsotropic = false;
86
87
88
89
90
91 private AffineTransform mapModeTransform =
92 AffineTransform.getScaleInstance(TWIP_SCALE, TWIP_SCALE);
93
94
95
96
97
98
99 private Shape initialClip;
100
101
102
103
104
105 private Graphics2D g2;
106
107
108
109
110
111
112 private GDIObject[] gdiObjects = new GDIObject[256];
113
114
115 private Paint brushPaint = new Color(0, 0, 0, 0);
116 private Paint penPaint = Color.BLACK;
117 private Stroke penStroke = new BasicStroke();
118
119 private int textAlignMode = 0;
120
121
122
123
124 private Color textColor = Color.BLACK;
125
126
127
128
129
130 private int windingRule = GeneralPath.WIND_EVEN_ODD;
131
132
133
134
135
136
137 private int bkMode = EMFConstants.BKG_OPAQUE;
138
139
140
141
142
143
144 private boolean useCreatePen = true;
145
146
147
148
149
150
151
152 private int meterLimit = 10;
153
154
155
156
157
158 private int rop2 = EMFConstants.R2_COPYPEN;
159
160
161
162
163 private int scaleMode = Image.SCALE_SMOOTH;
164
165
166
167
168
169
170
171
172
173 private Point brushOrigin = new Point(0, 0);
174
175
176
177
178
179 private Vector tags = new Vector(0);
180
181
182
183
184 private GeneralPath path = null;
185
186
187
188
189
190
191
192
193 private AffineTransform pathTransform = new AffineTransform();
194
195
196
197
198
199 private Stack dcStack = new Stack();
200
201
202
203
204 private int arcDirection = EMFConstants.AD_COUNTERCLOCKWISE;
205
206
207
208
209
210
211 private class DC {
212 private Paint paint;
213 private Stroke stroke;
214 private AffineTransform transform;
215 private Shape clip;
216 public GeneralPath path;
217 public int bkMode;
218 public int windingRule;
219 public int meterLimit;
220 public boolean useCreatePen;
221 public int scaleMode;
222 public AffineTransform pathTransform;
223 }
224
225
226
227
228
229
230
231 public EMFRenderer(EMFInputStream is) throws IOException {
232 this.header = is.readHeader();
233
234
235 Tag tag;
236 while ((tag = is.readTag()) != null) {
237 tags.add(tag);
238 }
239 is.close();
240 }
241
242
243
244
245
246
247 public Dimension getSize() {
248 return header.getBounds().getSize();
249
250
251
252
253
254
255 }
256
257
258
259
260
261
262 public void paint(Graphics2D g2) {
263 this.g2 = g2;
264
265
266 Shape clip = g2.getClip();
267 AffineTransform at = g2.getTransform();
268 Map hints = g2.getRenderingHints();
269
270
271 g2.setRenderingHint(
272 RenderingHints.KEY_RENDERING,
273 RenderingHints.VALUE_RENDER_QUALITY);
274 g2.setRenderingHint(
275 RenderingHints.KEY_ANTIALIASING,
276 RenderingHints.VALUE_ANTIALIAS_ON);
277 g2.setRenderingHint(
278 RenderingHints.KEY_FRACTIONALMETRICS,
279 RenderingHints.VALUE_FRACTIONALMETRICS_ON);
280 g2.setRenderingHint(
281 RenderingHints.KEY_INTERPOLATION,
282 RenderingHints.VALUE_INTERPOLATION_BICUBIC);
283 g2.setRenderingHint(
284 RenderingHints.KEY_TEXT_ANTIALIASING,
285 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
286
287
288 initialTransform = g2.getTransform();
289
290
291 path = null;
292 figure = null;
293 meterLimit = 10;
294 windingRule = GeneralPath.WIND_EVEN_ODD;
295 bkMode = EMFConstants.BKG_OPAQUE;
296 useCreatePen = true;
297 scaleMode = Image.SCALE_SMOOTH;
298
299 windowOrigin = null;
300 viewportOrigin = null;
301 windowSize = null;
302 viewportSize = null;
303
304 mapModeIsotropic = false;
305 mapModeTransform = AffineTransform.getScaleInstance(
306 TWIP_SCALE, TWIP_SCALE);
307
308
309 resetTransformation(g2);
310
311
312 initialClip = g2.getClip();
313
314
315 Tag tag;
316 for (int i = 0; i < tags.size(); i++) {
317 tag = (Tag) tags.get(i);
318 if (tag instanceof EMFTag) {
319 ((EMFTag) tags.get(i)).render(this);
320 } else {
321 logger.warning("unknown tag: " + tag);
322 }
323 }
324
325
326 g2.setRenderingHints(hints);
327 g2.setTransform(at);
328 g2.setClip(clip);
329 }
330
331
332
333
334
335
336
337
338
339
340 private void resetTransformation(Graphics2D g2) {
341
342 if (initialTransform != null) {
343 g2.setTransform(initialTransform);
344 } else {
345 g2.setTransform(new AffineTransform());
346 }
347
348
349
350
351
352
353
354 if (windowOrigin != null) {
355 g2.translate(
356 - windowOrigin.getX(),
357 - windowOrigin.getY());
358 }
359
360 if (viewportOrigin != null) {
361 g2.translate(
362 - viewportOrigin.getX(),
363 - viewportOrigin.getY());
364 }
365
366
367 if (viewportSize != null && windowSize != null) {
368 double scaleX =
369 viewportSize.getWidth() /
370 windowSize.getWidth();
371 double scaleY =
372 viewportSize.getHeight() /
373 windowSize.getHeight();
374 g2.scale(scaleX, scaleY);
375 }
376 }
377
378
379
380
381
382 public void saveDC() {
383
384 DC dc = new DC();
385 dc.paint = g2.getPaint();
386 dc.stroke = g2.getStroke();
387 dc.transform = g2.getTransform();
388 dc.pathTransform = pathTransform;
389 dc.clip = g2.getClip();
390 dc.path = path;
391 dc.meterLimit = meterLimit;
392 dc.windingRule = windingRule;
393 dc.bkMode = bkMode;
394 dc.useCreatePen = useCreatePen;
395 dc.scaleMode = scaleMode;
396
397 dcStack.push(dc);
398 }
399
400
401
402
403
404 public void retoreDC() {
405
406 if (!dcStack.empty()) {
407
408 DC dc = (DC) dcStack.pop();
409
410
411 meterLimit = dc.meterLimit;
412 windingRule = dc.windingRule;
413 path = dc.path;
414 bkMode = dc.bkMode;
415 useCreatePen = dc.useCreatePen;
416 scaleMode = dc.scaleMode;
417 pathTransform = dc.pathTransform;
418 g2.setPaint(dc.paint);
419 g2.setStroke(dc.stroke);
420 g2.setTransform(dc.transform);
421 g2.setClip(dc.clip);
422 } else {
423
424 }
425 }
426
427
428
429
430
431 public void closeFigure() {
432 if (figure == null) {
433 return;
434 }
435
436 try {
437 figure.closePath();
438 appendToPath(figure);
439 figure = null;
440 } catch (java.awt.geom.IllegalPathStateException e) {
441 logger.warning("no figure to close");
442 }
443 }
444
445
446
447
448
449
450
451
452
453
454 public void fixViewportSize() {
455 if (mapModeIsotropic && (windowSize != null && viewportSize != null)) {
456 viewportSize.setSize(
457 viewportSize.getWidth(),
458 viewportSize.getWidth() *
459 (windowSize.getHeight() / windowSize.getWidth())
460 );
461 }
462 }
463
464
465
466
467
468
469 private void fillAndDrawOrAppend(Graphics2D g2, Shape s) {
470
471
472 if (!appendToPath(s)) {
473
474
475
476 if (useCreatePen) {
477
478
479 if (bkMode == EMFConstants.BKG_OPAQUE) {
480 fillShape(g2, s);
481 } else {
482
483
484
485 fillShape(g2, s);
486 }
487 } else {
488
489 fillShape(g2, s);
490 }
491 drawShape(g2, s);
492 }
493 }
494
495
496
497
498
499
500 private void drawOrAppend(Graphics2D g2, Shape s) {
501
502
503 if (!appendToPath(s)) {
504 drawShape(g2, s);
505 }
506 }
507
508
509
510
511
512
513
514
515 public void drawOrAppendText(String text, double x, double y) {
516
517
518
519
520
521
522
523
524
525
526
527
528 if (path != null) {
529
530 TextLayout tl = new TextLayout(
531 text,
532 g2.getFont(),
533 g2.getFontRenderContext());
534 path.append(tl.getOutline(null), false);
535 } else {
536 g2.setPaint(textColor);
537 g2.drawString(text, (int)x, (int)y);
538 }
539 }
540
541
542
543
544
545
546
547 private boolean appendToPath(Shape s) {
548
549
550 if (path != null) {
551
552 if (pathTransform != null) {
553 s = pathTransform.createTransformedShape(s);
554 }
555
556 path.append(s, false);
557
558 return true;
559 }
560
561 return false;
562 }
563
564
565
566
567 public void closePath() {
568 if (path != null) {
569 try {
570 path.closePath();
571 } catch (java.awt.geom.IllegalPathStateException e) {
572 logger.warning("no figure to close");
573 }
574 }
575 }
576
577
578
579
580
581
582
583
584
585 private void fillShape(Graphics2D g2, Shape s) {
586 g2.setPaint(brushPaint);
587 g2.fill(s);
588 }
589
590
591
592
593
594
595
596
597
598 private void drawShape(Graphics2D g2, Shape s) {
599 g2.setStroke(penStroke);
600
601
602 if (rop2 == EMFConstants.R2_BLACK) {
603 g2.setComposite(AlphaComposite.SrcOver);
604 g2.setPaint(Color.black);
605 }
606
607 else if (rop2 == EMFConstants.R2_COPYPEN) {
608 g2.setComposite(AlphaComposite.SrcOver);
609 g2.setPaint(penPaint);
610 }
611
612 else if (rop2 == EMFConstants.R2_NOP) {
613 g2.setComposite(AlphaComposite.SrcOver);
614 g2.setPaint(penPaint);
615 }
616
617 else if (rop2 == EMFConstants.R2_WHITE) {
618 g2.setComposite(AlphaComposite.SrcOver);
619 g2.setPaint(Color.white);
620 }
621
622 else if (rop2 == EMFConstants.R2_NOTCOPYPEN) {
623 g2.setComposite(AlphaComposite.SrcOver);
624
625 }
626
627
628 else if (rop2 == EMFConstants.R2_XORPEN) {
629 g2.setComposite(AlphaComposite.Xor);
630 } else {
631 logger.warning("got unsupported ROP" + rop2);
632
633
634
635
636
637
638
639
640
641
642
643
644 }
645
646 g2.draw(s);
647 }
648
649
650
651
652
653 public void setFont(Font font) {
654 g2.setFont(font);
655 }
656
657 public AffineTransform getTransform() {
658 return g2.getTransform();
659 }
660
661 public void transform(AffineTransform transform) {
662 g2.transform(transform);
663 }
664
665 public void resetTransformation() {
666 resetTransformation(g2);
667 }
668
669 public void setTransform(AffineTransform at) {
670 g2.setTransform(at);
671 }
672
673 public void setClip(Shape shape) {
674 g2.setClip(shape);
675 }
676
677 public void clip(Shape shape) {
678 g2.clip(shape);
679 }
680
681 public Shape getClip() {
682 return g2.getClip();
683 }
684
685 public void drawImage(BufferedImage image, AffineTransform transform) {
686 g2.drawImage(image, transform, null);
687 }
688
689 public void drawImage(BufferedImage image, int x, int y, int width, int height) {
690 g2.drawImage(image, x, y, width, height, null);
691 }
692
693 public void drawShape(Shape shape) {
694 drawShape(g2, shape);
695 }
696
697 public void fillShape(Shape shape) {
698 fillShape(g2, shape);
699 }
700
701 public void fillAndDrawOrAppend(Shape s) {
702 fillAndDrawOrAppend(g2, s);
703 }
704
705 public void drawOrAppend(Shape s) {
706 drawOrAppend(g2, s);
707 }
708
709
710
711
712
713 public int getWindingRule() {
714 return windingRule;
715 }
716
717 public GeneralPath getFigure() {
718 return figure;
719 }
720
721 public void setFigure(GeneralPath figure) {
722 this.figure = figure;
723 }
724
725 public GeneralPath getPath() {
726 return path;
727 }
728
729 public void setPath(GeneralPath path) {
730 this.path = path;
731 }
732
733 public Shape getInitialClip() {
734 return initialClip;
735 }
736
737 public AffineTransform getPathTransform() {
738 return pathTransform;
739 }
740
741 public void setPathTransform(AffineTransform pathTransform) {
742 this.pathTransform = pathTransform;
743 }
744
745 public void setWindingRule(int windingRule) {
746 this.windingRule = windingRule;
747 }
748
749 public void setMapModeIsotropic(boolean mapModeIsotropic) {
750 this.mapModeIsotropic = mapModeIsotropic;
751 }
752
753 public AffineTransform getMapModeTransform() {
754 return mapModeTransform;
755 }
756
757 public void setMapModeTransform(AffineTransform mapModeTransform) {
758 this.mapModeTransform = mapModeTransform;
759 }
760
761 public void setWindowOrigin(Point windowOrigin) {
762 this.windowOrigin = windowOrigin;
763 }
764
765 public void setViewportOrigin(Point viewportOrigin) {
766 this.viewportOrigin = viewportOrigin;
767 }
768
769 public void setViewportSize(Dimension viewportSize) {
770 this.viewportSize = viewportSize;
771 fixViewportSize();
772 resetTransformation();
773 }
774
775 public void setWindowSize(Dimension windowSize) {
776 this.windowSize = windowSize;
777 fixViewportSize();
778 resetTransformation();
779 }
780
781 public GDIObject getGDIObject(int index) {
782 return gdiObjects[index];
783 }
784
785 public void storeGDIObject(int index, GDIObject tag) {
786 gdiObjects[index] = tag;
787 }
788
789 public void setUseCreatePen(boolean useCreatePen) {
790 this.useCreatePen = useCreatePen;
791 }
792
793 public void setPenPaint(Paint penPaint) {
794 this.penPaint = penPaint;
795 }
796
797 public Stroke getPenStroke() {
798 return penStroke;
799 }
800
801 public void setPenStroke(Stroke penStroke) {
802 this.penStroke = penStroke;
803 }
804
805 public void setBrushPaint(Paint brushPaint) {
806 this.brushPaint = brushPaint;
807 }
808
809 public float getMeterLimit() {
810 return meterLimit;
811 }
812
813 public void setMeterLimit(int meterLimit) {
814 this.meterLimit = meterLimit;
815 }
816
817 public void setTextColor(Color textColor) {
818 this.textColor = textColor;
819 }
820
821 public void setRop2(int rop2) {
822 this.rop2 = rop2;
823 }
824
825 public void setBkMode(int bkMode) {
826 this.bkMode = bkMode;
827 }
828
829 public int getTextAlignMode() {
830 return textAlignMode;
831 }
832
833 public void setTextAlignMode(int textAlignMode) {
834 this.textAlignMode = textAlignMode;
835 }
836
837 public void setScaleMode(int scaleMode) {
838 this.scaleMode = scaleMode;
839 }
840
841 public Point getBrushOrigin() {
842 return brushOrigin;
843 }
844
845 public void setBrushOrigin(Point brushOrigin) {
846 this.brushOrigin = brushOrigin;
847 }
848
849 public void setArcDirection(int arcDirection) {
850 this.arcDirection = arcDirection;
851 }
852
853 public int getArcDirection() {
854 return arcDirection;
855 }
856 }