001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.bridge;
020:
021: import java.awt.AlphaComposite;
022: import java.awt.Color;
023: import java.awt.Composite;
024: import java.awt.Cursor;
025: import java.awt.RenderingHints;
026: import java.awt.geom.GeneralPath;
027: import java.awt.geom.Rectangle2D;
028:
029: import org.apache.batik.css.engine.CSSEngine;
030: import org.apache.batik.css.engine.CSSStylableElement;
031: import org.apache.batik.css.engine.SVGCSSEngine;
032: import org.apache.batik.css.engine.value.ListValue;
033: import org.apache.batik.css.engine.value.Value;
034: import org.apache.batik.css.engine.value.svg.ICCColor;
035: import org.apache.batik.dom.svg.SVGOMDocument;
036: import org.apache.batik.ext.awt.MultipleGradientPaint;
037: import org.apache.batik.ext.awt.image.renderable.ClipRable;
038: import org.apache.batik.ext.awt.image.renderable.Filter;
039: import org.apache.batik.gvt.CompositeGraphicsNode;
040: import org.apache.batik.gvt.GraphicsNode;
041: import org.apache.batik.gvt.filter.Mask;
042: import org.apache.batik.util.CSSConstants;
043: import org.apache.batik.util.XMLConstants;
044: import org.w3c.dom.Element;
045: import org.w3c.dom.css.CSSPrimitiveValue;
046: import org.w3c.dom.css.CSSValue;
047:
048: /**
049: * A collection of utility method involving CSS property. The listed
050: * methods bellow could be used as convenient methods to create
051: * concrete objects regarding to CSS properties.
052: *
053: * @author <a href="mailto:tkormann@apache.org">Thierry Kormann</a>
054: * @version $Id: CSSUtilities.java 498740 2007-01-22 18:35:57Z dvholten $
055: */
056: public abstract class CSSUtilities implements CSSConstants,
057: ErrorConstants, XMLConstants {
058:
059: /**
060: * No instance of this class is required.
061: */
062: protected CSSUtilities() {
063: }
064:
065: /////////////////////////////////////////////////////////////////////////
066: // Global methods
067: /////////////////////////////////////////////////////////////////////////
068:
069: /**
070: * Returns CSSEngine associated to the specified element.
071: * @param e the element
072: */
073: public static CSSEngine getCSSEngine(Element e) {
074: return ((SVGOMDocument) e.getOwnerDocument()).getCSSEngine();
075: }
076:
077: /**
078: * Returns the computed style of the given property.
079: */
080: public static Value getComputedStyle(Element e, int property) {
081: CSSEngine engine = getCSSEngine(e);
082: if (engine == null)
083: return null;
084: return engine.getComputedStyle((CSSStylableElement) e, null,
085: property);
086: }
087:
088: /////////////////////////////////////////////////////////////////////////
089: // 'pointer-events'
090: /////////////////////////////////////////////////////////////////////////
091:
092: /**
093: * Returns the type that describes how this graphics node reacts to events.
094: *
095: * @return GraphicsNode.VISIBLE_PAINTED |
096: * GraphicsNode.VISIBLE_FILL |
097: * GraphicsNode.VISIBLE_STROKE |
098: * GraphicsNode.VISIBLE |
099: * GraphicsNode.PAINTED |
100: * GraphicsNode.FILL |
101: * GraphicsNode.STROKE |
102: * GraphicsNode.ALL |
103: * GraphicsNode.NONE
104: */
105: public static int convertPointerEvents(Element e) {
106: Value v = getComputedStyle(e, SVGCSSEngine.POINTER_EVENTS_INDEX);
107: String s = v.getStringValue();
108: switch (s.charAt(0)) {
109: case 'v':
110: if (s.length() == 7) {
111: return GraphicsNode.VISIBLE;
112: } else {
113: switch (s.charAt(7)) {
114: case 'p':
115: return GraphicsNode.VISIBLE_PAINTED;
116: case 'f':
117: return GraphicsNode.VISIBLE_FILL;
118: case 's':
119: return GraphicsNode.VISIBLE_STROKE;
120: default:
121: // can't be reached
122: throw new IllegalStateException(
123: "unexpected event, must be one of (p,f,s) is:"
124: + s.charAt(7));
125: }
126: }
127: case 'p':
128: return GraphicsNode.PAINTED;
129: case 'f':
130: return GraphicsNode.FILL;
131: case 's':
132: return GraphicsNode.STROKE;
133: case 'a':
134: return GraphicsNode.ALL;
135: case 'n':
136: return GraphicsNode.NONE;
137: default:
138: // can't be reached
139: throw new IllegalStateException(
140: "unexpected event, must be one of (v,p,f,s,a,n) is:"
141: + s.charAt(0));
142: }
143: }
144:
145: /////////////////////////////////////////////////////////////////////////
146: // 'enable-background'
147: /////////////////////////////////////////////////////////////////////////
148:
149: /**
150: * Returns the subregion of user space where access to the
151: * background image is allowed to happen.
152: *
153: * @param e the container element
154: */
155: public static Rectangle2D convertEnableBackground(Element e /*,
156: UnitProcessor.Context uctx*/) {
157: Value v = getComputedStyle(e,
158: SVGCSSEngine.ENABLE_BACKGROUND_INDEX);
159: if (v.getCssValueType() != CSSValue.CSS_VALUE_LIST) {
160: return null; // accumulate
161: }
162: ListValue lv = (ListValue) v;
163: int length = lv.getLength();
164: switch (length) {
165: case 1:
166: return CompositeGraphicsNode.VIEWPORT; // new
167: case 5: // new <x>,<y>,<width>,<height>
168: float x = lv.item(1).getFloatValue();
169: float y = lv.item(2).getFloatValue();
170: float w = lv.item(3).getFloatValue();
171: float h = lv.item(4).getFloatValue();
172: return new Rectangle2D.Float(x, y, w, h);
173:
174: default:
175: throw new IllegalStateException("Unexpected length:"
176: + length); // Cannot happen
177: }
178: }
179:
180: /////////////////////////////////////////////////////////////////////////
181: // 'color-interpolation-filters'
182: /////////////////////////////////////////////////////////////////////////
183:
184: /**
185: * Returns the color space for the specified filter element. Checks the
186: * 'color-interpolation-filters' property.
187: *
188: * @param e the element
189: * @return true if the color space is linear, false otherwise (sRGB).
190: */
191: public static boolean convertColorInterpolationFilters(Element e) {
192: Value v = getComputedStyle(e,
193: SVGCSSEngine.COLOR_INTERPOLATION_FILTERS_INDEX);
194: return CSS_LINEARRGB_VALUE == v.getStringValue();
195: }
196:
197: /////////////////////////////////////////////////////////////////////////
198: // 'color-interpolation'
199: /////////////////////////////////////////////////////////////////////////
200:
201: /**
202: * Returns the color space for the specified element. Checks the
203: * 'color-interpolation' property
204: *
205: * @param e the element
206: */
207: public static MultipleGradientPaint.ColorSpaceEnum convertColorInterpolation(
208: Element e) {
209: Value v = getComputedStyle(e,
210: SVGCSSEngine.COLOR_INTERPOLATION_INDEX);
211: return (CSS_LINEARRGB_VALUE == v.getStringValue()) ? MultipleGradientPaint.LINEAR_RGB
212: : MultipleGradientPaint.SRGB;
213: }
214:
215: /////////////////////////////////////////////////////////////////////////
216: // 'cursor'
217: /////////////////////////////////////////////////////////////////////////
218:
219: /**
220: * Checks if the cursor property on the input element is set to auto
221: */
222: public static boolean isAutoCursor(Element e) {
223: Value cursorValue = CSSUtilities.getComputedStyle(e,
224: SVGCSSEngine.CURSOR_INDEX);
225:
226: boolean isAuto = false;
227: if (cursorValue != null) {
228: if (cursorValue.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE
229: && cursorValue.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT
230: && cursorValue.getStringValue().charAt(0) == 'a') {
231: isAuto = true;
232: } else if (cursorValue.getCssValueType() == CSSValue.CSS_VALUE_LIST
233: && cursorValue.getLength() == 1) {
234: Value lValue = cursorValue.item(0);
235: if (lValue != null
236: && lValue.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE
237: && lValue.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT
238: && lValue.getStringValue().charAt(0) == 'a') {
239: isAuto = true;
240: }
241: }
242: }
243:
244: return isAuto;
245: }
246:
247: /**
248: * Returns the Cursor corresponding to the input element's
249: * cursor property
250: *
251: * @param e the element
252: */
253: public static Cursor convertCursor(Element e, BridgeContext ctx) {
254: return ctx.getCursorManager().convertCursor(e);
255: }
256:
257: ////////////////////////////////////////////////////////////////////////
258: // 'color-rendering', 'text-rendering', 'image-rendering',
259: // 'shape-rendering'
260: ////////////////////////////////////////////////////////////////////////
261:
262: /**
263: * Fills the rendering hints for the specified shape element or do
264: * nothing none has been specified. Checks the 'shape-rendering'
265: * property. If the given RenderingHints is null, a new
266: * RenderingHints is created.
267: *
268: * <p>Here is how the mapping between SVG rendering hints and the Java2D
269: * rendering hints is done:</p>
270: *
271: * <dl>
272: * <dt>'optimizeSpeed':</dt>
273: * <dd>
274: * <ul>
275: * <li>KEY_RENDERING=VALUE_RENDER_SPEED</li>
276: * <li>KEY_ANTIALIASING=VALUE_ANTIALIAS_OFF</li>
277: * </ul>
278: * </dd>
279: * <dt>'crispEdges':</dt>
280: * <dd>
281: * <ul>
282: * <li>KEY_RENDERING=VALUE_RENDER_DEFAULT</li>
283: * <li>KEY_ANTIALIASING=VALUE_ANTIALIAS_OFF</li>
284: * </ul>
285: * </dd>
286: * <dt>'geometricPrecision':</dt>
287: * <dd>
288: * <ul>
289: * <li>KEY_RENDERING=VALUE_RENDER_QUALITY</li>
290: * <li>KEY_ANTIALIASING=VALUE_ANTIALIAS_ON</li>
291: * </ul>
292: * </dd>
293: * </dl>
294: *
295: * @param e the element
296: * @param hints a RenderingHints to fill, or null.
297: */
298: public static RenderingHints convertShapeRendering(Element e,
299: RenderingHints hints) {
300: Value v = getComputedStyle(e,
301: SVGCSSEngine.SHAPE_RENDERING_INDEX);
302: String s = v.getStringValue();
303: int len = s.length();
304: if ((len == 4) && (s.charAt(0) == 'a')) // auto
305: return hints;
306: if (len < 10)
307: return hints; // Unknown.
308:
309: if (hints == null)
310: hints = new RenderingHints(null);
311:
312: switch (s.charAt(0)) {
313: case 'o': // optimizeSpeed
314: hints.put(RenderingHints.KEY_RENDERING,
315: RenderingHints.VALUE_RENDER_SPEED);
316: hints.put(RenderingHints.KEY_ANTIALIASING,
317: RenderingHints.VALUE_ANTIALIAS_OFF);
318: break;
319: case 'c': // crispEdges
320: hints.put(RenderingHints.KEY_RENDERING,
321: RenderingHints.VALUE_RENDER_DEFAULT);
322: hints.put(RenderingHints.KEY_ANTIALIASING,
323: RenderingHints.VALUE_ANTIALIAS_OFF);
324: break;
325: case 'g': // geometricPrecision
326: hints.put(RenderingHints.KEY_RENDERING,
327: RenderingHints.VALUE_RENDER_QUALITY);
328: hints.put(RenderingHints.KEY_ANTIALIASING,
329: RenderingHints.VALUE_ANTIALIAS_ON);
330: hints.put(RenderingHints.KEY_STROKE_CONTROL,
331: RenderingHints.VALUE_STROKE_PURE);
332: break;
333: }
334: return hints;
335: }
336:
337: /**
338: * Fills the rendering hints for the specified text element or do
339: * nothing if none has been specified. If the given RenderingHints
340: * is null, a new one is created. Checks the 'text-rendering'
341: * property.
342: *
343: * <p>Here is how the mapping between SVG rendering hints and the Java2D
344: * rendering hints is done:</p>
345: *
346: * <dl>
347: * <dt>'optimizeSpeed':</dt>
348: * <dd>
349: * <ul>
350: * <li>KEY_RENDERING=VALUE_RENDER_SPEED</li>
351: * <li>KEY_ANTIALIASING=VALUE_ANTIALIAS_OFF</li>
352: * <li>KEY_TEXT_ANTIALIASING=VALUE_TEXT_ANTIALIAS_OFF</li>
353: * <li>KEY_FRACTIONALMETRICS=VALUE_FRACTIONALMETRICS_OFF</li>
354: * </ul>
355: * </dd>
356: * <dt>'optimizeLegibility':</dt>
357: * <dd>
358: * <ul>
359: * <li>KEY_RENDERING=VALUE_RENDER_QUALITY</li>
360: * <li>KEY_ANTIALIASING=VALUE_ANTIALIAS_ON</li>
361: * <li>KEY_TEXT_ANTIALIASING=VALUE_TEXT_ANTIALIAS_ON</li>
362: * <li>KEY_FRACTIONALMETRICS=VALUE_FRACTIONALMETRICS_OFF</li>
363: * </ul>
364: * </dd>
365: * <dt>'geometricPrecision':</dt>
366: * <dd>
367: * <ul>
368: * <li>KEY_RENDERING=VALUE_RENDER_QUALITY</li>
369: * <li>KEY_ANTIALIASING=VALUE_ANTIALIAS_DEFAULT</li>
370: * <li>KEY_TEXT_ANTIALIASING=VALUE_TEXT_ANTIALIAS_DEFAULT</li>
371: * <li>KEY_FRACTIONALMETRICS=VALUE_FRACTIONALMETRICS_ON</li>
372: * </ul>
373: * </dd>
374: * </dl>
375: *
376: * <p>Note that for text both KEY_TEXT_ANTIALIASING and
377: * KEY_ANTIALIASING are set as there is no guarantee that a Java2D
378: * text rendering primitive will be used to draw text (eg. SVG
379: * Font...).</p>
380: *
381: * @param e the element
382: * @param hints a RenderingHints to fill, or null.
383: */
384: public static RenderingHints convertTextRendering(Element e,
385: RenderingHints hints) {
386: Value v = getComputedStyle(e, SVGCSSEngine.TEXT_RENDERING_INDEX);
387: String s = v.getStringValue();
388: int len = s.length();
389: if ((len == 4) && (s.charAt(0) == 'a')) // auto
390: return hints;
391: if (len < 13)
392: return hints; // Unknown.
393:
394: if (hints == null)
395: hints = new RenderingHints(null);
396:
397: switch (s.charAt(8)) {
398: case 's': // optimizeSpeed
399: hints.put(RenderingHints.KEY_RENDERING,
400: RenderingHints.VALUE_RENDER_SPEED);
401: hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
402: RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
403: hints.put(RenderingHints.KEY_ANTIALIASING,
404: RenderingHints.VALUE_ANTIALIAS_OFF);
405: // hints.put(RenderingHints.KEY_FRACTIONALMETRICS,
406: // RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
407: break;
408: case 'l': // optimizeLegibility
409: hints.put(RenderingHints.KEY_RENDERING,
410: RenderingHints.VALUE_RENDER_QUALITY);
411: hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
412: RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
413: hints.put(RenderingHints.KEY_ANTIALIASING,
414: RenderingHints.VALUE_ANTIALIAS_ON);
415: // hints.put(RenderingHints.KEY_FRACTIONALMETRICS,
416: // RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
417: break;
418: case 'c': // geometricPrecision
419: hints.put(RenderingHints.KEY_RENDERING,
420: RenderingHints.VALUE_RENDER_QUALITY);
421: hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
422: RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
423: hints.put(RenderingHints.KEY_ANTIALIASING,
424: RenderingHints.VALUE_ANTIALIAS_ON);
425: hints.put(RenderingHints.KEY_FRACTIONALMETRICS,
426: RenderingHints.VALUE_FRACTIONALMETRICS_ON);
427: hints.put(RenderingHints.KEY_STROKE_CONTROL,
428: RenderingHints.VALUE_STROKE_PURE);
429: break;
430: }
431: return hints;
432: }
433:
434: /**
435: * Fills the rendering hints for the specified image element or do
436: * nothing if none has been specified. If the given RenderingHints
437: * is null, a new one is created. Checks the 'image-rendering'
438: * property.
439: *
440: * <p>Here is how the mapping between SVG rendering hints and the Java2D
441: * rendering hints is done:</p>
442: *
443: * <dl>
444: * <dt>'optimizeSpeed':</dt>
445: * <dd>
446: * <ul>
447: * <li>KEY_RENDERING=VALUE_RENDER_SPEED</li>
448: * <li>KEY_INTERPOLATION=VALUE_INTERPOLATION_NEAREST_NEIGHBOR</li>
449: * </ul>
450: * </dd>
451: * <dt>'optimizeQuality':</dt>
452: * <dd>
453: * <ul>
454: * <li>KEY_RENDERING=VALUE_RENDER_QUALITY</li>
455: * <li>KEY_INTERPOLATION=VALUE_INTERPOLATION_BICUBIC</li>
456: * </ul>
457: * </dd>
458: * </dl>
459: *
460: * @param e the element
461: * @param hints a RenderingHints to fill, or null.
462: */
463: public static RenderingHints convertImageRendering(Element e,
464: RenderingHints hints) {
465: Value v = getComputedStyle(e,
466: SVGCSSEngine.IMAGE_RENDERING_INDEX);
467: String s = v.getStringValue();
468: int len = s.length();
469: if ((len == 4) && (s.charAt(0) == 'a')) // auto
470: return hints;
471: if (len < 13)
472: return hints; // Unknown.
473:
474: if (hints == null)
475: hints = new RenderingHints(null);
476:
477: switch (s.charAt(8)) {
478: case 's': // optimizeSpeed
479: hints.put(RenderingHints.KEY_RENDERING,
480: RenderingHints.VALUE_RENDER_SPEED);
481: hints
482: .put(
483: RenderingHints.KEY_INTERPOLATION,
484: RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
485: break;
486: case 'q': // optimizeQuality
487: hints.put(RenderingHints.KEY_RENDERING,
488: RenderingHints.VALUE_RENDER_QUALITY);
489: hints.put(RenderingHints.KEY_INTERPOLATION,
490: RenderingHints.VALUE_INTERPOLATION_BICUBIC);
491: break;
492: }
493: return hints;
494: }
495:
496: /**
497: * Fills the rendering hints for the specified element or do
498: * nothing if none has been specified. If the given RenderingHints
499: * is null, a new one is created. Checks the 'color-rendering'
500: * property.
501: *
502: * <p>Here is how the mapping between SVG rendering hints and the Java2D
503: * rendering hints is done:</p>
504: *
505: * <dl>
506: * <dt>'optimizeSpeed':</dt>
507: * <dd>
508: * <ul>
509: * <li>KEY_COLOR_RENDERING=VALUE_COLOR_RENDER_SPEED</li>
510: * <li>KEY_ALPHA_INTERPOLATION=VALUE_ALPHA_INTERPOLATION_SPEED</li>
511: * </ul>
512: * </dd>
513: * <dt>'optimizeQuality':</dt>
514: * <dd>
515: * <ul>
516: * <li>KEY_COLOR_RENDERING=VALUE_COLOR_RENDER_QUALITY</li>
517: * <li>KEY_ALPHA_INTERPOLATION=VALUE_ALPHA_INTERPOLATION_QUALITY</li>
518: * </ul>
519: * </dd>
520: * </dl>
521: *
522: * @param e the element
523: * @param hints a RenderingHints to fill, or null.
524: */
525: public static RenderingHints convertColorRendering(Element e,
526: RenderingHints hints) {
527: Value v = getComputedStyle(e,
528: SVGCSSEngine.COLOR_RENDERING_INDEX);
529: String s = v.getStringValue();
530: int len = s.length();
531: if ((len == 4) && (s.charAt(0) == 'a')) // auto
532: return hints;
533: if (len < 13)
534: return hints; // Unknown.
535:
536: if (hints == null)
537: hints = new RenderingHints(null);
538:
539: switch (s.charAt(8)) {
540: case 's': // optimizeSpeed
541: hints.put(RenderingHints.KEY_COLOR_RENDERING,
542: RenderingHints.VALUE_COLOR_RENDER_SPEED);
543: hints.put(RenderingHints.KEY_ALPHA_INTERPOLATION,
544: RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
545: break;
546: case 'q': // optimizeQuality
547: hints.put(RenderingHints.KEY_COLOR_RENDERING,
548: RenderingHints.VALUE_COLOR_RENDER_QUALITY);
549: hints.put(RenderingHints.KEY_ALPHA_INTERPOLATION,
550: RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
551: break;
552: }
553: return hints;
554: }
555:
556: /////////////////////////////////////////////////////////////////////////
557: // 'display'
558: /////////////////////////////////////////////////////////////////////////
559:
560: /**
561: * Returns true if the specified element has to be displayed, false
562: * otherwise. Checks the 'display' property.
563: *
564: * @param e the element
565: */
566: public static boolean convertDisplay(Element e) {
567: if (!(e instanceof CSSStylableElement)) {
568: return true;
569: }
570: Value v = getComputedStyle(e, SVGCSSEngine.DISPLAY_INDEX);
571: return v.getStringValue().charAt(0) != 'n';
572: }
573:
574: /////////////////////////////////////////////////////////////////////////
575: // 'visibility'
576: /////////////////////////////////////////////////////////////////////////
577:
578: /**
579: * Returns true if the specified element is visible, false
580: * otherwise. Checks the 'visibility' property.
581: *
582: * @param e the element
583: */
584: public static boolean convertVisibility(Element e) {
585: Value v = getComputedStyle(e, SVGCSSEngine.VISIBILITY_INDEX);
586: return v.getStringValue().charAt(0) == 'v';
587: }
588:
589: /////////////////////////////////////////////////////////////////////////
590: // 'opacity'
591: /////////////////////////////////////////////////////////////////////////
592:
593: public static final Composite TRANSPARENT = AlphaComposite
594: .getInstance(AlphaComposite.SRC_OVER, 0);
595:
596: /**
597: * Returns a composite object that represents the 'opacity' of the
598: * specified element.
599: *
600: * @param e the element
601: */
602: public static Composite convertOpacity(Element e) {
603: Value v = getComputedStyle(e, SVGCSSEngine.OPACITY_INDEX);
604: float f = v.getFloatValue();
605: if (f <= 0f) {
606: return TRANSPARENT;
607: } else if (f >= 1.0f) {
608: return AlphaComposite.SrcOver;
609: } else {
610: return AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
611: f);
612: }
613: }
614:
615: /////////////////////////////////////////////////////////////////////////
616: // 'overflow' and 'clip'
617: /////////////////////////////////////////////////////////////////////////
618:
619: /**
620: * Returns true if the 'overflow' property indicates that an
621: * additional clip is required, false otherwise. An additional
622: * clip is needed if the 'overflow' property is 'scroll' or
623: * 'hidden'.
624: *
625: * @param e the element with the 'overflow' property
626: */
627: public static boolean convertOverflow(Element e) {
628: Value v = getComputedStyle(e, SVGCSSEngine.OVERFLOW_INDEX);
629: String s = v.getStringValue();
630: return (s.charAt(0) == 'h') || (s.charAt(0) == 's');
631: }
632:
633: /**
634: * Returns an array of floating offsets representing the 'clip'
635: * property or null if 'auto'. The offsets are specified in the
636: * order top, right, bottom, left.
637: *
638: * @param e the element with the 'clip' property
639: */
640: public static float[] convertClip(Element e) {
641: Value v = getComputedStyle(e, SVGCSSEngine.CLIP_INDEX);
642: int primitiveType = v.getPrimitiveType();
643: switch (primitiveType) {
644: case CSSPrimitiveValue.CSS_RECT:
645: float[] off = new float[4];
646: off[0] = v.getTop().getFloatValue();
647: off[1] = v.getRight().getFloatValue();
648: off[2] = v.getBottom().getFloatValue();
649: off[3] = v.getLeft().getFloatValue();
650: return off;
651: case CSSPrimitiveValue.CSS_IDENT:
652: return null; // 'auto' means no offsets
653: default:
654: // can't be reached
655: throw new IllegalStateException("Unexpected primitiveType:"
656: + primitiveType);
657: }
658: }
659:
660: /////////////////////////////////////////////////////////////////////////
661: // 'filter'
662: /////////////////////////////////////////////////////////////////////////
663:
664: /**
665: * Returns a <tt>Filter</tt> referenced by the specified element
666: * and which applies on the specified graphics node.
667: * Handle the 'filter' property.
668: *
669: * @param filteredElement the element that references the filter
670: * @param filteredNode the graphics node associated to the element
671: * to filter.
672: * @param ctx the bridge context
673: */
674: public static Filter convertFilter(Element filteredElement,
675: GraphicsNode filteredNode, BridgeContext ctx) {
676: Value v = getComputedStyle(filteredElement,
677: SVGCSSEngine.FILTER_INDEX);
678: int primitiveType = v.getPrimitiveType();
679: switch (primitiveType) {
680: case CSSPrimitiveValue.CSS_IDENT:
681: return null; // 'filter:none'
682:
683: case CSSPrimitiveValue.CSS_URI:
684: String uri = v.getStringValue();
685: Element filter = ctx.getReferencedElement(filteredElement,
686: uri);
687: Bridge bridge = ctx.getBridge(filter);
688: if (bridge == null || !(bridge instanceof FilterBridge)) {
689: throw new BridgeException(ctx, filteredElement,
690: ERR_CSS_URI_BAD_TARGET, new Object[] { uri });
691: }
692: return ((FilterBridge) bridge).createFilter(ctx, filter,
693: filteredElement, filteredNode);
694: default:
695: throw new IllegalStateException(
696: "Unexpected primitive type:" + primitiveType); // can't be reached
697:
698: }
699: }
700:
701: /////////////////////////////////////////////////////////////////////////
702: // 'clip-path' and 'clip-rule'
703: /////////////////////////////////////////////////////////////////////////
704:
705: /**
706: * Returns a <tt>Clip</tt> referenced by the specified element and
707: * which applies on the specified graphics node.
708: * Handle the 'clip-path' property.
709: *
710: * @param clippedElement the element that references the clip
711: * @param clippedNode the graphics node associated to the element to clip
712: * @param ctx the bridge context
713: */
714: public static ClipRable convertClipPath(Element clippedElement,
715: GraphicsNode clippedNode, BridgeContext ctx) {
716: Value v = getComputedStyle(clippedElement,
717: SVGCSSEngine.CLIP_PATH_INDEX);
718: int primitiveType = v.getPrimitiveType();
719: switch (primitiveType) {
720: case CSSPrimitiveValue.CSS_IDENT:
721: return null; // 'clip-path:none'
722:
723: case CSSPrimitiveValue.CSS_URI:
724: String uri = v.getStringValue();
725: Element cp = ctx.getReferencedElement(clippedElement, uri);
726: Bridge bridge = ctx.getBridge(cp);
727: if (bridge == null || !(bridge instanceof ClipBridge)) {
728: throw new BridgeException(ctx, clippedElement,
729: ERR_CSS_URI_BAD_TARGET, new Object[] { uri });
730: }
731: return ((ClipBridge) bridge).createClip(ctx, cp,
732: clippedElement, clippedNode);
733: default:
734: throw new IllegalStateException(
735: "Unexpected primitive type:" + primitiveType); // can't be reached
736: }
737: }
738:
739: /**
740: * Returns the 'clip-rule' for the specified element.
741: *
742: * @param e the element interested in its a 'clip-rule'
743: * @return GeneralPath.WIND_NON_ZERO | GeneralPath.WIND_EVEN_ODD
744: */
745: public static int convertClipRule(Element e) {
746: Value v = getComputedStyle(e, SVGCSSEngine.CLIP_RULE_INDEX);
747: return (v.getStringValue().charAt(0) == 'n') ? GeneralPath.WIND_NON_ZERO
748: : GeneralPath.WIND_EVEN_ODD;
749: }
750:
751: /////////////////////////////////////////////////////////////////////////
752: // 'mask'
753: /////////////////////////////////////////////////////////////////////////
754:
755: /**
756: * Returns a <tt>Mask</tt> referenced by the specified element and
757: * which applies on the specified graphics node.
758: * Handle the 'mask' property.
759: *
760: * @param maskedElement the element that references the mask
761: * @param maskedNode the graphics node associated to the element to mask
762: * @param ctx the bridge context
763: */
764: public static Mask convertMask(Element maskedElement,
765: GraphicsNode maskedNode, BridgeContext ctx) {
766: Value v = getComputedStyle(maskedElement,
767: SVGCSSEngine.MASK_INDEX);
768: int primitiveType = v.getPrimitiveType();
769: switch (primitiveType) {
770: case CSSPrimitiveValue.CSS_IDENT:
771: return null; // 'mask:none'
772:
773: case CSSPrimitiveValue.CSS_URI:
774: String uri = v.getStringValue();
775: Element m = ctx.getReferencedElement(maskedElement, uri);
776: Bridge bridge = ctx.getBridge(m);
777: if (bridge == null || !(bridge instanceof MaskBridge)) {
778: throw new BridgeException(ctx, maskedElement,
779: ERR_CSS_URI_BAD_TARGET, new Object[] { uri });
780: }
781: return ((MaskBridge) bridge).createMask(ctx, m,
782: maskedElement, maskedNode);
783: default:
784: throw new IllegalStateException(
785: "Unexpected primitive type:" + primitiveType); // can't be reached
786: }
787: }
788:
789: /**
790: * Returns the 'fill-rule' for the specified element.
791: *
792: * @param e the element interested in its a 'fill-rule'
793: * @return GeneralPath.WIND_NON_ZERO | GeneralPath.WIND_EVEN_ODD
794: */
795: public static int convertFillRule(Element e) {
796: Value v = getComputedStyle(e, SVGCSSEngine.FILL_RULE_INDEX);
797: return (v.getStringValue().charAt(0) == 'n') ? GeneralPath.WIND_NON_ZERO
798: : GeneralPath.WIND_EVEN_ODD;
799: }
800:
801: /////////////////////////////////////////////////////////////////////////
802: // 'lighting-color'
803: /////////////////////////////////////////////////////////////////////////
804:
805: /**
806: * Converts the color defined on the specified lighting filter element
807: * to a <tt>Color</tt>.
808: *
809: * @param e the lighting filter element
810: * @param ctx the bridge context
811: */
812: public static Color convertLightingColor(Element e,
813: BridgeContext ctx) {
814: Value v = getComputedStyle(e, SVGCSSEngine.LIGHTING_COLOR_INDEX);
815: if (v.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
816: return PaintServer.convertColor(v, 1);
817: } else {
818: return PaintServer.convertRGBICCColor(e, v.item(0),
819: (ICCColor) v.item(1), 1, ctx);
820: }
821: }
822:
823: /////////////////////////////////////////////////////////////////////////
824: // 'flood-color' and 'flood-opacity'
825: /////////////////////////////////////////////////////////////////////////
826:
827: /**
828: * Converts the color defined on the specified <feFlood>
829: * element to a <tt>Color</tt>.
830: *
831: * @param e the feFlood element
832: * @param ctx the bridge context
833: */
834: public static Color convertFloodColor(Element e, BridgeContext ctx) {
835: Value v = getComputedStyle(e, SVGCSSEngine.FLOOD_COLOR_INDEX);
836: Value o = getComputedStyle(e, SVGCSSEngine.FLOOD_OPACITY_INDEX);
837: float f = PaintServer.convertOpacity(o);
838: if (v.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
839: return PaintServer.convertColor(v, f);
840: } else {
841: return PaintServer.convertRGBICCColor(e, v.item(0),
842: (ICCColor) v.item(1), f, ctx);
843: }
844: }
845:
846: /////////////////////////////////////////////////////////////////////////
847: // 'stop-color'
848: /////////////////////////////////////////////////////////////////////////
849:
850: /**
851: * Converts the color defined on the specified <stop> element
852: * to a <tt>Color</tt>.
853: *
854: * @param e the stop element
855: * @param opacity the paint opacity
856: * @param ctx the bridge context to use
857: */
858: public static Color convertStopColor(Element e, float opacity,
859: BridgeContext ctx) {
860: Value v = getComputedStyle(e, SVGCSSEngine.STOP_COLOR_INDEX);
861: Value o = getComputedStyle(e, SVGCSSEngine.STOP_OPACITY_INDEX);
862: opacity *= PaintServer.convertOpacity(o);
863: if (v.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
864: return PaintServer.convertColor(v, opacity);
865: } else {
866: return PaintServer.convertRGBICCColor(e, v.item(0),
867: (ICCColor) v.item(1), opacity, ctx);
868: }
869: }
870:
871: /////////////////////////////////////////////////////////////////////////
872: // CSS support for <use>
873: /////////////////////////////////////////////////////////////////////////
874:
875: /**
876: * Partially computes the style in the 'def' tree and set it in the 'use'
877: * tree.
878: * <p>Note: This method must be called only when 'use' has been
879: * added to the DOM tree.
880: *
881: * @param refElement the referenced element
882: * @param localRefElement the referenced element in the current document
883: */
884: public static void computeStyleAndURIs(Element refElement,
885: Element localRefElement, String uri) {
886: // Pull fragement id off first...
887: int idx = uri.indexOf('#');
888: if (idx != -1)
889: uri = uri.substring(0, idx);
890:
891: // Only set xml:base if we have a real URL.
892: if (uri.length() != 0)
893: localRefElement.setAttributeNS(XML_NAMESPACE_URI, "base",
894: uri);
895:
896: CSSEngine engine = CSSUtilities.getCSSEngine(localRefElement);
897: CSSEngine refEngine = CSSUtilities.getCSSEngine(refElement);
898:
899: engine.importCascadedStyleMaps(refElement, refEngine,
900: localRefElement);
901: }
902:
903: /////////////////////////////////////////////////////////////////////////
904: // Additional utility methods used internally
905: /////////////////////////////////////////////////////////////////////////
906:
907: /**
908: * Returns the winding rule represented by the specified CSSValue.
909: *
910: * @param v the value that represents the rule
911: * @return GeneralPath.WIND_NON_ZERO | GeneralPath.WIND_EVEN_ODD
912: */
913: protected static int rule(CSSValue v) {
914: return (((CSSPrimitiveValue) v).getStringValue().charAt(0) == 'n') ? GeneralPath.WIND_NON_ZERO
915: : GeneralPath.WIND_EVEN_ODD;
916: }
917: }
|