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}