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