KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > svggen > SVGGraphics2D


1 /*
2
3    Copyright 2001-2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.svggen;
19
20 import java.awt.BasicStroke JavaDoc;
21 import java.awt.Color JavaDoc;
22 import java.awt.Dimension JavaDoc;
23 import java.awt.Font JavaDoc;
24 import java.awt.FontMetrics JavaDoc;
25 import java.awt.Graphics JavaDoc;
26 import java.awt.Graphics2D JavaDoc;
27 import java.awt.GraphicsConfiguration JavaDoc;
28 import java.awt.Image JavaDoc;
29 import java.awt.Shape JavaDoc;
30 import java.awt.Stroke JavaDoc;
31 import java.awt.font.GlyphVector JavaDoc;
32 import java.awt.font.TextLayout JavaDoc;
33 import java.awt.geom.AffineTransform JavaDoc;
34 import java.awt.geom.NoninvertibleTransformException JavaDoc;
35 import java.awt.image.BufferedImage JavaDoc;
36 import java.awt.image.BufferedImageOp JavaDoc;
37 import java.awt.image.ImageObserver JavaDoc;
38 import java.awt.image.RenderedImage JavaDoc;
39 import java.awt.image.renderable.RenderableImage JavaDoc;
40 import java.io.FileOutputStream JavaDoc;
41 import java.io.IOException JavaDoc;
42 import java.io.OutputStreamWriter JavaDoc;
43 import java.io.Writer JavaDoc;
44 import java.text.AttributedCharacterIterator JavaDoc;
45
46 import org.apache.batik.ext.awt.g2d.AbstractGraphics2D;
47 import org.apache.batik.ext.awt.g2d.GraphicContext;
48 import org.apache.batik.util.XMLConstants;
49 import org.w3c.dom.Document JavaDoc;
50 import org.w3c.dom.DocumentFragment JavaDoc;
51 import org.w3c.dom.Element JavaDoc;
52 import org.w3c.dom.Node JavaDoc;
53
54 /**
55  * This implementation of the java.awt.Graphics2D abstract class
56  * allows users to generate SVG (Scalable Vector Graphics) content
57  * from Java code.
58  *
59  * SVGGraphics2D generates a DOM tree whose root is obtained through
60  * the getRoot method. Refer to the DOMTreeManager and DOMGroupManager
61  * documentation for details on the structure of the generated
62  * DOM tree.
63  *
64  * The SVGGraphics2D class can produce a DOM tree using any implementation
65  * that conforms to the W3C DOM 1.0 specification (see http://www.w3.org).
66  * At construction time, the SVGGraphics2D must be given a org.w3.dom.Document
67  * instance that is used as a factory to create the various nodes in the
68  * DOM tree it generates.
69  *
70  * The various graphic context attributes (e.g., AffineTransform,
71  * Paint) are managed by a GraphicContext object.
72  *
73  *
74  * @author <a HREF="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
75  * @version $Id: SVGGraphics2D.java,v 1.42 2005/04/02 12:58:17 deweese Exp $
76  * @see org.apache.batik.ext.awt.g2d.GraphicContext
77  * @see org.apache.batik.svggen.DOMTreeManager
78  * @see org.apache.batik.svggen.DOMGroupManager
79  * @see org.apache.batik.svggen.ImageHandler
80  * @see org.apache.batik.svggen.ExtensionHandler
81  * @see org.w3c.dom.Document
82  */

