Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/batik/bridge/AbstractSVGGradientElementBridge.java


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.bridge;
19  
20  import java.awt.Color;
21  import java.awt.Paint;
22  import java.awt.geom.AffineTransform;
23  import java.util.Iterator;
24  import java.util.LinkedList;
25  import java.util.List;
26  
27  import org.apache.batik.dom.svg.SVGOMDocument;
28  import org.apache.batik.dom.util.XLinkSupport;
29  import org.apache.batik.ext.awt.MultipleGradientPaint;
30  import org.apache.batik.gvt.GraphicsNode;
31  import org.apache.batik.util.ParsedURL;
32  import org.w3c.dom.Element;
33  import org.w3c.dom.Node;
34  
35  /**
36   * Bridge class for vending gradients.
37   *
38   * @author <a href="mailto:tkormann@apache.org">Thierry Kormann</a>
39   * @version $Id: AbstractSVGGradientElementBridge.java,v 1.12 2004/08/18 07:12:30 vhardy Exp $
40   */
41  public abstract class AbstractSVGGradientElementBridge extends AbstractSVGBridge
42      implements PaintBridge, ErrorConstants {
43  
44      /**
45       * Constructs a new AbstractSVGGradientElementBridge.
46       */
47      protected AbstractSVGGradientElementBridge() {}
48  
49      /**
50       * Creates a <tt>Paint</tt> according to the specified parameters.
51       *
52       * @param ctx the bridge context to use
53       * @param paintElement the element that defines a Paint
54       * @param paintedElement the element referencing the paint
55       * @param paintedNode the graphics node on which the Paint will be applied
56       * @param opacity the opacity of the Paint to create
57       */
58      public Paint createPaint(BridgeContext ctx,
59                               Element paintElement,
60                               Element paintedElement,
61                               GraphicsNode paintedNode,
62                               float opacity) {
63  
64          String s;
65  
66          // stop elements
67          List stops = extractStop(paintElement, opacity, ctx);
68          // if no stops are defined, painting is the same as 'none'
69          if (stops == null) {
70              return null;
71          }
72          int stopLength = stops.size();
73          // if one stops is defined, painting is the same as a single color
74          if (stopLength == 1) {
75              return ((Stop)stops.get(0)).color;
76          }
77          float [] offsets = new float[stopLength];
78          Color [] colors = new Color[stopLength];
79          Iterator iter = stops.iterator();
80          for (int i=0; iter.hasNext(); ++i) {
81              Stop stop = (Stop)iter.next();
82              offsets[i] = stop.offset;
83              colors[i] = stop.color;
84          }
85  
86          // 'spreadMethod' attribute - default is pad
87          MultipleGradientPaint.CycleMethodEnum spreadMethod
88              = MultipleGradientPaint.NO_CYCLE;
89          s = SVGUtilities.getChainableAttributeNS
90              (paintElement, null, SVG_SPREAD_METHOD_ATTRIBUTE, ctx);
91          if (s.length() != 0) {
92              spreadMethod = convertSpreadMethod(paintElement, s);
93          }
94  
95          // 'color-interpolation' CSS property
96          MultipleGradientPaint.ColorSpaceEnum colorSpace
97              = CSSUtilities.convertColorInterpolation(paintElement);
98  
99          // 'gradientTransform' attribute - default is an Identity matrix
100         AffineTransform transform;
101         s = SVGUtilities.getChainableAttributeNS
102             (paintElement, null, SVG_GRADIENT_TRANSFORM_ATTRIBUTE, ctx);
103         if (s.length() != 0) {
104             transform = SVGUtilities.convertTransform
105                 (paintElement, SVG_GRADIENT_TRANSFORM_ATTRIBUTE, s);
106         } else {
107             transform = new AffineTransform();
108         }
109 
110         Paint paint = buildGradient(paintElement,
111                                     paintedElement,
112                                     paintedNode,
113                                     spreadMethod,
114                                     colorSpace,
115                                     transform,
116                                     colors,
117                                     offsets,
118                                     ctx);
119         return paint;
120     }
121 
122     /**
123      * Builds a concrete gradient according to the specified parameters.
124      *
125      * @param paintElement the element that defines a Paint
126      * @param paintedElement the element referencing the paint
127      * @param paintedNode the graphics node on which the Paint will be applied
128      * @param spreadMethod the spread method
129      * @param colorSpace the color space (sRGB | LinearRGB)
130      * @param transform the gradient transform
131      * @param colors the colors of the gradient
132      * @param offsets the offsets
133      * @param ctx the bridge context to use
134      */
135     protected abstract
136         Paint buildGradient(Element paintElement,
137                             Element paintedElement,
138                             GraphicsNode paintedNode,
139                             MultipleGradientPaint.CycleMethodEnum spreadMethod,
140                             MultipleGradientPaint.ColorSpaceEnum colorSpace,
141                             AffineTransform transform,
142                             Color [] colors,
143                             float [] offsets,
144                             BridgeContext ctx);
145 
146     // convenient methods
147 
148     /**
149      * Converts the spreadMethod attribute.
150      *
151      * @param paintElement the paint Element with a spreadMethod
152      * @param s the spread method
153      */
154     protected static MultipleGradientPaint.CycleMethodEnum convertSpreadMethod
155         (Element paintElement, String s) {
156         if (SVG_REPEAT_VALUE.equals(s)) {
157             return MultipleGradientPaint.REPEAT;
158         }
159         if (SVG_REFLECT_VALUE.equals(s)) {
160             return MultipleGradientPaint.REFLECT;
161         }
162         if (SVG_PAD_VALUE.equals(s)) {
163             return MultipleGradientPaint.NO_CYCLE;
164         }
165         throw new BridgeException
166             (paintElement, ERR_ATTRIBUTE_VALUE_MALFORMED,
167              new Object[] {SVG_SPREAD_METHOD_ATTRIBUTE, s});
168     }
169 
170     /**
171      * Returns the stops elements of the specified gradient
172      * element. Stops can be children of the gradients or defined on
173      * one of its 'ancestor' (linked with the xlink:href attribute).
174      *
175      * @param paintElement the gradient element
176      * @param opacity the opacity
177      * @param ctx the bridge context to use
178      */
179     protected static List extractStop(Element paintElement,
180                                       float opacity,
181                                       BridgeContext ctx) {
182 
183         List refs = new LinkedList();
184         for (;;) {
185             List stops = extractLocalStop(paintElement, opacity, ctx);
186             if (stops != null) {
187                 return stops; // stop elements found, exit
188             }
189             String uri = XLinkSupport.getXLinkHref(paintElement);
190             if (uri.length() == 0) {
191                 return null; // no xlink:href found, exit
192             }
193             // check if there is circular dependencies
194             SVGOMDocument doc = (SVGOMDocument)paintElement.getOwnerDocument();
195             ParsedURL purl = new ParsedURL(doc.getURL(), uri);
196             if (!purl.complete())
197                 throw new BridgeException(paintElement,
198                                           ERR_URI_MALFORMED,
199                                           new Object[] {uri});
200 
201             if (contains(refs, purl)) {
202                 throw new BridgeException(paintElement,
203                                           ERR_XLINK_HREF_CIRCULAR_DEPENDENCIES,
204                                           new Object[] {uri});
205             }
206             refs.add(purl);
207             paintElement = ctx.getReferencedElement(paintElement, uri);
208         }
209     }
210 
211     /**
212      * Returns a list of <tt>Stop</tt> elements, children of the
213      * specified paintElement can have or null if any.
214      *
215      * @param gradientElement the paint element
216      * @param opacity the opacity
217      * @param ctx the bridge context
218      */
219     protected static List extractLocalStop(Element gradientElement,
220                                            float opacity,
221                                            BridgeContext ctx) {
222         LinkedList stops = null;
223         Stop previous = null;
224         for (Node n = gradientElement.getFirstChild();
225              n != null;
226              n = n.getNextSibling()) {
227 
228             if ((n.getNodeType() != Node.ELEMENT_NODE)) {
229                 continue;
230             }
231 
232             Element e = (Element)n;
233             Bridge bridge = ctx.getBridge(e);
234             if (bridge == null || !(bridge instanceof SVGStopElementBridge)) {
235                 continue;
236             }
237             Stop stop = ((SVGStopElementBridge)bridge).createStop
238                 (ctx, gradientElement, e, opacity);
239             if (stops == null) {
240                 stops = new LinkedList();
241             }
242             if (previous != null) {
243                 if (stop.offset < previous.offset) {
244                     stop.offset = previous.offset;
245                 }
246             }
247             stops.add(stop);
248             previous = stop;
249         }
250         return stops;
251     }
252 
253     /**
254      * Returns true if the specified list of URLs contains the specified url.
255      *
256      * @param urls the list of URLs
257      * @param key the url to search for
258      */
259     private static boolean contains(List urls, ParsedURL key) {
260         Iterator iter = urls.iterator();
261         while (iter.hasNext()) {
262             if (key.equals(iter.next()))
263                 return true;
264         }
265         return false;
266     }
267 
268     /**
269      * This class represents a gradient &lt;stop> element.
270      */
271     public static class Stop {
272 
273         /** The stop color. */
274         public Color color;
275         /** The stop offset. */
276         public float offset;
277 
278         /**
279          * Constructs a new stop definition.
280          *
281          * @param color the stop color
282          * @param offset the stop offset
283          */
284         public Stop(Color color, float offset) {
285             this.color = color;
286             this.offset = offset;
287         }
288     }
289 
290     /**
291      * Bridge class for the gradient &lt;stop> element.
292      */
293     public static class SVGStopElementBridge extends AbstractSVGBridge
294         implements Bridge {
295 
296         /**
297          * Returns 'stop'.
298          */
299         public String getLocalName() {
300             return SVG_STOP_TAG;
301         }
302 
303         /**
304          * Creates a <tt>Stop</tt> according to the specified parameters.
305          *
306          * @param ctx the bridge context to use
307          * @param gradientElement the gradient element
308          * @param stopElement the stop element
309          * @param opacity an additional opacity of the stop color
310          */
311         public Stop createStop(BridgeContext ctx,
312                                Element gradientElement,
313                                Element stopElement,
314                                float opacity) {
315 
316             String s = stopElement.getAttributeNS(null, SVG_OFFSET_ATTRIBUTE);
317             if (s.length() == 0) {
318                 throw new BridgeException(stopElement, ERR_ATTRIBUTE_MISSING,
319                                           new Object[] {SVG_OFFSET_ATTRIBUTE});
320             }
321             float offset;
322             try {
323                 offset = SVGUtilities.convertRatio(s);
324             } catch (NumberFormatException ex) {
325                 throw new BridgeException
326                     (stopElement, ERR_ATTRIBUTE_VALUE_MALFORMED,
327                      new Object[] {SVG_OFFSET_ATTRIBUTE, s, ex});
328             }
329             Color color
330                 = CSSUtilities.convertStopColor(stopElement, opacity, ctx);
331 
332             return new Stop(color, offset);
333         }
334     }
335 }