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 * Standalone EMF renderer.
30 *
31 * @author Daniel Noll (daniel@nuix.com)
32 * @version $Id$
33 */
34 public class EMFRenderer {
35 private static final Logger logger = Logger.getLogger("org.freehep.graphicsio.emf");
36
37 /**
38 * Header read from the EMFInputStream
39 */
40 private EMFHeader header;
41
42 /**
43 * Each logical unit is mapped to one twentieth of a
44 * printer's point (1/1440 inch, also called a twip).
45 */
46 public static double TWIP_SCALE = 1d / 1440 * 254;
47
48 /**
49 * affect by all XXXTo methods, e.g. LinTo. ExtMoveTo creates the
50 * starting point. CloseFigure closes the figure.
51 */
52 private GeneralPath figure = null;
53
54 /**
55 * AffineTransform which is the base for all rendering
56 * operations.
57 */
58 private AffineTransform initialTransform;
59
60 /**
61 * origin of the emf window, set by SetWindowOrgEx
62 */
63 private Point windowOrigin = null;
64
65 /**
66 * origin of the emf viewport, set By SetViewportOrgEx
67 */
68 private Point viewportOrigin = null;
69
70 /**
71 * size of the emf window, set by SetWindowExtEx
72 */
73 private Dimension windowSize = null;
74
75 /**
76 * size of the emf viewport, set by SetViewportExtEx
77 */
78 private Dimension viewportSize = null;
79
80 /**
81 * The MM_ISOTROPIC mode ensures a 1:1 aspect ratio.
82 * The MM_ANISOTROPIC mode allows the x-coordinates
83 * and y-coordinates to be adjusted independently.
84 */
85 private boolean mapModeIsotropic = false;
86
87 /**
88 * AffineTransform defined by SetMapMode. Used for
89 * resizing the emf to propper device bounds.
90 */
91 private AffineTransform mapModeTransform =
92 AffineTransform.getScaleInstance(TWIP_SCALE, TWIP_SCALE);
93
94
95 /**
96 * clipping area which is the base for all rendering
97 * operations.
98 */
99 private Shape initialClip;
100
101 /**
102 * current Graphics2D to paint on. It is set during
103 * {@link #paint(java.awt.Graphics2D)}
104 */
105 private Graphics2D g2;
106
107 /**
108 * objects used by {@link org.freehep.graphicsio.emf.gdi.SelectObject}.
109 * The array is filled by CreateXXX functions, e.g.
110 * {@link org.freehep.graphicsio.emf.gdi.CreatePen}
111 */
112 private GDIObject[] gdiObjects = new GDIObject[256]; // TODO: Make this more flexible.
113
114 // Rendering state.
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 * color for simple text rendering
123 */
124 private Color textColor = Color.BLACK;
125
126 /**
127 * written by {@link org.freehep.graphicsio.emf.gdi.SetPolyFillMode} used by
128 * e.g. {@link org.freehep.graphicsio.emf.gdi.PolyPolygon16}
129 */
130 private int windingRule = GeneralPath.WIND_EVEN_ODD;
131
132 /**
133 * Defined by SetBkModes, either {@link EMFConstants#BKG_OPAQUE} or
134 * {@link EMFConstants#BKG_TRANSPARENT}. Used in
135 * {@link #fillAndDrawOrAppend(java.awt.Graphics2D, java.awt.Shape)}
136 */
137 private int bkMode = EMFConstants.BKG_OPAQUE;
138
139 /**
140 * The SetBkMode function affects the line styles for lines drawn using a
141 * pen created by the CreatePen function. SetBkMode does not affect lines
142 * drawn using a pen created by the ExtCreatePen function.
143 */
144 private boolean useCreatePen = true;
145
146 /**
147 * The miter length is defined as the distance from the intersection
148 * of the line walls on the inside of the join to the intersection of
149 * the line walls on the outside of the join. The miter limit is the
150 * maximum allowed ratio of the miter length to the line width.
151 */
152 private int meterLimit = 10;
153
154 /**
155 * The SetROP2 function sets the current foreground mix mode.
156 * Default is to use the pen.
157 */
158 private int rop2 = EMFConstants.R2_COPYPEN;
159
160 /**
161 * e.g. {@link Image#SCALE_SMOOTH} for rendering images
162 */
163 private int scaleMode = Image.SCALE_SMOOTH;
164
165 /**
166 * The brush origin is a pair of coordinates specifying the location of one
167 * pixel in the bitmap. The default brush origin coordinates are (0,0). For
168 * horizontal coordinates, the value 0 corresponds to the leftmost column
169 * of pixels; the width corresponds to the rightmost column. For vertical
170 * coordinates, the value 0 corresponds to the uppermost row of pixels;
171 * the height corresponds to the lowermost row.
172 */
173 private Point brushOrigin = new Point(0, 0);
174
175 /**
176 * stores the parsed tags. Filled by the constructor. Read by
177 * {@link #paint(java.awt.Graphics2D)}
178 */
179 private Vector tags = new Vector(0);
180
181 /**
182 * Created by BeginPath and closed by EndPath.
183 */
184 private GeneralPath path = null;
185
186 /**
187 * The transformations set by ModifyWorldTransform are redirected to
188 * that AffineTransform. They do not affect the current paint context,
189 * after BeginPath is called. Only the figures appended to path
190 * are transformed by this AffineTransform.
191 * BeginPath clears the transformation, ModifyWorldTransform changes ist.
192 */
193 private AffineTransform pathTransform = new AffineTransform();
194
195 /**
196 * {@link org.freehep.graphicsio.emf.gdi.SaveDC} stores
197 * an Instance of DC if saveDC is read. RestoreDC pops an object.
198 */
199 private Stack dcStack = new Stack();
200
201 /**
202 * default direction is counterclockwise
203 */
204 private int arcDirection = EMFConstants.AD_COUNTERCLOCKWISE;
205
206 /**
207 * Class the encapsulate the state of a Graphics2D object.
208 * Instances are store in dcStack by
209 * {@link org.freehep.graphicsio.emf.EMFRenderer#paint(java.awt.Graphics2D)}
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 * Constructs the renderer.
227 *
228 * @param is the input stream to read the EMF records from.
229 * @throws IOException if an error occurs reading the header.
230 */
231 public EMFRenderer(EMFInputStream is) throws IOException {
232 this.header = is.readHeader();
233
234 // read all tags
235 Tag tag;
236 while ((tag = is.readTag()) != null) {
237 tags.add(tag);
238 }
239 is.close();
240 }
241
242 /**
243 * Gets the size of a canvas which would be required to render the EMF.
244 *
245 * @return the size.
246 */
247 public Dimension getSize() {
248 return header.getBounds().getSize();
249 // TODO see the mapModePart of resetTransformation()
250 // if uncommented size is too small
251 /* Dimension bounds = header.getBounds().getSize();
252 return new Dimension(
253 (int)Math.ceil(bounds.width * TWIP_SCALE),
254 (int)Math.ceil(bounds.height * TWIP_SCALE));*/
255 }
256
257 /**
258 * Paints the EMF onto the provided graphics context.
259 *
260 * @param g2 the graphics context to paint onto.
261 */
262 public void paint(Graphics2D g2) {
263 this.g2 = g2;
264
265 // store at leat clip and transformation
266 Shape clip = g2.getClip();
267 AffineTransform at = g2.getTransform();
268 Map hints = g2.getRenderingHints();
269
270 // some quality settings
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 // used by SetWorldTransform to reset transformation
288 initialTransform = g2.getTransform();
289
290 // set the initial value, defaults for EMF
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 // apply all default settings
309 resetTransformation(g2);
310
311 // determin initial clip after all basic transformations
312 initialClip = g2.getClip();
313
314 // iterate and render all tags
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 // reset Transform and clip
326 g2.setRenderingHints(hints);
327 g2.setTransform(at);
328 g2.setClip(clip);
329 }
330
331 // ---------------------------------------------------------------------
332 // complex drawing methods for EMFTags
333 // ---------------------------------------------------------------------
334
335 /**
336 * set the initial transform, the windowOrigin and viewportOrigin,
337 * scales by viewportSize and windowSize
338 * @param g2 Context to apply transformations
339 */
340 private void resetTransformation(Graphics2D g2) {
341 // rest to device configuration
342 if (initialTransform != null) {
343 g2.setTransform(initialTransform);
344 } else {
345 g2.setTransform(new AffineTransform());
346 }
347
348 /* TODO mapModeTransform dows not work correctly
349 if (mapModeTransform != null) {
350 g2.transform(mapModeTransform);
351 }*/
352
353 // move to window origin
354 if (windowOrigin != null) {
355 g2.translate(
356 - windowOrigin.getX(),
357 - windowOrigin.getY());
358 }
359 // move to window origin
360 if (viewportOrigin != null) {
361 g2.translate(
362 - viewportOrigin.getX(),
363 - viewportOrigin.getY());
364 }
365
366 // TWIP_SCALE by window and viewport size
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 * Stores the current state. Used by
380 * {@link org.freehep.graphicsio.emf.gdi.SaveDC}
381 */
382 public void saveDC() {
383 // create a DC instance with current settings
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 // push it on top of the stack
397 dcStack.push(dc);
398 }
399
400 /**
401 * Retores a saved state. Used by
402 * {@link org.freehep.graphicsio.emf.gdi.RestoreDC}
403 */
404 public void retoreDC() {
405 // is somethoing stored?
406 if (!dcStack.empty()) {
407 // read it
408 DC dc = (DC) dcStack.pop();
409
410 // use it
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 // set the default values
424 }
425 }
426
427 /**
428 * closes and appends the current open figure to the
429 * path
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 * Logical units are mapped to arbitrary units with equally scaled axes;
447 * that is, one unit along the x-axis is equal to one unit along the y-axis.
448 * Use the SetWindowExtEx and SetViewportExtEx functions to specify the
449 * units and the orientation of the axes. Graphics device interface (GDI)
450 * makes adjustments as necessary to ensure the x and y units remain the
451 * same size (When the window extent is set, the viewport will be adjusted
452 * to keep the units isotropic).
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 * fills a shape using the brushPaint, penPaint and penStroke
466 * @param g2 Painting context
467 * @param s Shape to fill with current brush
468 */
469 private void fillAndDrawOrAppend(Graphics2D g2, Shape s) {
470 // don't draw, just append the shape if BeginPath
471 // has opened the path
472 if (!appendToPath(s)) {
473 // The SetBkMode function affects the line styles for lines drawn using a
474 // pen created by the CreatePen function. SetBkMode does not affect lines
475 // drawn using a pen created by the ExtCreatePen function.
476 if (useCreatePen) {
477 // OPAQUE Background is filled with the current background
478 // color before the text, hatched brush, or pen is drawn.
479 if (bkMode == EMFConstants.BKG_OPAQUE) {
480 fillShape(g2, s);
481 } else {
482 // TRANSPARENT Background remains untouched.
483 // TODO: if we really do nothing some drawings are incomplete
484 // this needs definitly a fix
485 fillShape(g2, s);
486 }
487 } else {
488 // always fill the background if ExtCreatePen is set
489 fillShape(g2, s);
490 }
491 drawShape(g2, s);
492 }
493 }
494
495 /**
496 * draws a shape using the penPaint and penStroke
497 * @param g2 Painting context
498 * @param s Shape to draw with current paen
499 */
500 private void drawOrAppend(Graphics2D g2, Shape s) {
501 // don't draw, just append the shape if BeginPath
502 // opens a GeneralPath
503 if (!appendToPath(s)) {
504 drawShape(g2, s);
505 }
506 }
507
508 /**
509 * draws the text
510 *
511 * @param text Text
512 * @param x x-Position
513 * @param y y-Position
514 */
515 public void drawOrAppendText(String text, double x, double y) {
516 // TODO: Use explicit widths to pixel-position each character, if present.
517 // TODO: Implement alignment properly. What we have already seems to work well enough.
518 // FontRenderContext frc = g2.getFontRenderContext();
519 // TextLayout layout = new TextLayout(str, g2.getFont(), frc);
520 // if ((textAlignMode & EMFConstants.TA_CENTER) != 0) {
521 // layout.draw(g2, x + (width - textWidth) / 2, y);
522 // } else if ((textAlignMode & EMFConstants.TA_RIGHT) != 0) {
523 // layout.draw(g2, x + width - textWidth, y);
524 // } else {
525 // layout.draw(g2, x, y);
526 // }
527
528 if (path != null) {
529 // do not use g2.drawString(str, x, y) to be aware of path
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 * Append the shape to the current path
543 *
544 * @param s Shape to fill with current brush
545 * @return true, if path was changed
546 */
547 private boolean appendToPath(Shape s) {
548 // don't draw, just append the shape if BeginPath
549 // opens a GeneralPath
550 if (path != null) {
551 // aplly transformation if set
552 if (pathTransform != null) {
553 s = pathTransform.createTransformedShape(s);
554 }
555 // append the shape
556 path.append(s, false);
557 // current path set
558 return true;
559 }
560 // current path not set
561 return false;
562 }
563
564 /**
565 * closes the path opened by {@link org.freehep.graphicsio.emf.gdi.BeginPath}
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 * fills a shape using the brushPaint, penPaint and penStroke.
579 * This method should only be called for path painting. It doesn't check for a
580 * current path.
581 *
582 * @param g2 Painting context
583 * @param s Shape to fill with current brush
584 */
585 private void fillShape(Graphics2D g2, Shape s) {
586 g2.setPaint(brushPaint);
587 g2.fill(s);
588 }
589
590 /**
591 * draws a shape using the penPaint and penStroke
592 * This method should only be called for path drawing. It doesn't check for a
593 * current path.
594 *
595 * @param g2 Painting context
596 * @param s Shape to draw with current pen
597 */
598 private void drawShape(Graphics2D g2, Shape s) {
599 g2.setStroke(penStroke);
600
601 // R2_BLACK Pixel is always 0.
602 if (rop2 == EMFConstants.R2_BLACK) {
603 g2.setComposite(AlphaComposite.SrcOver);
604 g2.setPaint(Color.black);
605 }
606 // R2_COPYPEN Pixel is the pen color.
607 else if (rop2 == EMFConstants.R2_COPYPEN) {
608 g2.setComposite(AlphaComposite.SrcOver);
609 g2.setPaint(penPaint);
610 }
611 // R2_NOP Pixel remains unchanged.
612 else if (rop2 == EMFConstants.R2_NOP) {
613 g2.setComposite(AlphaComposite.SrcOver);
614 g2.setPaint(penPaint);
615 }
616 // R2_WHITE Pixel is always 1.
617 else if (rop2 == EMFConstants.R2_WHITE) {
618 g2.setComposite(AlphaComposite.SrcOver);
619 g2.setPaint(Color.white);
620 }
621 // R2_NOTCOPYPEN Pixel is the inverse of the pen color.
622 else if (rop2 == EMFConstants.R2_NOTCOPYPEN) {
623 g2.setComposite(AlphaComposite.SrcOver);
624 // TODO: set at least inverted color if paint is a color
625 }
626 // R2_XORPEN Pixel is a combination of the colors
627 // in the pen and in the screen, but not in both.
628 else if (rop2 == EMFConstants.R2_XORPEN) {
629 g2.setComposite(AlphaComposite.Xor);
630 } else {
631 logger.warning("got unsupported ROP" + rop2);
632 // TODO:
633 //R2_MASKNOTPEN Pixel is a combination of the colors common to both the screen and the inverse of the pen.
634 //R2_MASKPEN Pixel is a combination of the colors common to both the pen and the screen.
635 //R2_MASKPENNOT Pixel is a combination of the colors common to both the pen and the inverse of the screen.
636 //R2_MERGENOTPEN Pixel is a combination of the screen color and the inverse of the pen color.
637 //R2_MERGEPEN Pixel is a combination of the pen color and the screen color.
638 //R2_MERGEPENNOT Pixel is a combination of the pen color and the inverse of the screen color.
639 //R2_NOT Pixel is the inverse of the screen color.
640 //R2_NOTCOPYPEN Pixel is the inverse of the pen color.
641 //R2_NOTMASKPEN Pixel is the inverse of the R2_MASKPEN color.
642 //R2_NOTMERGEPEN Pixel is the inverse of the R2_MERGEPEN color.
643 //R2_NOTXORPEN Pixel is the inverse of the R2_XORPEN color.
644 }
645
646 g2.draw(s);
647 }
648
649 // ---------------------------------------------------------------------
650 // simple wrapping methods to the painting context
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 // simple getter / setter methods
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 }