83 public class SVGGraphics2D extends AbstractGraphics2D
84     implements Cloneable JavaDoc, SVGSyntax, XMLConstants, ErrorConstants {
85     /*
86      * Constants definitions
87      */

88     public static final String JavaDoc DEFAULT_XML_ENCODING = "ISO-8859-1";
89
90     /**
91      * Controls the policy for grouping nodes. Once the number of attributes
92      * overridden by a child element is greater than DEFAULT_MAX_GC_OVERRIDES,
93      * a new group is created.
94      *
95      * @see org.apache.batik.svggen.DOMTreeManager
96      */

97     public static final int DEFAULT_MAX_GC_OVERRIDES = 3;
98
99
100     /**
101      * The DOMTreeManager manages the process of creating
102      * and growing the SVG tree. This SVGGraphics2D relies on
103      * the DOMTreeManager to process attributes based on the
104      * GraphicContext state and create groups when needed.
105      */

106     protected DOMTreeManager domTreeManager;
107
108     /**
109      * The DOMGroupManager manages additions to the current group
110      * node associated for this Graphics2D object. Once a group
111      * is complete, the group manager appends it to the DOM tree
112      * through the DOMTreeManager.
113      * Note that each SVGGraphics2D instance has its own DOMGroupManager
114      * (i.e., its own current group) but that all SVGGraphics2D
115      * originating from the same SVGGraphics2D through various
116      * createGraphics calls share the same DOMTreeManager.
117      */

118     protected DOMGroupManager domGroupManager;
119
120     /**
121      * Contains some information for SVG generation.
122      */

123     protected SVGGeneratorContext generatorCtx;
124
125     /**
126      * Used to convert Java 2D API Shape objects to equivalent SVG
127      * elements
128      */

129     protected SVGShape shapeConverter;
130
131     /**
132      * SVG Canvas size
133      */

134     protected Dimension JavaDoc svgCanvasSize;
135
136     /**
137      * Used to create proper font metrics
138      */

139     protected Graphics2D JavaDoc fmg;
140
141     {
142         BufferedImage JavaDoc bi
143             = new BufferedImage JavaDoc(1, 1, BufferedImage.TYPE_INT_ARGB);
144
145         fmg = bi.createGraphics();
146     }
147
148     /**
149      * @return SVG Canvas size, as set in the root svg element
150      */

151     public final Dimension JavaDoc getSVGCanvasSize(){
152         return svgCanvasSize;
153     }
154
155     /**
156      * Set the Canvas size, this is used to set the width and
157      * height attributes on the outermost 'svg' element.
158      * @param svgCanvasSize SVG Canvas size. May be null (equivalent
159      * to 100%, 100%)
160      */

161     public final void setSVGCanvasSize(Dimension JavaDoc svgCanvasSize) {
162         this.svgCanvasSize = new Dimension JavaDoc(svgCanvasSize);
163     }
164
165     /**
166      * @return the SVGGeneratorContext used by this SVGGraphics2D instance.
167      */

168     public final SVGGeneratorContext getGeneratorContext() {
169         return generatorCtx;
170     }
171
172     /**
173      * @return the SVGShape used by this SVGGraphics2D instance to
174      * turn Java2D shapes into SVG Shape objects.
175      */

176     public final SVGShape getShapeConverter() {
177         return shapeConverter;
178     }
179
180     /**
181      * @return the DOMTreeManager used by this SVGGraphics2D instance
182      */

183     public final DOMTreeManager getDOMTreeManager(){
184         return domTreeManager;
185     }
186
187     /**
188      * Set a DOM Tree manager for the SVGGraphics2D.
189      * @param treeMgr the new DOM Tree manager this SVGGraphics2D should use
190      */

191      protected final void setDOMTreeManager(DOMTreeManager treeMgr) {
192         this.domTreeManager = treeMgr;
193         generatorCtx.genericImageHandler.setDOMTreeManager(domTreeManager);
194     }
195
196      /**
197      * @return the DOMGroupManager used by this SVGGraphics2D instance
198      */

199     protected final DOMGroupManager getDOMGroupManager(){
200         return domGroupManager;
201     }
202
203     /**
204      * Set a new DOM Group manager for this SVGGraphics2D.
205      * @param groupMgr the new DOM Group manager this SVGGraphics2D should use
206      */

207      protected final void setDOMGroupManager(DOMGroupManager groupMgr) {
208     this.domGroupManager = groupMgr;
209     }
210
211     /**
212      * @return the Document used as a DOM object factory by this
213      * SVGGraphics2D instance
214      */

215     public final Document JavaDoc getDOMFactory(){
216         return generatorCtx.domFactory;
217     }
218
219     /**
220      * @return the ImageHandler used by this SVGGraphics2D instance
221      */

222     public final ImageHandler getImageHandler(){
223         return generatorCtx.imageHandler;
224     }
225
226     /**
227      * @return the GenericImageHandler used by this SVGGraphics2D instance
228      */

229     public final GenericImageHandler getGenericImageHandler(){
230         return generatorCtx.genericImageHandler;
231     }
232
233     /**
234      * @return the extension handler used by this SVGGraphics2D instance
235      */

236     public final ExtensionHandler getExtensionHandler(){
237         return generatorCtx.extensionHandler;
238     }
239
240     /**
241      * @param extensionHandler new extension handler this SVGGraphics2D
242      * should use
243      */

244     public final void setExtensionHandler(ExtensionHandler extensionHandler) {
245         generatorCtx.setExtensionHandler(extensionHandler);
246     }
247
248     /**
249      * @param domFactory Factory which will produce Elements for the DOM tree
250      * this Graphics2D generates.
251      * @exception SVGGraphics2DRuntimeException if domFactory is null.
252      */

253     public SVGGraphics2D(Document JavaDoc domFactory) {
254         this(SVGGeneratorContext.createDefault(domFactory), false);
255     }
256
257     /**
258      * @param domFactory Factory which will produce Elements for the DOM tree
259      * this Graphics2D generates.
260      * @param imageHandler defines how images are referenced in the
261      * generated SVG fragment
262      * @param extensionHandler defines how Java 2D API extensions map
263      * to SVG Nodes.
264      * @param textAsShapes if true, all text is turned into SVG shapes in the
265      * convertion. No SVG text is output.
266      *
267      * @exception SVGGraphics2DRuntimeException if domFactory is null.
268      */

269     public SVGGraphics2D(Document JavaDoc domFactory,
270                          ImageHandler imageHandler,
271                          ExtensionHandler extensionHandler,
272                          boolean textAsShapes) {
273         this(buildSVGGeneratorContext(domFactory,
274                                       imageHandler,
275                                       extensionHandler),
276              textAsShapes);
277     }
278
279     /**
280      * Helper method to create an <tt>SVGGeneratorContext</tt> from the
281      * constructor parameters.
282      */

283     public static SVGGeneratorContext
284         buildSVGGeneratorContext(Document JavaDoc domFactory,
285                                  ImageHandler imageHandler,
286                                  ExtensionHandler extensionHandler){
287
288         SVGGeneratorContext generatorCtx = new SVGGeneratorContext(domFactory);
289         generatorCtx.setIDGenerator(new SVGIDGenerator());
290         generatorCtx.setExtensionHandler(extensionHandler);
291         generatorCtx.setImageHandler(imageHandler);
292         generatorCtx.setStyleHandler(new DefaultStyleHandler());
293         generatorCtx.setComment("Generated by the Batik Graphics2D SVG Generator");
294         generatorCtx.setErrorHandler(new DefaultErrorHandler());
295
296         return generatorCtx;
297     }
298
299     /**
300      * Creates a new SVGGraphics2D object.
301      * @param generatorCtx the <code>SVGGeneratorContext</code> instance
302      * that will provide all useful information to the generator.
303      * @param textAsShapes if true, all text is turned into SVG shapes in the
304      * convertion. No SVG text is output.
305      *
306      * @exception SVGGraphics2DRuntimeException if generatorContext is null.
307      */

308     public SVGGraphics2D(SVGGeneratorContext generatorCtx,
309                          boolean textAsShapes) {
310         super(textAsShapes);
311
312         if (generatorCtx == null)
313             // no error handler here as we don't have the ctx...
314
throw new SVGGraphics2DRuntimeException(ERR_CONTEXT_NULL);
315
316         setGeneratorContext(generatorCtx);
317     }
318
319     /**
320      * Sets an non null <code>SVGGeneratorContext</code>.
321      */

322     protected void setGeneratorContext(SVGGeneratorContext generatorCtx) {
323         this.generatorCtx = generatorCtx;
324
325         this.gc = new GraphicContext(new AffineTransform JavaDoc());
326
327         SVGGeneratorContext.GraphicContextDefaults gcDefaults =
328             generatorCtx.getGraphicContextDefaults();
329
330         if(gcDefaults != null){
331             if(gcDefaults.getPaint() != null){
332                 gc.setPaint(gcDefaults.getPaint());
333             }
334             if(gcDefaults.getStroke() != null){
335                 gc.setStroke(gcDefaults.getStroke());
336             }
337             if(gcDefaults.getComposite() != null){
338                 gc.setComposite(gcDefaults.getComposite());
339             }
340             if(gcDefaults.getClip() != null){
341                 gc.setClip(gcDefaults.getClip());
342             }
343             if(gcDefaults.getRenderingHints() != null){
344                 gc.setRenderingHints(gcDefaults.getRenderingHints());
345             }
346             if(gcDefaults.getFont() != null){
347                 gc.setFont(gcDefaults.getFont());
348             }
349             if(gcDefaults.getBackground() != null){
350                 gc.setBackground(gcDefaults.getBackground());
351             }
352         }
353
354         this.shapeConverter = new SVGShape(generatorCtx);
355         this.domTreeManager = new DOMTreeManager(gc,
356                                                  generatorCtx,
357                                                  DEFAULT_MAX_GC_OVERRIDES);
358         this.domGroupManager = new DOMGroupManager(gc, domTreeManager);
359         this.domTreeManager.addGroupManager(domGroupManager);
360         generatorCtx.genericImageHandler.setDOMTreeManager(domTreeManager);
361     }
362
363     /**
364      * This constructor is used in create()
365      *
366      * @see #create
367      */

368     public SVGGraphics2D(SVGGraphics2D g) {
369         super(g);
370         this.generatorCtx = g.generatorCtx;
371         this.gc.validateTransformStack();
372         this.shapeConverter = g.shapeConverter;
373         this.domTreeManager = g.domTreeManager;
374         this.domGroupManager = new DOMGroupManager(this.gc, this.domTreeManager);
375         this.domTreeManager.addGroupManager(this.domGroupManager);
376     }
377
378     /**
379      * @param svgFileName name of the file where SVG content
380      * should be written
381      */

382     public void stream(String JavaDoc svgFileName) throws SVGGraphics2DIOException {
383         stream(svgFileName, false);
384     }
385
386     /**
387      * @param svgFileName name of the file where SVG content
388      * should be written
389      * @param useCss defines whether the output SVG should use CSS style
390      * properties as opposed to plain attributes.
391      */

392     public void stream(String JavaDoc svgFileName, boolean useCss)
393         throws SVGGraphics2DIOException {
394         try {
395             OutputStreamWriter JavaDoc writer =
396                 new OutputStreamWriter JavaDoc(new FileOutputStream JavaDoc(svgFileName),
397                                        DEFAULT_XML_ENCODING);
398             stream(writer, useCss);
399             writer.flush();
400             writer.close();
401         } catch (SVGGraphics2DIOException io) {
402             // this one as already been handled in stream(Writer, boolean)
403
// method => rethrow it in all cases
404
throw io;
405         } catch (IOException JavaDoc e) {
406             generatorCtx.errorHandler.
407                 handleError(new SVGGraphics2DIOException(e));
408         }
409     }
410
411     /**
412      * @param writer used to writer out the SVG content
413      */

414     public void stream(Writer JavaDoc writer) throws SVGGraphics2DIOException {
415         stream(writer, false);
416     }
417
418     /**
419      * @param writer used to writer out the SVG content
420      * @param useCss defines whether the output SVG should use CSS
421      * style properties as opposed to plain attributes.
422      */

423     public void stream(Writer JavaDoc writer, boolean useCss)
424         throws SVGGraphics2DIOException {
425         Element JavaDoc svgRoot = getRoot();
426         stream(svgRoot, writer, useCss);
427     }
428
429     /**
430      * @param svgRoot root element to stream out
431      */

432     public void stream(Element JavaDoc svgRoot, Writer JavaDoc writer)
433         throws SVGGraphics2DIOException {
434         stream(svgRoot, writer, false);
435     }
436
437     /**
438      * @param svgRoot root element to stream out
439      * @param writer output
440      * @param useCss defines whether the output SVG should use CSS style
441      * properties as opposed to plain attributes.
442      */

443     public void stream(Element JavaDoc svgRoot, Writer JavaDoc writer, boolean useCss)
444         throws SVGGraphics2DIOException {
445         Node JavaDoc rootParent = svgRoot.getParentNode();
446         Node JavaDoc nextSibling = svgRoot.getNextSibling();
447
448         try {
449             //
450
// Enforce that the default and xlink namespace
451
// declarations appear on the root element
452
//
453
svgRoot.setAttributeNS(XMLNS_NAMESPACE_URI,
454                                    XMLNS_PREFIX,
455                                    SVG_NAMESPACE_URI);
456
457             svgRoot.setAttributeNS(XMLNS_NAMESPACE_URI,
458                                    XMLNS_PREFIX + ":" + XLINK_PREFIX,
459                                    XLINK_NAMESPACE_URI);
460
461             DocumentFragment JavaDoc svgDocument =
462                 svgRoot.getOwnerDocument().createDocumentFragment();
463             svgDocument.appendChild(svgRoot);
464
465             if (useCss)
466                 SVGCSSStyler.style(svgDocument);
467
468             XmlWriter.writeXml(svgDocument, writer);
469             writer.flush();
470         } catch (SVGGraphics2DIOException e) {
471             // this catch prevents from catching an SVGGraphics2DIOException
472
// and wrapping it again in another SVGGraphics2DIOException
473
// as would do the second catch (XmlWriter throws SVGGraphics2DIO
474
// Exception but flush throws IOException)
475
generatorCtx.errorHandler.
476                 handleError(e);
477         } catch (IOException JavaDoc io) {
478             generatorCtx.errorHandler.
479                 handleError(new SVGGraphics2DIOException(io));
480         } finally {
481             // Restore the svgRoot to its original tree position
482
if (rootParent != null) {
483                 if (nextSibling == null) {
484                     rootParent.appendChild(svgRoot);
485                 } else {
486                     rootParent.insertBefore(svgRoot, nextSibling);
487                 }
488             }
489         }
490     }
491
492     /**
493      * Invoking this method will return a set of definition element that
494      * contain all the definitions referenced by the attributes generated by
495      * the various converters. This also resets the converters.
496      */

497     public java.util.List JavaDoc getDefinitionSet(){
498         return domTreeManager.getDefinitionSet();
499     }
500
501     /**
502      * Invoking this method will return a reference to the topLevelGroup
503      * Element managed by this object. It will also cause this object
504      * to start working with a new topLevelGroup.
505      *
506      * @return top level group
507      */

508     public Element JavaDoc getTopLevelGroup(){
509         return getTopLevelGroup(true);
510     }
511
512     /**
513      * Invoking this method will return a reference to the topLevelGroup
514      * Element managed by this object. It will also cause this object
515      * to start working with a new topLevelGroup.
516      *
517      * @param includeDefinitionSet if true, the definition set is included and
518      * the converters are reset (i.e., they start with an empty set
519      * of definitions).
520      * @return top level group
521      */

522     public Element JavaDoc getTopLevelGroup(boolean includeDefinitionSet){
523         return domTreeManager.getTopLevelGroup(includeDefinitionSet);
524     }
525
526     /**
527      * Sets the topLevelGroup to the input element. This will throw an exception
528      * if the input element is not of type 'g' or if it is null.
529      */

530     public void setTopLevelGroup(Element JavaDoc topLevelGroup){
531         domTreeManager.setTopLevelGroup(topLevelGroup);
532     }
533
534     /**
535      * @return the svg root node of the SVG document associated with this
536      * object
537      */

538     public Element JavaDoc getRoot(){
539         return getRoot(null);
540     }
541
542     /**
543      * This version of the getRoot method will append the input svgRoot
544      * and set its attributes.
545      *
546      * @param svgRoot an SVG element underwhich the content should
547      * be appended.
548      * @return the svg root node of the SVG document associated with
549      * this object.
550      */

551     public Element JavaDoc getRoot(Element JavaDoc svgRoot) {
552         svgRoot = domTreeManager.getRoot(svgRoot);
553         if (svgCanvasSize != null){
554             svgRoot.setAttributeNS(null, SVG_WIDTH_ATTRIBUTE,
555                                    "" + svgCanvasSize.width);
556             svgRoot.setAttributeNS(null, SVG_HEIGHT_ATTRIBUTE,
557                                    "" + svgCanvasSize.height);
558         }
559         return svgRoot;
560     }
561
562     /**
563      * Creates a new <code>Graphics</code> object that is
564      * a copy of this <code>Graphics</code> object.
565      * @return a new graphics context that is a copy of
566      * this graphics context.
567      */

568     public Graphics JavaDoc create(){
569         return new SVGGraphics2D(this);
570     }
571
572
573     /**
574      * Sets the paint mode of this graphics context to alternate between
575      * this graphics context's current color and the new specified color.
576      * This specifies that logical pixel operations are performed in the
577      * XOR mode, which alternates pixels between the current color and
578      * a specified XOR color.
579      * <p>
580      * When drawing operations are performed, pixels which are the
581      * current color are changed to the specified color, and vice versa.
582      * <p>
583      * Pixels that are of colors other than those two colors are changed
584      * in an unpredictable but reversible manner; if the same figure is
585      * drawn twice, then all pixels are restored to their original values.
586      * @param c1 the XOR alternation color
587      */

588     public void setXORMode(Color JavaDoc c1) {
589         generatorCtx.errorHandler.
590             handleError(new SVGGraphics2DRuntimeException(ERR_XOR));
591     }
592
593     /**
594      * Gets the font metrics for the specified font.
595      * @return the font metrics for the specified font.
596      * @param f the specified font
597      * @see java.awt.Graphics#getFont
598      * @see java.awt.FontMetrics
599      * @see java.awt.Graphics#getFontMetrics()
600      */

601     public FontMetrics JavaDoc getFontMetrics(Font JavaDoc f){
602         return fmg.getFontMetrics(f);
603     }
604
605     /**
606      * Copies an area of the component by a distance specified by
607      * <code>dx</code> and <code>dy</code>. From the point specified
608      * by <code>x</code> and <code>y</code>, this method
609      * copies downwards and to the right. To copy an area of the
610      * component to the left or upwards, specify a negative value for
611      * <code>dx</code> or <code>dy</code>.
612      * If a portion of the source rectangle lies outside the bounds
613      * of the component, or is obscured by another window or component,
614      * <code>copyArea</code> will be unable to copy the associated
615      * pixels. The area that is omitted can be refreshed by calling
616      * the component's <code>paint</code> method.
617      * @param x the <i>x</i> coordinate of the source rectangle.
618      * @param y the <i>y</i> coordinate of the source rectangle.
619      * @param width the width of the source rectangle.
620      * @param height the height of the source rectangle.
621      * @param dx the horizontal distance to copy the pixels.
622      * @param dy the vertical distance to copy the pixels.
623      */

624     public void copyArea(int x, int y, int width, int height,
625                          int dx, int dy){
626         // No-op
627
}
628
629     /**
630      * Draws as much of the specified image as is currently available.
631      * The image is drawn with its top-left corner at
632      * (<i>x</i>,&nbsp;<i>y</i>) in this graphics context's coordinate
633      * space. Transparent pixels in the image do not affect whatever
634      * pixels are already there.
635      * <p>
636      * This method returns immediately in all cases, even if the
637      * complete image has not yet been loaded, and it has not been dithered
638      * and converted for the current output device.
639      * <p>
640      * If the image has not yet been completely loaded, then
641      * <code>drawImage</code> returns <code>false</code>. As more of
642      * the image becomes available, the process that draws the image notifies
643      * the specified image observer.
644      * @param img the specified image to be drawn.
645      * @param x the <i>x</i> coordinate.
646      * @param y the <i>y</i> coordinate.
647      * @param observer object to be notified as more of
648      * the image is converted.
649      * @see java.awt.Image
650      * @see java.awt.image.ImageObserver
651      * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
652      */

653     public boolean drawImage(Image JavaDoc img, int x, int y,
654                              ImageObserver JavaDoc observer) {
655         Element JavaDoc imageElement =
656             getGenericImageHandler().createElement(getGeneratorContext());
657         AffineTransform JavaDoc xform = getGenericImageHandler().handleImage(
658                                                          img, imageElement,
659                                                          x, y,
660                                                          img.getWidth(null),
661                                                          img.getHeight(null),
662                                                          getGeneratorContext());
663         
664         if (xform == null) {
665             domGroupManager.addElement(imageElement);
666         } else {
667             AffineTransform JavaDoc inverseTransform = null;
668             try {
669                 inverseTransform = xform.createInverse();
670             } catch(NoninvertibleTransformException JavaDoc e) {
671                 // This should never happen since handleImage
672
// always returns invertible transform
673
throw new SVGGraphics2DRuntimeException(ERR_UNEXPECTED);
674             }
675             gc.transform(xform);
676             domGroupManager.addElement(imageElement);
677             gc.transform(inverseTransform);
678         }
679         return true;
680     }
681
682     /**
683      * Draws as much of the specified image as has already been scaled
684      * to fit inside the specified rectangle.
685      * <p>
686      * The image is drawn inside the specified rectangle of this
687      * graphics context's coordinate space, and is scaled if
688      * necessary. Transparent pixels do not affect whatever pixels
689      * are already there.
690      * <p>
691      * This method returns immediately in all cases, even if the
692      * entire image has not yet been scaled, dithered, and converted
693      * for the current output device.
694      * If the current output representation is not yet complete, then
695      * <code>drawImage</code> returns <code>false</code>. As more of
696      * the image becomes available, the process that draws the image notifies
697      * the image observer by calling its <code>imageUpdate</code> method.
698      * <p>
699      * A scaled version of an image will not necessarily be
700      * available immediately just because an unscaled version of the
701      * image has been constructed for this output device. Each size of
702      * the image may be cached separately and generated from the original
703      * data in a separate image production sequence.
704      * @param img the specified image to be drawn.
705      * @param x the <i>x</i> coordinate.
706      * @param y the <i>y</i> coordinate.
707      * @param width the width of the rectangle.
708      * @param height the height of the rectangle.
709      * @param observer object to be notified as more of
710      * the image is converted.
711      * @see java.awt.Image
712      * @see java.awt.image.ImageObserver
713      * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
714      */

715     public boolean drawImage(Image JavaDoc img, int x, int y,
716                              int width, int height,
717                              ImageObserver JavaDoc observer){
718         Element JavaDoc imageElement =
719             getGenericImageHandler().createElement(getGeneratorContext());
720         AffineTransform JavaDoc xform
721             = getGenericImageHandler().handleImage(
722                                        img, imageElement,
723                                        x, y,
724                                        width, height,
725                                        getGeneratorContext());
726         
727         if (xform == null) {
728             domGroupManager.addElement(imageElement);
729         } else {
730             AffineTransform JavaDoc inverseTransform = null;
731             try {
732                 inverseTransform = xform.createInverse();
733             } catch(NoninvertibleTransformException JavaDoc e) {
734                 // This should never happen since handleImage
735
// always returns invertible transform
736
throw new SVGGraphics2DRuntimeException(ERR_UNEXPECTED);
737             }
738             gc.transform(xform);
739             domGroupManager.addElement(imageElement);
740             gc.transform(inverseTransform);
741         }
742         return true;
743     }
744
745     /**
746      * Disposes of this graphics context and releases
747      * any system resources that it is using.
748      * A <code>Graphics</code> object cannot be used after
749      * <code>dispose</code>has been called.
750      * <p>
751      * When a Java program runs, a large number of <code>Graphics</code>
752      * objects can be created within a short time frame.
753      * Although the finalization process of the garbage collector
754      * also disposes of the same system resources, it is preferable
755      * to manually free the associated resources by calling this
756      * method rather than to rely on a finalization process which
757      * may not run to completion for a long period of time.
758      * <p>
759      * Graphics objects which are provided as arguments to the
760      * <code>paint</code> and <code>update</code> methods
761      * of components are automatically released by the system when
762      * those methods return. For efficiency, programmers should
763      * call <code>dispose</code> when finished using
764      * a <code>Graphics</code> object only if it was created
765      * directly from a component or another <code>Graphics</code> object.
766      * @see java.awt.Graphics#finalize
767      * @see java.awt.Component#paint
768      * @see java.awt.Component#update
769      * @see java.awt.Component#getGraphics
770      * @see java.awt.Graphics#create
771      */

772     public void dispose() {
773         this.domTreeManager.removeGroupManager(this.domGroupManager);
774     }
775
776     /**
777      * Strokes the outline of a <code>Shape</code> using the settings of the
778      * current <code>Graphics2D</code> context. The rendering attributes
779      * applied include the <code>Clip</code>, <code>Transform</code>,
780      * <code>Paint</code>, <code>Composite</code> and
781      * <code>Stroke</code> attributes.
782      * @param s the <code>Shape</code> to be rendered
783      * @see #setStroke
784      * @see #setPaint
785      * @see java.awt.Graphics#setColor
786      * @see #transform
787      * @see #setTransform
788      * @see #clip
789      * @see #setClip
790      * @see #setComposite
791      */

792     public void draw(Shape JavaDoc s) {
793         // Only BasicStroke can be converted to an SVG attribute equivalent.
794
// If the GraphicContext's Stroke is not an instance of BasicStroke,
795
// then the stroked outline is filled.
796
Stroke JavaDoc stroke = gc.getStroke();
797         if (stroke instanceof BasicStroke JavaDoc) {
798             Element JavaDoc svgShape = shapeConverter.toSVG(s);
799             if (svgShape != null) {
800                 domGroupManager.addElement(svgShape, DOMGroupManager.DRAW);
801             }
802         } else {
803             Shape JavaDoc strokedShape = stroke.createStrokedShape(s);
804             fill(strokedShape);
805         }
806     }
807
808
809     /**
810      * Renders an image, applying a transform from image space into user space
811      * before drawing.
812      * The transformation from user space into device space is done with
813      * the current <code>Transform</code> in the <code>Graphics2D</code>.
814      * The specified transformation is applied to the image before the
815      * transform attribute in the <code>Graphics2D</code> context is applied.
816      * The rendering attributes applied include the <code>Clip</code>,
817      * <code>Transform</code>, and <code>Composite</code> attributes.
818      * Note that no rendering is done if the specified transform is
819      * noninvertible.
820      * @param img the <code>Image</code> to be rendered
821      * @param xform the transformation from image space into user space
822      * @param obs the {@link ImageObserver}
823      * to be notified as more of the <code>Image</code>
824      * is converted
825      * @return <code>true</code> if the <code>Image</code> is
826      * fully loaded and completely rendered;
827      * <code>false</code> if the <code>Image</code> is still being loaded.
828      * @see #transform
829      * @see #setTransform
830      * @see #setComposite
831      * @see #clip
832      * @see #setClip
833      */

834     public boolean drawImage(Image JavaDoc img,
835                              AffineTransform JavaDoc xform,
836                              ImageObserver JavaDoc obs){
837         boolean retVal = true;
838
839         if (xform == null) {
840             retVal = drawImage(img, 0, 0, null);
841         } else if(xform.getDeterminant() != 0){
842             AffineTransform JavaDoc inverseTransform = null;
843             try{
844                 inverseTransform = xform.createInverse();
845             } catch(NoninvertibleTransformException JavaDoc e){
846                 // Should never happen since we checked the
847
// matrix determinant
848
throw new SVGGraphics2DRuntimeException(ERR_UNEXPECTED);
849             }
850
851             gc.transform(xform);
852             retVal = drawImage(img, 0, 0, null);
853             gc.transform(inverseTransform);
854         } else {
855             AffineTransform JavaDoc savTransform = new AffineTransform JavaDoc(gc.getTransform());
856             gc.transform(xform);
857             retVal = drawImage(img, 0, 0, null);
858             gc.setTransform(savTransform);
859         }
860
861         return retVal;
862
863     }
864
865
866     /**
867      * Renders a <code>BufferedImage</code> that is
868      * filtered with a
869      * {@link BufferedImageOp}.
870      * The rendering attributes applied include the <code>Clip</code>,
871      * <code>Transform</code>
872      * and <code>Composite</code> attributes. This is equivalent to:
873      * <pre>
874      * img1 = op.filter(img, null);
875      * drawImage(img1, new AffineTransform(1f,0f,0f,1f,x,y), null);
876      * </pre>
877      * @param op the filter to be applied to the image before rendering
878      * @param img the <code>BufferedImage</code> to be rendered
879      * @param x the x coordinate in user space where the upper left
880      * corner of the image is rendered
881      * @param y the y coordinate in user space where the upper left
882      * corner of the image is rendered
883      * @see #transform
884      * @see #setTransform
885      * @see #setComposite
886      * @see #clip
887      * @see #setClip
888      */

889     public void drawImage(BufferedImage JavaDoc img,
890                           BufferedImageOp JavaDoc op,
891                           int x,
892                           int y){
893         //
894
// Only convert if the input image is of type sRGB
895
// non-premultiplied
896
//
897
/*if(img.getType() == BufferedImage.TYPE_INT_ARGB){
898             //
899             // There are two special cases: AffineTransformOp and
900             // ColorConvertOp. If the input op is not one of these
901             // two, then SVGBufferedImageOp is requested to map the
902             // filter to an SVG equivalent.
903             //
904             if(op instanceof AffineTransformOp){
905                 AffineTransformOp transformOp = (AffineTransformOp)op;
906                 AffineTransform transform = transformOp.getTransform();
907
908                 if(transform.getDeterminant() != 0){
909                     AffineTransform inverseTransform = null;
910                     try{
911                         inverseTransform = transform.createInverse();
912                     }catch(NoninvertibleTransformException e){
913                         // This should never happen since we checked the
914                         // matrix determinant
915                         throw new SVGGraphics2DRuntimeException(ERR_UNEXPECTED);
916                     }
917                     gc.transform(transform);
918                     drawImage(img, x, y, null);
919                     gc.transform(inverseTransform);
920                 }
921                 else{
922                     AffineTransform savTransform =
923                     new AffineTransform(gc.getTransform());
924                     gc.transform(transform);
925                     drawImage(img, x, y, null);
926                     gc.setTransform(savTransform);
927                 }
928             }
929             else if(op instanceof ColorConvertOp){
930                 img = op.filter(img, null);
931                 drawImage(img, x, y, null);
932             }
933             else{
934                 //
935                 // Try and convert to an SVG filter
936                 //
937                 SVGFilterDescriptor filterDesc =
938                 domTreeManager.getFilterConverter().toSVG(op, null);
939                 if(filterDesc != null){
940                 //
941                 // Because other filters may be needed to represent the
942                 // composite that applies to this image, a group is created that
943                 // contains the image element.
944                 //
945                 Element imageElement =
946                 getDOMFactory().
947                 createElementNS(SVG_NAMESPACE_URI, SVG_IMAGE_TAG);
948                 getImageHandler().handleImage((Image)img, imageElement,
949                 generatorCtx);
950                 imageElement.setAttributeNS(null, SVG_X_ATTRIBUTE,
951                 Integer.toString(x));
952                 imageElement.setAttributeNS(null, SVG_Y_ATTRIBUTE,
953                 Integer.toString(y));
954                 imageElement.setAttributeNS(null, SVG_WIDTH_ATTRIBUTE,
955                 Integer.toString(img.getWidth(null)));
956                 imageElement.setAttributeNS(null, SVG_HEIGHT_ATTRIBUTE,
957                 Integer.toString(img.getHeight(null)));
958                 imageElement.setAttributeNS(null, SVG_FILTER_ATTRIBUTE,
959                 filterDesc.getFilterValue());
960                 Element imageGroup = generatorCtx.domFactory.createElementNS(SVG_NAMESPACE_URI,
961                 SVG_G_TAG);
962                 imageGroup.appendChild(imageElement);
963
964                     domGroupManager.addElement(imageGroup);
965                 }
966                 else{
967                     //
968                     // Could not convert to an equivalent SVG filter:
969                     // filter now and draw resulting image
970                     //
971                     img = op.filter(img, null);
972                     drawImage(img, x, y, null);
973                 }
974             }
975         }
976         else{*/

977             //
978
// Input image is not sRGB non premultiplied.
979
// Do not try conversion: apply filter and paint
980
//
981
img = op.filter(img, null);
982         drawImage(img, x, y, null);
983             // }
984
}
985
986
987     /**
988      * Renders a {@link RenderedImage},
989      * applying a transform from image
990      * space into user space before drawing.
991      * The transformation from user space into device space is done with
992      * the current <code>Transform</code> in the <code>Graphics2D</code>.
993      * The specified transformation is applied to the image before the
994      * transform attribute in the <code>Graphics2D</code> context is applied.
995      * The rendering attributes applied include the <code>Clip</code>,
996      * <code>Transform</code>, and <code>Composite</code> attributes. Note
997      * that no rendering is done if the specified transform is
998      * noninvertible.
999      * @param img the image to be rendered
1000     * @param trans2 the transformation from image space into user space
1001     * @see #transform
1002     * @see #setTransform
1003     * @see #setComposite
1004     * @see #clip
1005     * @see #setClip
1006     */

1007    public void drawRenderedImage(RenderedImage JavaDoc img,
1008                                  AffineTransform JavaDoc trans2) {
1009
1010        Element JavaDoc image =
1011            getGenericImageHandler().createElement(getGeneratorContext());
1012        AffineTransform JavaDoc trans1
1013            = getGenericImageHandler().handleImage(
1014                                       img, image,
1015                                       img.getMinX(),
1016                                       img.getMinY(),
1017                                       img.getWidth(),
1018                                       img.getHeight(),
1019                                       getGeneratorContext());
1020        
1021        AffineTransform JavaDoc xform;
1022        
1023        // Concatenate the transformation we receive from the imageHandler
1024
// to the user-supplied one. Be aware that both may be null.
1025
if (trans2 == null) {
1026            xform = trans1;
1027        } else {
1028            if(trans1 == null) {
1029                xform = trans2;
1030             } else {
1031                xform = new AffineTransform JavaDoc(trans2);
1032                xform.concatenate(trans1);
1033            }
1034        }
1035
1036        if(xform == null) {
1037            domGroupManager.addElement(image);
1038        } else if(xform.getDeterminant() != 0){
1039            AffineTransform JavaDoc inverseTransform = null;
1040            try{
1041                inverseTransform = xform.createInverse();
1042            }catch(NoninvertibleTransformException JavaDoc e){
1043                // This should never happen since we checked
1044
// the matrix determinant
1045
throw new SVGGraphics2DRuntimeException(ERR_UNEXPECTED);
1046            }
1047            gc.transform(xform);
1048            domGroupManager.addElement(image);
1049            gc.transform(inverseTransform);
1050        } else {
1051            AffineTransform JavaDoc savTransform = new AffineTransform JavaDoc(gc.getTransform());
1052            gc.transform(xform);
1053            domGroupManager.addElement(image);
1054            gc.setTransform(savTransform);
1055        }
1056    }
1057
1058    /**
1059     * Renders a
1060     * {@link RenderableImage},
1061     * applying a transform from image space into user space before drawing.
1062     * The transformation from user space into device space is done with
1063     * the current <code>Transform</code> in the <code>Graphics2D</code>.
1064     * The specified transformation is applied to the image before the
1065     * transform attribute in the <code>Graphics2D</code> context is applied.
1066     * The rendering attributes applied include the <code>Clip</code>,
1067     * <code>Transform</code>, and <code>Composite</code> attributes. Note
1068     * that no rendering is done if the specified transform is
1069     * noninvertible.
1070     * <p>
1071     * Rendering hints set on the <code>Graphics2D</code> object might
1072     * be used in rendering the <code>RenderableImage</code>.
1073     * If explicit control is required over specific hints recognized by a
1074     * specific <code>RenderableImage</code>, or if knowledge of which hints
1075     * are used is required, then a <code>RenderedImage</code> should be
1076     * obtained directly from the <code>RenderableImage</code>
1077     * and rendered using
1078     * {@link #drawRenderedImage(RenderedImage, AffineTransform)}.
1079     * @param img the image to be rendered
1080     * @param trans2 the transformation from image space into user space
1081     * @see #transform
1082     * @see #setTransform
1083     * @see #setComposite
1084     * @see #clip
1085     * @see #setClip
1086     * @see #drawRenderedImage
1087     */

1088    public void drawRenderableImage(RenderableImage JavaDoc img,
1089                                    AffineTransform JavaDoc trans2){
1090                                    
1091        Element JavaDoc image =
1092            getGenericImageHandler().createElement(getGeneratorContext());
1093        
1094        AffineTransform JavaDoc trans1 =
1095            getGenericImageHandler().handleImage(
1096                                     img, image,
1097                                     img.getMinX(),
1098                                     img.getMinY(),
1099                                     img.getWidth(),
1100                                     img.getHeight(),
1101                                     getGeneratorContext());
1102        
1103        AffineTransform JavaDoc xform;
1104
1105        // Concatenate the transformation we receive from the imageHandler
1106
// to the user-supplied one. Be aware that both may be null.
1107
if (trans2 == null) {
1108            xform = trans1;
1109        } else {
1110            if(trans1 == null) {
1111                xform = trans2;
1112             } else {
1113                xform = new AffineTransform JavaDoc(trans2);
1114                xform.concatenate(trans1);
1115            }
1116        }
1117
1118        if (xform == null) {
1119            domGroupManager.addElement(image);
1120        } else if(xform.getDeterminant() != 0){
1121            AffineTransform JavaDoc inverseTransform = null;
1122            try{
1123                inverseTransform = xform.createInverse();
1124            }catch(NoninvertibleTransformException JavaDoc e){
1125                // This should never happen because we checked the
1126
// matrix determinant
1127
throw new SVGGraphics2DRuntimeException(ERR_UNEXPECTED);
1128            }
1129            gc.transform(xform);
1130            domGroupManager.addElement(image);
1131            gc.transform(inverseTransform);
1132        } else {
1133            AffineTransform JavaDoc savTransform = new AffineTransform JavaDoc(gc.getTransform());
1134            gc.transform(xform);
1135            domGroupManager.addElement(image);
1136            gc.setTransform(savTransform);
1137        }
1138    }
1139
1140
1141    /**
1142     * Renders the text specified by the specified <code>String</code>,
1143     * using the current <code>Font</code> and <code>Paint</code> attributes
1144     * in the <code>Graphics2D</code> context.
1145     * The baseline of the first character is at position
1146     * (<i>x</i>,&nbsp;<i>y</i>) in the User Space.
1147     * The rendering attributes applied include the <code>Clip</code>,
1148     * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and
1149     * <code>Composite</code> attributes. For characters in script systems
1150     * such as Hebrew and Arabic, the glyphs can be rendered from right to
1151     * left, in which case the coordinate supplied is the location of the
1152     * leftmost character on the baseline.
1153     * @param s the <code>String</code> to be rendered
1154     * @param x the x coordinate where the <code>String</code>
1155     * should be rendered
1156     * @param y the y coordinate where the <code>String</code>
1157     * should be rendered
1158     * @see #setPaint
1159     * @see java.awt.Graphics#setColor
1160     * @see java.awt.Graphics#setFont
1161     * @see #setTransform
1162     * @see #setComposite
1163     * @see #setClip
1164     */

1165    public void drawString(String JavaDoc s, float x, float y) {
1166        if (!textAsShapes) {
1167
1168            if (generatorCtx.svgFont) {
1169                // record that the font is being used to draw this string, this is
1170
// so that the SVG Font element will only create glyphs for the
1171
// characters that are needed
1172
domTreeManager.gcConverter.getFontConverter().recordFontUsage(s, getFont());
1173            }
1174
1175            Font JavaDoc font = getFont();
1176
1177            // Account for the font transform if there is one
1178
AffineTransform JavaDoc txtTxf = null;
1179            AffineTransform JavaDoc savTxf = getTransform();
1180
1181            if (font != null){
1182                txtTxf = font.getTransform();
1183                if (txtTxf != null && !txtTxf.isIdentity()){
1184                    //
1185
// The additional transform applies about the text's origin
1186
//
1187
AffineTransform JavaDoc t = new AffineTransform JavaDoc();
1188                    t.translate(x, y);
1189                    t.concatenate(txtTxf);
1190                    t.translate(-x, -y);
1191                    this.transform(t);
1192                } else {
1193                    txtTxf = null;
1194                }
1195            }
1196
1197            Element JavaDoc text =
1198                getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_TEXT_TAG);
1199            text.setAttributeNS(null, SVG_X_ATTRIBUTE,
1200                                generatorCtx.doubleString(x));
1201            text.setAttributeNS(null, SVG_Y_ATTRIBUTE,
1202                                generatorCtx.doubleString(y));
1203            text.setAttributeNS(XML_NAMESPACE_URI,
1204                                XML_SPACE_ATTRIBUTE,
1205                                XML_PRESERVE_VALUE);
1206            text.appendChild(getDOMFactory().createTextNode(s));
1207            domGroupManager.addElement(text, DOMGroupManager.FILL);
1208
1209            if (txtTxf != null){
1210                this.setTransform(savTxf);
1211            }
1212                
1213        } else {
1214            GlyphVector JavaDoc gv = getFont().
1215                createGlyphVector(getFontRenderContext(), s);
1216            drawGlyphVector(gv, x, y);
1217        }
1218    }
1219
1220    /**
1221     * Renders the text of the specified iterator, using the
1222     * <code>Graphics2D</code> context's current <code>Paint</code>. The
1223     * iterator must specify a font
1224     * for each character. The baseline of the
1225     * first character is at position (<i>x</i>,&nbsp;<i>y</i>) in the
1226     * User Space.
1227     * The rendering attributes applied include the <code>Clip</code>,
1228     * <code>Transform</code>, <code>Paint</code>, and
1229     * <code>Composite</code> attributes.
1230     * For characters in script systems such as Hebrew and Arabic,
1231     * the glyphs can be rendered from right to left, in which case the
1232     * coordinate supplied is the location of the leftmost character
1233     * on the baseline.<br />
1234     *
1235     * <b>Note</b>: The current implementation turns a drawString call
1236     * into shapes. Therefore, the generated SVG file will be sub-optimal
1237     * in terms of size and will have lost semantic (i.e., text is no
1238     * longer text but shapes), but it is graphically accurate.
1239     *
1240     * @param iterator the iterator whose text is to be rendered
1241     * @param x the x coordinate where the iterator's text is to be rendered
1242     * @param y the y coordinate where the iterator's text is to be rendered
1243     * @see #setPaint
1244     * @see java.awt.Graphics#setColor
1245     * @see #setTransform
1246     * @see #setComposite
1247     * @see #setClip
1248     */

