    001/* ===================================================
    002 * JFreeSVG : an SVG library for the Java(tm) platform
    003 * ===================================================
    004 *
    005 * (C)opyright 2013-2015, 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}