View Javadoc

1   // Copyright 2000-2006 FreeHEP
2   package org.freehep.graphicsio.swf;
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.Rectangle;
14  import java.awt.Shape;
15  import java.awt.Stroke;
16  import java.awt.TexturePaint;
17  import java.awt.font.GlyphVector;
18  import java.awt.geom.AffineTransform;
19  import java.awt.geom.Area;
20  import java.awt.geom.Rectangle2D;
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.Properties;
30  import java.util.Vector;
31  
32  import org.freehep.graphics2d.PrintColor;
33  import org.freehep.graphics2d.VectorGraphics;
34  import org.freehep.graphics2d.font.FontEncoder;
35  import org.freehep.graphicsio.AbstractVectorGraphicsIO;
36  import org.freehep.graphicsio.ImageConstants;
37  import org.freehep.graphicsio.PageConstants;
38  import org.freehep.util.UserProperties;
39  import org.freehep.util.Value;
40  
41  /**
42   * SWF Graphics 2D driver.
43   * 
44   * @author Mark Donszelmann
45   * @version $Id: SWFGraphics2D.java 9981 2006-11-28 00:22:04Z duns $
46   */
47  public class SWFGraphics2D extends AbstractVectorGraphicsIO implements
48  		SWFConstants {
49  
50      private static final int SWF_VERSION = 8;       // for cyclic gradients
51  	private static final String rootKey = SWFGraphics2D.class.getName();
52  
53  	public static final String TRANSPARENT = rootKey + "."
54  			+ PageConstants.TRANSPARENT;
55  
56  	public static final String BACKGROUND = rootKey + "."
57  			+ PageConstants.BACKGROUND;
58  
59  	public static final String BACKGROUND_COLOR = rootKey + "."
60  			+ PageConstants.BACKGROUND_COLOR;
61  
62  	public static final String WRITE_IMAGES_AS = rootKey + "."
63  			+ ImageConstants.WRITE_IMAGES_AS;
64  
65  	private static final UserProperties defaultProperties = new UserProperties();
66  	static {
67  		defaultProperties.setProperty(TRANSPARENT, true);
68  		defaultProperties.setProperty(BACKGROUND, false);
69  		defaultProperties.setProperty(BACKGROUND_COLOR, Color.GRAY);
70  		defaultProperties.setProperty(WRITE_IMAGES_AS, ImageConstants.SMALLEST);
71  		// Seems not to work yet...
72  		defaultProperties.setProperty(CLIP, false);
73  		defaultProperties.setProperty(TEXT_AS_SHAPES, true);
74  	}
75  
76  	public static Properties getDefaultProperties() {
77  		return defaultProperties;
78  	}
79  
80  	public static void setDefaultProperties(Properties newProperties) {
81  		defaultProperties.setProperties(newProperties);
82  	}
83  
84  	public final static String version = "$Revision: 9981 $";
85  
86  	private OutputStream ros;
87  
88  	private SWFOutputStream os;
89  
90  	private Value id;
91  
92  	private Value depth;
93  
94  	private static final float frameRate = 20.0f;
95  
96  	private boolean compress = true;
97  
98  	private LineStyleArray lineStyles;
99  
100 	private FillStyleArray fillStyles;
101 
102 	// keeps the color for text, which cannot be a paint
103 	private Color textColor;
104 
105 	// keeps the last clip which has not been written yet, its ID and depth
106 	private Shape unwrittenClip = null;
107 
108 	private AffineTransform clipTransform = null;
109 
110 	private int clipID, clipDepthID, showClipID, showClipDepthID;
111 
112 	// for rendering shapes
113 	boolean fillStroke;
114 
115 	// for debugging
116 	private static final boolean showBounds = false;
117 
118 	/*
119 	 * ================================================================================
120 	 * Table of Contents: ------------------ 1. Constructors & Factory Methods
121 	 * 2. Document Settings 3. Header, Trailer, Multipage & Comments 3.1 Header &
122 	 * Trailer 3.2 MultipageDocument methods 4. Create & Dispose 5. Drawing
123 	 * Methods 5.1. shapes (draw/fill) 5.1.1. lines, rectangles, round
124 	 * rectangles 5.1.2. polylines, polygons 5.1.3. ovals, arcs 5.1.4. shapes
125 	 * 5.2. Images 5.3. Strings 6. Transformations 7. Clipping 8. Graphics State /
126 	 * Settings 8.1. stroke/linewidth 8.2. paint/color 8.3. font 8.4. rendering
127 	 * hints 9. Auxiliary 10. Private/Utility Methos
128 	 * ================================================================================
129 	 */
130 
131 	/*
132 	 * ================================================================================
133 	 * 1. Constructors & Factory Methods
134 	 * ================================================================================
135 	 */
136 	public SWFGraphics2D(File file, Dimension size)
137 			throws FileNotFoundException {
138 		this(new FileOutputStream(file), size);
139 	}
140 
141 	public SWFGraphics2D(File file, Component component)
142 			throws FileNotFoundException {
143 		this(new FileOutputStream(file), component);
144 	}
145 
146 	public SWFGraphics2D(OutputStream os, Dimension size) {
147 		super(size, true);
148 		init(os);
149 	}
150 
151 	public SWFGraphics2D(OutputStream os, Component component) {
152 		super(component, true);
153 		init(os);
154 	}
155 
156 	private void init(OutputStream os) {
157 		initProperties(getDefaultProperties());
158 		ros = os;
159 		id = new Value().set(1);
160 		depth = new Value().set(1);
161 		textColor = getColor();
162 		fillStroke = true;
163 	}
164 
165 	protected SWFGraphics2D(SWFGraphics2D graphics, boolean doRestoreOnDispose) {
166 		super(graphics, doRestoreOnDispose);
167 		// Create a graphics context from a given graphics context.
168 		// This constructor is used by the system to clone a given graphics
169 		// context.
170 		// doRestoreOnDispose is used to call writeGraphicsRestore(),
171 		// when the graphics context is being disposed off.
172 		os = graphics.os;
173 		id = graphics.id;
174 		depth = graphics.depth;
175 		lineStyles = new LineStyleArray();
176 		lineStyles.add(graphics.lineStyles.get(0));
177 		if (showBounds || isProperty(CLIP)) {
178 			lineStyles.add(graphics.lineStyles.get(1));
179 			lineStyles.add(graphics.lineStyles.get(2));
180 		}
181 		fillStyles = new FillStyleArray();
182 		fillStyles.add(graphics.fillStyles.get(0));
183 		textColor = graphics.textColor;
184 		fillStroke = graphics.fillStroke;
185 	}
186 
187 	/*
188 	 * ================================================================================ |
189 	 * 2. Document Settings
190 	 * ================================================================================
191 	 */
192 
193 	/*
194 	 * ================================================================================ |
195 	 * 3. Header, Trailer, Multipage & Comments
196 	 * ================================================================================
197 	 */
198 	/* 3.1 Header & Trailer */
199 	public void writeHeader() throws IOException {
200 		os = new SWFOutputStream(new BufferedOutputStream(ros), SWF_VERSION, getSize(),
201 				frameRate, compress);
202 
203 		String description = getCreator() + ":" + getClass().getName();
204 		if (!isDeviceIndependent()) {
205 			description += " " + version.substring(1, version.length() - 1);
206 		}
207 		// FIXME no way to write out a comment...
208 	}
209 
210 	public void writeBackground() throws IOException {
211 		if (isProperty(TRANSPARENT)) {
212 			setBackground(null);
213 		} else if (isProperty(BACKGROUND)) {
214 			setBackground(getPropertyColor(BACKGROUND_COLOR));
215 			clearRect(0.0, 0.0, getSize().width, getSize().height);
216 		} else {
217 			setBackground(getComponent() != null ? getComponent()
218 					.getBackground() : Color.WHITE);
219 			clearRect(0.0, 0.0, getSize().width, getSize().height);
220 		}
221 	}
222 
223 	public void writeTrailer() throws IOException {
224 		os.writeTag(new ShowFrame());
225 		os.writeTag(new End());
226 	}
227 
228 	public void closeStream() throws IOException {
229 		os.close();
230 	}
231 
232 	/* 3.2 MultipageDocument methods */
233 
234 	/*
235 	 * ================================================================================
236 	 * 4. Create & Dispose
237 	 * ================================================================================
238 	 */
239 
240 	public Graphics create() {
241 		// Create a new graphics context from the current one.
242 		try {
243 			// Save the current context for restore later.
244 			writeGraphicsSave();
245 		} catch (IOException e) {
246 			handleException(e);
247 		}
248 		// The correct graphics context should be created.
249 		return new SWFGraphics2D(this, true);
250 	}
251 
252 	public Graphics create(double x, double y, double width, double height) {
253 		// Create a new graphics context from the current one.
254 		try {
255 			// Save the current context for restore later.
256 			writeGraphicsSave();
257 		} catch (IOException e) {
258 			handleException(e);
259 		}
260 		// The correct graphics context should be created.
261 		VectorGraphics graphics = new SWFGraphics2D(this, true);
262 		graphics.translate(x, y);
263 		graphics.clipRect(0, 0, width, height);
264 		return graphics;
265 	}
266 
267 	protected void writeGraphicsSave() throws IOException {
268 	}
269 
270 	protected void writeGraphicsRestore() throws IOException {
271 		popStreamAndWriteClip();
272 	}
273 
274 	/*
275 	 * ================================================================================ |
276 	 * 5. Drawing Methods
277 	 * ================================================================================
278 	 */
279 	/* 5.1.4. shapes */
280 
281 	public void draw(Shape shape) {
282 		Shape strokedShape = getStroke().createStrokedShape(shape);
283 		if (fillStroke) {
284 			// do this for dashed lines and non-round linejoins and linecaps
285 			fill(new Area(strokedShape));
286 			return;
287 		}
288 
289 		try {
290 			Rectangle2D bounds = strokedShape.getBounds2D();
291 			SWFShape swfShape = createShape(shape, 1, 0, -1);
292 			os.writeTag(new DefineShape4(id.getInt(), bounds, fillStyles,
293 					lineStyles, swfShape));
294 			os.writeTag(new PlaceObject2(id.getInt(), depth.getInt(),
295 					getTransform()));
296 			id.set(id.getInt() + 1);
297 			depth.set(depth.getInt() + 1);
298 
299 			if (showBounds) {
300 				SWFShape swfBounds = createShape(bounds, 2, 0, -1);
301 				os.writeTag(new DefineShape4(id.getInt(), bounds, fillStyles,
302 						lineStyles, swfBounds));
303 				os.writeTag(new PlaceObject2(id.getInt(), depth.getInt(),
304 						getTransform()));
305 				id.set(id.getInt() + 1);
306 				depth.set(depth.getInt() + 1);
307 			}
308 
309 		} catch (IOException e) {
310 			handleException(e);
311 		}
312 	}
313 
314 	public void fill(Shape shape) {
315 		try {
316 			Rectangle2D bounds = new BasicStroke().createStrokedShape(shape)
317 					.getBounds2D();
318 			SWFShape swfShape = createShape(shape, 0, 1, -1);
319 			os.writeTag(new DefineShape4(id.getInt(), bounds, fillStyles,
320 					lineStyles, swfShape));
321 			os.writeTag(new PlaceObject2(id.getInt(), depth.getInt(),
322 					getTransform()));
323 			id.set(id.getInt() + 1);
324 			depth.set(depth.getInt() + 1);
325 
326 			if (showBounds) {
327 				SWFShape swfBounds = createShape(bounds, 2, 0, -1);
328 				os.writeTag(new DefineShape4(id.getInt(), bounds, fillStyles,
329 						lineStyles, swfBounds));
330 				os.writeTag(new PlaceObject2(id.getInt(), depth.getInt(),
331 						getTransform()));
332 				id.set(id.getInt() + 1);
333 				depth.set(depth.getInt() + 1);
334 			}
335 
336 			boolean eo = SWFPathConstructor.isEvenOdd(shape);
337 			if (!eo) {
338 				writeWarning(getClass()
339 						+ ": cannot fill using non-zero winding rule, used even-odd instead.");
340 			}
341 		} catch (IOException e) {
342 			handleException(e);
343 		}
344 	}
345 
346 	public void fillAndDraw(Shape shape, Color fillColor) {
347 		try {
348 			setFillColor(fillColor);
349 			fill(shape);
350 			setFillColor(getColor());
351 			draw(shape);
352 		} catch (IOException e) {
353 			handleException(e);
354 		}
355 	}
356 
357 	/* 5.2. Images */
358 	public void copyArea(int x, int y, int width, int height, int dx, int dy) {
359 		writeWarning(getClass()
360 				+ ": copyArea(int, int, int, int, int, int) not implemented.");
361 		// Mostly unimplemented.
362 	}
363 
364 	protected void writeImage(RenderedImage image, AffineTransform xform,
365 			Color bkg) throws IOException {
366 
367 		// define image
368 		int imageID = id.getInt();
369 		os.writeTag(getImageTag(imageID, image, bkg));
370 		id.set(id.getInt() + 1);
371 
372 		// define shape for image
373 		Shape shape = xform.createTransformedShape(new Rectangle(0, 0, image
374 				.getWidth(), image.getHeight()));
375 
376 		AffineTransform imageTransform = new AffineTransform(TWIPS, 0, 0,
377 				TWIPS, 0, 0);
378 		xform.concatenate(imageTransform);
379 
380 		// create fill from shape
381 		SWFShape imageShape = createShape(shape, 0, 0, 1);
382 		FillStyleArray imageFill = new FillStyleArray();
383 		imageFill.add(new FillStyle(imageID, false, xform));
384 
385 		LineStyleArray imageLine = new LineStyleArray();
386 		Rectangle bounds = shape.getBounds();
387 		os.writeTag(new DefineShape4(id.getInt(), bounds, imageFill, imageLine,
388 				imageShape));
389 
390 		// place image
391 		os.writeTag(new PlaceObject2(id.getInt(), depth.getInt(),
392 				getTransform()));
393 		id.set(id.getInt() + 1);
394 		depth.set(depth.getInt() + 1);
395 
396 		if (showBounds) {
397 			SWFShape swfBounds = createShape(bounds, 2, 0, -1);
398 			os.writeTag(new DefineShape4(id.getInt(), bounds, fillStyles,
399 					lineStyles, swfBounds));
400 			os.writeTag(new PlaceObject2(id.getInt(), depth.getInt(),
401 					getTransform()));
402 			id.set(id.getInt() + 1);
403 			depth.set(depth.getInt() + 1);
404 		}
405 	}
406 
407 	private final static Properties replaceFonts = new Properties();
408 	static {
409 		replaceFonts.setProperty("Symbol", "Serif");
410 		replaceFonts.setProperty("ZapfDingbats", "Serif");
411 	}
412 
413 	/* 5.3. Strings */
414 	protected void writeString(String string, double x, double y)
415 			throws IOException {
416 		// for special fonts (Symbol, ZapfDingbats) we choose a standard font
417 		// and
418 		// encode using unicode.
419 		String fontName = getFont().getName();
420 		string = FontEncoder.getEncodedString(string, fontName);
421 		fontName = replaceFonts.getProperty(fontName, null);
422 
423 		Font font = (fontName == null) ? getFont() : new Font(fontName,
424 				getFont().getStyle(), getFont().getSize());
425 		Font font1024 = font.deriveFont((float) (1024.0 / TWIPS));
426 
427 		GlyphVector glyphs = font1024.createGlyphVector(getFontRenderContext(),
428 				string);
429 		Rectangle2D bounds = font.createGlyphVector(getFontRenderContext(),
430 				string).getVisualBounds();
431 		bounds.setRect(bounds.getX() + x, bounds.getY() + y, bounds.getWidth(),
432 				bounds.getHeight());
433 
434 		// define the Glyphs
435 		int fontID = id.getInt();
436 		DefineFont swfFont = new DefineFont(fontID);
437 		id.set(id.getInt() + 1);
438 
439 		// define Text
440 		Vector text = new Vector();
441 		DefineText2.RecordType1 record1 = new DefineText2.RecordType1(fontID,
442 				textColor, (int) (x * TWIPS), (int) (y * TWIPS), (int) (font
443 						.getSize2D() * TWIPS));
444 		DefineText2.RecordType0 record0 = new DefineText2.RecordType0();
445 		text.add(record1);
446 		text.add(record0);
447 		int textID = id.getInt();
448 		DefineText2 swfText = new DefineText2(textID, bounds,
449 				new AffineTransform(), text);
450 		id.set(id.getInt() + 1);
451 
452 		// hook font and text
453 		for (int i = 0; i < glyphs.getNumGlyphs(); i++) {
454 			// add filled shapes to font
455 			swfFont.add(createShape(glyphs.getGlyphOutline(i), -1, 1, -1));
456 
457 			// add glyphs to text
458 			// float advance = glyphs.getGlyphMetrics(i).getAdvance();
459 			record0.add(new DefineText2.GlyphEntry(i, 0)); // (int)(advance*TWIPS)));
460 		}
461 
462 		// write font and text
463 		os.writeTag(swfFont);
464 		os.writeTag(swfText);
465 
466 		// place String
467 		os.writeTag(new PlaceObject2(textID, depth.getInt(), getTransform()));
468 		depth.set(depth.getInt() + 1);
469 
470 		if (showBounds) {
471 			SWFShape swfBounds = createShape(bounds, 2, 0, -1);
472 			os.writeTag(new DefineShape4(id.getInt(), bounds, fillStyles,
473 					lineStyles, swfBounds));
474 			os.writeTag(new PlaceObject2(id.getInt(), depth.getInt(),
475 					getTransform()));
476 			id.set(id.getInt() + 1);
477 			depth.set(depth.getInt() + 1);
478 		}
479 	}
480 
481 	/*
482 	 * ================================================================================ |
483 	 * 6. Transformations
484 	 * ================================================================================
485 	 */
486 
487 	protected void writeTransform(AffineTransform t) throws IOException {
488 		// Transforms written when needed
489 	}
490 
491 	protected void writeSetTransform(AffineTransform t) throws IOException {
492 		// Transforms written when needed
493 	}
494 
495 	/*
496 	 * ================================================================================ |
497 	 * 7. Clipping
498 	 * ================================================================================
499 	 */
500 	protected void writeSetClip(Shape s) throws IOException {
501 		writeClip(s);
502 	}
503 
504 	protected void writeClip(Shape s) throws IOException {
505 		// we assume we can write nested clips
506 		popStreamAndWriteClip();
507 
508 		unwrittenClip = s;
509 		clipTransform = (unwrittenClip != null) ? new AffineTransform(
510 				getTransform()) : null;
511 
512 		if (unwrittenClip != null) {
513 			// reserve IDs
514 			clipID = id.getInt();
515 			id.set(id.getInt() + 1);
516 			clipDepthID = depth.getInt();
517 			depth.set(depth.getInt() + 1);
518 
519 			if (isProperty(CLIP)) {
520 				showClipID = id.getInt();
521 				id.set(id.getInt() + 1);
522 				showClipDepthID = depth.getInt();
523 				depth.set(depth.getInt() + 1);
524 			}
525 
526 			os.pushBuffer();
527 		} else {
528 			clipID = 0;
529 			clipDepthID = 0;
530 
531 			if (isProperty(CLIP)) {
532 				showClipID = 0;
533 				showClipDepthID = 0;
534 			}
535 		}
536 	}
537 
538 	/*
539 	 * ================================================================================ |
540 	 * 8. Graphics State
541 	 * ================================================================================
542 	 */
543 	protected void writeStroke(Stroke stroke) throws IOException {
544 		fillStroke = true;
545 		if (stroke instanceof BasicStroke) {
546 			BasicStroke bs = (BasicStroke) stroke;
547 			setPen(bs, getColor());
548 			if (bs.getLineWidth() == 0) {
549 				fillStroke = false;
550 			}
551 		}
552 	}
553 
554 	/* 8.2. paint/color */
555 	public void setPaintMode() {
556 		writeWarning(getClass() + ": setPaintMode() not implemented.");
557 		// Mostly unimplemented.
558 	}
559 
560 	public void setXORMode(Color c1) {
561 		writeWarning(getClass() + ": setXORMode(Color) not implemented.");
562 		// Mostly unimplemented.
563 	}
564 
565 	protected void writePaint(Color p) throws IOException {
566 		setPen((BasicStroke) getStroke(), p);
567 		setFillColor(p);
568 		textColor = PrintColor.createPrintColor(p);
569 	}
570 
571 	protected void writePaint(GradientPaint p) throws IOException {
572 		Gradient[] gradient = new Gradient[2];
573 		gradient[0] = new Gradient(0, p.getColor1());
574 		gradient[1] = new Gradient(255, p.getColor2());
575 		double x0 = p.getPoint1().getX();
576 		double y0 = p.getPoint1().getY();
577 		double dx = p.getPoint2().getX() - x0;
578 		double dy = p.getPoint2().getY() - y0;
579 		double scale = p.getPoint1().distance(p.getPoint2()) * TWIPS / 32768;
580 		double angle = Math.atan2(dy, dx);
581 		AffineTransform transform = new AffineTransform(scale, 0, 0, scale, dx
582 				/ 2 + x0, dy / 2 + y0);
583 		transform.rotate(angle);
584 
585         int spreadMode = p.isCyclic() ? FillStyle.SPREAD_MODE_REFLECT : FillStyle.SPREAD_MODE_PAD;
586 		fillStyles = new FillStyleArray();
587 		fillStyles.add(new FillStyle(gradient, FillStyle.LINEAR_GRADIENT, spreadMode, FillStyle.INTERPOLATION_MODE_NORMAL_RGB, 0.0f, transform));
588 
589 		textColor = PrintColor.mixColor(p.getColor1(), p.getColor2());
590 	}
591 
592 	protected void writePaint(TexturePaint p) throws IOException {
593 		// define image
594 		BufferedImage image = p.getImage();
595 		int imageID = id.getInt();
596 		os.writeTag(getImageTag(imageID, image, null));
597 		id.set(id.getInt() + 1);
598 
599 		// setup image as fill
600 		Rectangle2D a = p.getAnchorRect();
601 		double sx = a.getWidth() / image.getWidth();
602 		double sy = a.getHeight() / image.getHeight();
603 		fillStyles = new FillStyleArray();
604 		fillStyles.add(new FillStyle(imageID, true, new AffineTransform(sx
605 				* TWIPS, 0, 0, sy * TWIPS, a.getX(), a.getY())));
606 		textColor = PrintColor.black;
607 	}
608 
609 	protected void writePaint(Paint p) throws IOException {
610 		writeWarning(getClass() + ": writePaint(Paint) not implemented for "
611 				+ p.getClass());
612 		// Write out the paint.
613 		writePaint(Color.BLACK);
614 	}
615 
616 	/* 8.3. font */
617 	protected void writeFont(Font font) throws IOException {
618 		// written when needed
619 	}
620 
621 	/* 8.4. rendering hints */
622 
623 	/*
624 	 * ================================================================================ |
625 	 * 9. Auxiliary
626 	 * ================================================================================
627 	 */
628 	public GraphicsConfiguration getDeviceConfiguration() {
629 		writeWarning(getClass() + ": getDeviceConfiguration() not implemented.");
630 		// Mostly unimplemented
631 		return null;
632 	}
633 
634 	public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
635 		writeWarning(getClass()
636 				+ ": hit(Rectangle, Shape, boolean) not implemented.");
637 		// Mostly unimplemented
638 		return false;
639 	}
640 
641 	public void writeComment(String comment) throws IOException {
642 		writeWarning(getClass() + ": writeComment(String) not implemented.");
643 		// Write out the comment.
644 	}
645 
646 	public String toString() {
647 		return "SWFGraphics2D";
648 	}
649 
650 	/*
651 	 * Private methods
652 	 */
653 	SWFShape createShape(Shape shape, int stroke, int fill0, int fill1)
654 			throws IOException {
655 		// use a resolution compatible with the current transform
656 		AffineTransform t = getTransform();
657 		double resolution = 0.5 / (TWIPS * Math.min(t.getScaleX(), t
658 				.getScaleY()));
659 
660 		Vector path = new Vector();
661 		SWFPathConstructor pc = new SWFPathConstructor(path, stroke, fill0,
662 				fill1, resolution);
663 		pc.addPath(shape);
664 		return new SWFShape(path);
665 	}
666 
667 	private void setPen(BasicStroke stroke, Color color) throws IOException {
668 		lineStyles = new LineStyleArray();
669 		lineStyles.add(new LineStyle((int) (stroke.getLineWidth() * TWIPS), getPrintColor(color)));
670 
671 		if (showBounds || isProperty(CLIP)) {
672 			lineStyles.add(new LineStyle(TWIPS, Color.cyan));
673 			lineStyles.add(new LineStyle(TWIPS, Color.orange));
674 		}
675 	}
676 
677 	private void setFillColor(Color color) throws IOException {
678 		fillStyles = new FillStyleArray();
679 		fillStyles.add(new FillStyle(getPrintColor(color)));
680 	}
681 
682 	private void popStreamAndWriteClip() throws IOException {
683 		if (unwrittenClip == null)
684 			return;
685 
686 		if ((clipID == 0) || (clipDepthID == 0)) {
687 			System.err
688 					.println("SWFGraphics2D: internal error, invalid clipID or clipDepthID");
689 			return;
690 		}
691 
692 		// pop buffer
693 		os.popBuffer();
694 
695 		Rectangle2D bounds = unwrittenClip.getBounds2D();
696 		SWFShape clipShape = createShape(unwrittenClip, 0, 1, -1);
697 		os.writeTag(new DefineShape4(clipID, bounds, fillStyles, lineStyles,
698 				clipShape));
699 
700 		int clipDepth = depth.getInt() - 1;
701 		os.writeTag(new PlaceObject2(clipID, clipDepthID, clipTransform,
702 				clipDepth));
703 
704 		// System.out.println("Clip "+clipID+" at depth "+clipDepthID+" for
705 		// clipDepth "+clipDepth+": "+unwrittenClip);
706 
707 		if (isProperty(CLIP)) {
708 			if ((showClipID == 0) || (showClipDepthID == 0)) {
709 				System.err
710 						.println("SWFGraphics2D: internal error, invalid showClipID or showClipDepthID");
711 				return;
712 			}
713 
714 			SWFShape swfBounds = createShape(bounds, 3, 0, -1);
715 			os.writeTag(new DefineShape4(showClipID, bounds, fillStyles,
716 					lineStyles, swfBounds));
717 			os.writeTag(new PlaceObject(showClipID, showClipDepthID,
718 					clipTransform));
719 		}
720 
721 		// append popped buffer;
722 		os.append();
723 	}
724 
725 	private SWFTag getImageTag(int imageID, RenderedImage image, Color bkg)
726 			throws IOException {
727 		String writeAs = getProperty(WRITE_IMAGES_AS);
728 
729 		DefineBitsLossless2 flateTag = null;
730 		if (writeAs.equals(ImageConstants.ZLIB)
731 				|| writeAs.equals(ImageConstants.SMALLEST)) {
732 			flateTag = new DefineBitsLossless2(imageID, image, bkg);
733 		}
734 
735 		DefineBitsJPEG3 jpgTag = null;
736 		if (writeAs.equals(ImageConstants.JPG)
737 				|| writeAs.equals(ImageConstants.SMALLEST)) {
738 			jpgTag = new DefineBitsJPEG3(imageID, image, bkg, new Properties());
739 		}
740 
741 		SWFTag imageTag;
742 		if (writeAs.equals(ImageConstants.ZLIB)) {
743 			imageTag = flateTag;
744 		} else if (writeAs.equals(ImageConstants.JPG)) {
745 			imageTag = jpgTag;
746 		} else {
747 			imageTag = (jpgTag.getLength() < 0.5 * flateTag.getLength()) ? (SWFTag) jpgTag
748 					: (SWFTag) flateTag;
749 		}
750 
751 		return imageTag;
752 	}
753 }