1249    public void drawString(AttributedCharacterIterator iterator,
1250                           float x, float y) {
1251        TextLayout JavaDoc layout = new TextLayout JavaDoc(iterator, getFontRenderContext());
1252        layout.draw(this, x, y);
1253    }
1254
1255
1256    /**
1257     * Fills the interior of a <code>Shape</code> using the settings of the
1258     * <code>Graphics2D</code> context. The rendering attributes applied
1259     * include the <code>Clip</code>, <code>Transform</code>,
1260     * <code>Paint</code>, and <code>Composite</code>.
1261     * @param s the <code>Shape</code> to be filled
1262     * @see #setPaint
1263     * @see java.awt.Graphics#setColor
1264     * @see #transform
1265     * @see #setTransform
1266     * @see #setComposite
1267     * @see #clip
1268     * @see #setClip
1269     */

1270    public void fill(Shape JavaDoc s) {
1271        Element JavaDoc svgShape = shapeConverter.toSVG(s);
1272        if (svgShape != null) {
1273            domGroupManager.addElement(svgShape, DOMGroupManager.FILL);
1274        }
1275    }
1276
1277    /**
1278     * Returns the device configuration associated with this
1279     * <code>Graphics2D</code>.
1280     */

1281    public GraphicsConfiguration JavaDoc getDeviceConfiguration(){
1282        // TO BE DONE.
1283
return null;
1284    }
1285
1286}
1287
Popular Tags