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}