001/* ===================================================
002 * JFreeSVG : an SVG library for the Java(tm) platform
003 * ===================================================
004 * 
005 * (C)opyright 2013-present, by David Gilbert.  All rights reserved.
006 *
007 * Project Info:  http://www.jfree.org/jfreesvg/index.html
008 * 
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as published by
011 * the Free Software Foundation, either version 3 of the License, or
012 * (at your option) any later version.
013 *
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 *
019 * You should have received a copy of the GNU General Public License
020 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
021 * 
022 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
023 * Other names may be trademarks of their respective owners.]
024 * 
025 * If you do not wish to be bound by the terms of the GPL, an alternative
026 * commercial license can be purchased.  For details, please see visit the
027 * JFreeSVG home page:
028 * 
029 * http://www.jfree.org/jfreesvg
030 */
031
032package org.jfree.svg;
033
034import java.awt.RenderingHints;
035import java.lang.reflect.Field;
036import java.util.ArrayList;
037import java.util.List;
038
039/**
040 * Defines the rendering hints that can be used with the {@link SVGGraphics2D} 
041 * class.  The supported hints are:<br>
042 * <ul>
043 * <li>{@link #KEY_IMAGE_HANDLING} that controls how images are handled 
044 * (embedded in the SVG, or referenced externally);</li>
045 * <li>{@link #KEY_IMAGE_HREF} that allows the caller to specify the image
046 * href attribute for the next image;</li>
047 * <li>{@link #KEY_TEXT_RENDERING} that allows configuration of the preferred 
048 * value of the SVG {@code text-rendering} attribute in text elements;</li>
049 * <li>{@link #KEY_ELEMENT_ID} that allows the caller to specify the element
050 * ID for the next element;</li>
051 * <li>{@link #KEY_BEGIN_GROUP} tells the {@code SVGGraphics2D} instance 
052 * to start a new group element with attributes controlled by the hint value
053 * (which may be a {@code String} for the group ID or, more generally, a
054 * {@code Map} containing arbitrary attribute values).  Any other
055 * {@code Graphics2D} implementation will ignore this hint;</li>
056 * <li>{@link #KEY_END_GROUP} tells the {@code SVGGraphics2D} instance 
057 * to end a group element.  The hint value is ignored.  The caller assumes 
058 * responsibility for balancing the number of {@code KEY_BEGIN_GROUP} and 
059 * {@code KEY_END_GROUP} hints.  Any other {@code Graphics2D} 
060 * implementation will ignore this hint.</li>
061 * </ul>
062 * 
063 */
064public final class SVGHints {
065
066    private SVGHints() {
067        // no need to instantiate this    
068    }
069    
070    /**
071     * The key for the hint that controls whether images are embedded in the
072     * SVG or referenced externally.  Valid hint values are 
073     * {@link #VALUE_IMAGE_HANDLING_EMBED} and 
074     * {@link #VALUE_IMAGE_HANDLING_REFERENCE}.
075     */
076    public static final SVGHints.Key KEY_IMAGE_HANDLING = new SVGHints.Key(0);
077    
078    /**
079     * Hint value for {@code KEY_IMAGE_HANDLING} to specify that images 
080     * should be embedded in the SVG output using PNG data {@code Base64} 
081     * encoded.
082     */
083    public static final Object VALUE_IMAGE_HANDLING_EMBED 
084            = "VALUE_IMAGE_HANDLING_EMBED";
085    
086    /**
087     * Hint value for {@code KEY_IMAGE_HANDLING} to say that images should
088     * be referenced externally.
089     */
090    public static final Object VALUE_IMAGE_HANDLING_REFERENCE 
091            = "VALUE_IMAGE_HANDLING_REFERENCE";
092    
093    /**
094     * The key for a hint that permits configuration of the <a 
095     * href="https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/text-rendering">text-rendering 
096     * attribute</a> in SVG text elements
097     */
098    public static final SVGHints.Key KEY_TEXT_RENDERING = new SVGHints.Key(1);
099     
100    /**
101     * Hint value for {@code KEY_TEXT_RENDERING} to set the 
102     * {@code text-rendering} attribute in SVG text elements to 'auto'. 
103     */
104    public static final String VALUE_TEXT_RENDERING_AUTO = "auto";
105    
106    /**
107     * Hint value for {@code KEY_TEXT_RENDERING} to set the 
108     * {@code text-rendering} attribute in SVG text elements to 
109     * 'optimizeSpeed'. 
110     */
111    public static final String VALUE_TEXT_RENDERING_SPEED = "optimizeSpeed";
112    
113    /**
114     * Hint value for {@code KEY_TEXT_RENDERING} to set the 
115     * {@code text-rendering} attribute in SVG text elements to 
116     * 'optimizeLegibility'. 
117     */
118    public static final String VALUE_TEXT_RENDERING_LEGIBILITY 
119            = "optimizeLegibility";
120    
121    /**
122     * Hint value for {@code KEY_TEXT_RENDERING} to set the 
123     * {@code text-rendering} attribute in SVG text elements to 
124     * 'geometricPrecision'. 
125     */
126    public static final String VALUE_TEXT_RENDERING_PRECISION 
127            = "geometricPrecision";
128    
129    /**
130     * Hint value for {@code KEY_TEXT_RENDERING} to set the 
131     * {@code text-rendering} attribute in SVG text elements to 
132     * 'inherit'. 
133     */
134    public static final String VALUE_TEXT_RENDERING_INHERIT = "inherit";
135    
136    /**
137     * Hint key to supply string to be used as the href for an image that is 
138     * referenced rather than embedded.  The value associated with the key 
139     * should be a string and will be used for the next image element written 
140     * to the SVG output (and then the hint will be cleared).
141     * 
142     * @since 1.5
143     */
144    public static final SVGHints.Key KEY_IMAGE_HREF = new SVGHints.Key(2);
145    
146    /**
147     * Hint key to supply an element id for the next element generated.
148     * 
149     * @since 1.5
150     */
151    public static final SVGHints.Key KEY_ELEMENT_ID = new SVGHints.Key(3);
152
153    /**
154     * Hint key that informs the {@code SVGGraphics2D} that the caller 
155     * would like to begin a new group element.  The hint value is either:
156     *
157     * <ul>
158     *     <li>a {@code String} that will be used as the value of the
159     *     {@code id} attribute for the group; or</li>
160     *     <li>a {@code Map} instance containing arbitrary attribute values for the
161     *     group (usually including an {@code id}).</li>
162     * </ul>
163     * After opening the new group the hint is cleared and it is the caller's
164     * responsibility to close the group later using
165     * {@link SVGHints#KEY_END_GROUP}.  Groups can be nested.
166     * 
167     * @since 1.7
168     */
169    public static final SVGHints.Key KEY_BEGIN_GROUP = new SVGHints.Key(4);
170
171    /**
172     * Hint key that informs the {@code SVGGraphics2D} that the caller
173     * would like to close a previously opened group element.  The hint
174     * value is ignored.
175     * 
176     * @since 1.7
177     */
178    public static final SVGHints.Key KEY_END_GROUP = new SVGHints.Key(5);
179
180    /**
181     * Hint key that informs the {@code SVGGraphics2D} that the caller
182     * would like to add a title element to the output (with the hint value
183     * being a string containing the title text).
184     * 
185     * @since 1.9
186     */
187    public static final SVGHints.Key KEY_ELEMENT_TITLE = new SVGHints.Key(6);
188
189    /**
190     * The key for the hint that controls whether strings are rendered as
191     * characters or vector graphics (implemented using {@code TextLayout}).  
192     * The latter will result in larger output files but avoids problems with
193     * fonts not being available for the viewer.  Valid hint values are 
194     * {@link #VALUE_DRAW_STRING_TYPE_STANDARD} and 
195     * {@link #VALUE_DRAW_STRING_TYPE_VECTOR}.
196     * 
197     * @since 2.0
198     */
199    public static final SVGHints.Key KEY_DRAW_STRING_TYPE = new SVGHints.Key(7);
200    
201    /**
202     * Hint value for {@code KEY_DRAW_STRING_TYPE} to specify that strings
203     * should be written to the output using standard SVG text elements.
204     * 
205     * @since 2.0
206     */
207    public static final Object VALUE_DRAW_STRING_TYPE_STANDARD 
208            = "VALUE_DRAW_STRING_TYPE_STANDARD";
209    
210    /**
211     * Hint value for {@code KEY_DRAW_STRING_TYPE} to say that strings
212     * should be written to the output using vector graphics primitives.
213     * 
214     * @since 2.0
215     */
216    public static final Object VALUE_DRAW_STRING_TYPE_VECTOR
217            = "VALUE_DRAW_STRING_TYPE_VECTOR";
218    
219    /**
220     * A list of keys that are treated as synonyms for KEY_BEGIN_GROUP
221     * (the list does not include KEY_BEGIN_GROUP itself).
222     */
223    private static final List<RenderingHints.Key> beginGroupKeys;
224    
225    /**
226     * A list of keys that are treated as synonyms for KEY_END_GROUP
227     * (the list does not include KEY_END_GROUP itself).
228     */
229    private static final List<RenderingHints.Key> endGroupKeys;
230    
231    /**
232     * A list of keys that are treated as synonyms for KEY_ELEMENT_TITLE
233     * (the list does not include KEY_ELEMENT_TITLE itself).
234     */
235    private static final List<RenderingHints.Key> elementTitleKeys;
236    
237    static {
238        beginGroupKeys = new ArrayList<>();
239        endGroupKeys = new ArrayList<>();
240        elementTitleKeys = new ArrayList<>();
241        if (isOrsonChartsOnClasspath()) {
242            beginGroupKeys.add(getOrsonChartsBeginElementKey());
243            endGroupKeys.add(getOrsonChartsEndElementKey());
244            elementTitleKeys.add(getOrsonChartsElementTitleKey());
245        }
246        if (isJFreeChartOnClasspath()) {
247            beginGroupKeys.add(getJFreeChartBeginElementKey());
248            endGroupKeys.add(getJFreeChartEndElementKey());
249        }
250    }
251    
252    /**
253     * Creates and returns a list of keys that are synonymous with 
254     * {@link #KEY_BEGIN_GROUP}.
255     * 
256     * @return A list (never {@code null}).
257     * 
258     * @since 1.8
259     */
260    public static List<RenderingHints.Key> getBeginGroupKeys() {
261        return new ArrayList<>(beginGroupKeys);    
262    }
263    
264    /**
265     * Adds a key to the list of keys that are synonyms for 
266     * {@link SVGHints#KEY_BEGIN_GROUP}.
267     * 
268     * @param key  the key ({@code null} not permitted).
269     * 
270     * @since 1.8
271     */
272    public static void addBeginGroupKey(RenderingHints.Key key) {
273        beginGroupKeys.add(key);
274    }
275    
276    /**
277     * Removes a key from the list of keys that are synonyms for
278     * {@link SVGHints#KEY_BEGIN_GROUP}.
279     * 
280     * @param key  the key ({@code null} not permitted).
281     * 
282     * @since 1.8
283     */
284    public static void removeBeginGroupKey(RenderingHints.Key key) {
285        beginGroupKeys.remove(key);
286    }
287    
288    /**
289     * Clears the list of keys that are treated as synonyms for 
290     * {@link SVGHints#KEY_BEGIN_GROUP}.
291     * 
292     * @since 1.8
293     */
294    public static void clearBeginGroupKeys() {
295        beginGroupKeys.clear();
296    }
297    
298    /**
299     * Returns {@code true} if this key is equivalent to 
300     * {@link #KEY_BEGIN_GROUP}, and {@code false} otherwise.  The purpose 
301     * of this method is to allow certain keys from external packages (such as 
302     * JFreeChart and Orson Charts) to use their own keys to drive the 
303     * behaviour of {@code SVGHints.KEY_BEGIN_GROUP}.  This has two benefits: 
304     * (1) it avoids the necessity to make JFreeSVG a direct dependency, and 
305     * (2) it makes the grouping behaviour generic from the point of view of 
306     * the external package, rather than SVG-specific.
307     * 
308     * @param key  the key ({@code null} not permitted)
309     * 
310     * @return A boolean.
311     * 
312     * @since 1.8
313     */
314    public static boolean isBeginGroupKey(RenderingHints.Key key) {
315        return SVGHints.KEY_BEGIN_GROUP.equals(key) 
316                || beginGroupKeys.contains(key);        
317    }
318
319    /**
320     * Creates and returns a list of keys that are synonymous with 
321     * {@link #KEY_END_GROUP}.
322     * 
323     * @return A list (never {@code null}).
324     * 
325     * @since 1.8
326     */
327    public static List<RenderingHints.Key> getEndGroupKeys() {
328        return new ArrayList<>(endGroupKeys);    
329    }
330    
331    /**
332     * Adds a key to the list of keys that are synonyms for 
333     * {@link SVGHints#KEY_END_GROUP}.
334     * 
335     * @param key  the key ({@code null} not permitted).
336     * 
337     * @since 1.8
338     */
339    public static void addEndGroupKey(RenderingHints.Key key) {
340        endGroupKeys.add(key);
341    }
342    
343    /**
344     * Removes a key from the list of keys that are synonyms for
345     * {@link SVGHints#KEY_END_GROUP}.
346     * 
347     * @param key  the key ({@code null} not permitted).
348     * 
349     * @since 1.8
350     */
351    public static void removeEndGroupKey(RenderingHints.Key key) {
352        endGroupKeys.remove(key);
353    }
354    
355    /**
356     * Clears the list of keys that are treated as synonyms for 
357     * {@link SVGHints#KEY_END_GROUP}.
358     * 
359     * @since 1.8
360     */
361    public static void clearEndGroupKeys() {
362        endGroupKeys.clear();
363    }
364    
365    /**
366     * Returns {@code true} if this key is equivalent to 
367     * {@link #KEY_END_GROUP}, and {@code false} otherwise.  The purpose 
368     * of this method is to allow certain keys from external packages (such as 
369     * JFreeChart and Orson Charts) to use their own keys to drive the 
370     * behaviour of {@code SVGHints.KEY_END_GROUP}.  This has two benefits: 
371     * (1) it avoids the necessity to make JFreeSVG a direct dependency, and 
372     * (2) it makes the grouping behaviour generic from the point of view of 
373     * the external package, rather than SVG-specific.
374     * 
375     * @param key  the key ({@code null} not permitted).
376     * 
377     * @return A boolean.
378     * 
379     * @since 1.8
380     */
381    public static boolean isEndGroupKey(RenderingHints.Key key) {
382        return SVGHints.KEY_END_GROUP.equals(key) || endGroupKeys.contains(key);        
383    }
384
385    /**
386     * Creates and returns a list of keys that are synonymous with 
387     * {@link #KEY_ELEMENT_TITLE}.
388     * 
389     * @return A list (never {@code null}).
390     * 
391     * @since 1.9
392     */
393    public static List<RenderingHints.Key> getElementTitleKeys() {
394        return new ArrayList<>(elementTitleKeys);    
395    }
396    
397    /**
398     * Adds a key to the list of keys that are synonyms for 
399     * {@link SVGHints#KEY_ELEMENT_TITLE}.
400     * 
401     * @param key  the key ({@code null} not permitted).
402     * 
403     * @since 1.9
404     */
405    public static void addElementTitleKey(RenderingHints.Key key) {
406        elementTitleKeys.add(key);
407    }
408    
409    /**
410     * Removes a key from the list of keys that are synonyms for
411     * {@link SVGHints#KEY_ELEMENT_TITLE}.
412     * 
413     * @param key  the key ({@code null} not permitted).
414     * 
415     * @since 1.9
416     */
417    public static void removeElementTitleKey(RenderingHints.Key key) {
418        elementTitleKeys.remove(key);
419    }
420    
421    /**
422     * Clears the list of keys that are treated as synonyms for 
423     * {@link SVGHints#KEY_ELEMENT_TITLE}.
424     * 
425     * @since 1.9
426     */
427    public static void clearElementTitleKeys() {
428        elementTitleKeys.clear();
429    }
430    
431    /**
432     * Returns {@code true} if this key is equivalent to 
433     * {@link #KEY_ELEMENT_TITLE}, and {@code false} otherwise.  The 
434     * purpose of this method is to allow certain keys from external packages 
435     * (such as JFreeChart and Orson Charts) to use their own keys to drive the 
436     * behaviour of {@code SVGHints.KEY_ELEMENT_TITLE}.  This has two benefits: 
437     * (1) it avoids the necessity to make JFreeSVG a direct dependency, and 
438     * (2) it makes the element title behaviour generic from the point of view 
439     * of the external package, rather than SVG-specific.
440     * 
441     * @param key  the key ({@code null} not permitted)
442     * 
443     * @return A boolean.
444     * 
445     * @since 1.9
446     */
447    public static boolean isElementTitleKey(RenderingHints.Key key) {
448        return SVGHints.KEY_ELEMENT_TITLE.equals(key) 
449                || elementTitleKeys.contains(key);        
450    }
451
452    /**
453     * Returns {@code true} if Orson Charts (version 1.3 or later) is on 
454     * the classpath, and {@code false} otherwise.  This method is used to
455     * auto-register keys from Orson Charts that should translate to the 
456     * behaviour of {@link SVGHints#KEY_BEGIN_GROUP} and 
457     * {@link SVGHints#KEY_END_GROUP}.
458     * <br><br>
459     * The Orson Charts library can be found at
460     * http://www.object-refinery.com/orsoncharts/
461     * 
462     * @return A boolean.
463     * 
464     * @since 1.8
465     */
466    private static boolean isOrsonChartsOnClasspath() {
467        return (getOrsonChartsBeginElementKey() != null);
468    }
469    
470    /**
471     * Returns {@code true} if JFreeChart (1.0.18 or later) is on 
472     * the classpath, and {@code false} otherwise.  This method is used to
473     * auto-register keys from JFreeChart that should translate to the 
474     * behaviour of {@link SVGHints#KEY_BEGIN_GROUP} and 
475     * {@link SVGHints#KEY_END_GROUP}.
476     * 
477     * <p>The JFreeChart library can be found at <a href="http://www.jfree.org/jfreechart/">
478     * http://www.jfree.org/jfreechart/</a>.
479     * 
480     * @return A boolean.
481     * 
482     * @since 2.0
483     */
484    private static boolean isJFreeChartOnClasspath() {
485        return (getJFreeChartBeginElementKey() != null);
486    }
487
488    private static RenderingHints.Key fetchKey(String className, 
489            String fieldName) {
490        Class<?> hintsClass;
491        try {
492            hintsClass = Class.forName(className);
493            Field f = hintsClass.getDeclaredField(fieldName);
494            return (RenderingHints.Key) f.get(null);
495        } catch (ClassNotFoundException | NoSuchFieldException | SecurityException | IllegalArgumentException |
496                 IllegalAccessException e) {
497            return null;
498        }
499    }
500    
501    private static RenderingHints.Key getOrsonChartsBeginElementKey() {
502        return fetchKey("com.orsoncharts.Chart3DHints", "KEY_BEGIN_ELEMENT");
503    }
504
505    private static RenderingHints.Key getOrsonChartsEndElementKey() {
506        return fetchKey("com.orsoncharts.Chart3DHints", "KEY_END_ELEMENT");
507    }
508
509    private static RenderingHints.Key getOrsonChartsElementTitleKey() {
510        return fetchKey("com.orsoncharts.Chart3DHints", "KEY_ELEMENT_TITLE");
511    }
512
513    private static RenderingHints.Key getJFreeChartBeginElementKey() {
514        return fetchKey("org.jfree.chart.ChartHints", "KEY_BEGIN_ELEMENT");
515    }
516
517    private static RenderingHints.Key getJFreeChartEndElementKey() {
518        return fetchKey("org.jfree.chart.ChartHints", "KEY_END_ELEMENT");
519    }
520
521    /**
522     * A key for hints used by the {@link SVGGraphics2D} class.
523     */
524    public static class Key extends RenderingHints.Key {
525
526        /**
527         * Creates a new instance.
528         * 
529         * @param privateKey  the private key. 
530         */
531        public Key(int privateKey) {
532            super(privateKey);    
533        }
534    
535        /**
536         * Returns {@code true} if {@code val} is a value that is
537         * compatible with this key, and {@code false} otherwise.
538         * 
539         * @param val  the value.
540         * 
541         * @return A boolean. 
542         */
543        @Override
544        public boolean isCompatibleValue(Object val) {
545            switch (intKey()) {
546                case 0:
547                    return VALUE_IMAGE_HANDLING_EMBED.equals(val)
548                            || VALUE_IMAGE_HANDLING_REFERENCE.equals(val);
549                case 1:
550                    return VALUE_TEXT_RENDERING_AUTO.equals(val)
551                            || VALUE_TEXT_RENDERING_INHERIT.equals(val)
552                            || VALUE_TEXT_RENDERING_LEGIBILITY.equals(val)
553                            || VALUE_TEXT_RENDERING_PRECISION.equals(val)
554                            || VALUE_TEXT_RENDERING_SPEED.equals(val);
555                case 2: // KEY_IMAGE:URL
556                case 3: // KEY_ELEMENT_ID
557                case 4: // KEY_BEGIN_GROUP
558                    return val == null || val instanceof String;
559                case 5: // KEY_END_GROUP
560                    return true; // the value is ignored
561                case 6: // KEY_ELEMENT_TITLE
562                    return val instanceof String;
563                case 7:
564                    return val == null 
565                            || VALUE_DRAW_STRING_TYPE_STANDARD.equals(val)
566                            || VALUE_DRAW_STRING_TYPE_VECTOR.equals(val);
567                default:
568                    throw new RuntimeException("Not possible!");
569            }
570        }
571    }
572    
573}