001/* 002 * Copyright 2006 - 2013 003 * Stefan Balev <stefan.balev@graphstream-project.org> 004 * Julien Baudry <julien.baudry@graphstream-project.org> 005 * Antoine Dutot <antoine.dutot@graphstream-project.org> 006 * Yoann Pigné <yoann.pigne@graphstream-project.org> 007 * Guilhelm Savin <guilhelm.savin@graphstream-project.org> 008 * 009 * This file is part of GraphStream <http://graphstream-project.org>. 010 * 011 * GraphStream is a library whose purpose is to handle static or dynamic 012 * graph, create them from scratch, file or any source and display them. 013 * 014 * This program is free software distributed under the terms of two licenses, the 015 * CeCILL-C license that fits European law, and the GNU Lesser General Public 016 * License. You can use, modify and/ or redistribute the software under the terms 017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following 018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by 019 * the Free Software Foundation, either version 3 of the License, or (at your 020 * option) any later version. 021 * 022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 024 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 025 * 026 * You should have received a copy of the GNU Lesser General Public License 027 * along with this program. If not, see <http://www.gnu.org/licenses/>. 028 * 029 * The fact that you are presently reading this means that you have had 030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. 031 */ 032package org.graphstream.stream.file; 033 034import java.awt.Color; 035import java.io.FileWriter; 036import java.io.IOException; 037import java.io.OutputStream; 038import java.io.OutputStreamWriter; 039import java.io.Writer; 040import java.util.HashMap; 041import java.util.HashSet; 042import java.util.Iterator; 043import java.util.Locale; 044 045import javax.xml.stream.FactoryConfigurationError; 046import javax.xml.stream.XMLOutputFactory; 047import javax.xml.stream.XMLStreamException; 048import javax.xml.stream.XMLStreamWriter; 049 050import org.graphstream.graph.Edge; 051import org.graphstream.graph.Element; 052import org.graphstream.graph.Graph; 053import org.graphstream.graph.Node; 054import org.graphstream.ui.graphicGraph.StyleGroup; 055import org.graphstream.ui.graphicGraph.StyleGroupSet; 056import org.graphstream.ui.graphicGraph.stylesheet.Colors; 057import org.graphstream.ui.graphicGraph.stylesheet.Selector; 058import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants; 059import org.graphstream.ui.graphicGraph.stylesheet.Value; 060import org.graphstream.ui.graphicGraph.stylesheet.Values; 061import org.graphstream.ui.graphicGraph.stylesheet.Selector.Type; 062import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants.StrokeMode; 063import org.graphstream.ui.graphicGraph.stylesheet.StyleSheet; 064 065public class FileSinkSVG2 implements FileSink { 066 067 /* 068 * (non-Javadoc) 069 * 070 * @see org.graphstream.stream.file.FileSink#begin(java.lang.String) 071 */ 072 public void begin(String fileName) throws IOException { 073 throw new UnsupportedOperationException(); 074 } 075 076 /* 077 * (non-Javadoc) 078 * 079 * @see org.graphstream.stream.file.FileSink#begin(java.io.OutputStream) 080 */ 081 public void begin(OutputStream stream) throws IOException { 082 throw new UnsupportedOperationException(); 083 } 084 085 /* 086 * (non-Javadoc) 087 * 088 * @see org.graphstream.stream.file.FileSink#begin(java.io.Writer) 089 */ 090 public void begin(Writer writer) throws IOException { 091 throw new UnsupportedOperationException(); 092 } 093 094 /* 095 * (non-Javadoc) 096 * 097 * @see org.graphstream.stream.file.FileSink#end() 098 */ 099 public void end() throws IOException { 100 throw new UnsupportedOperationException(); 101 } 102 103 /* 104 * (non-Javadoc) 105 * 106 * @see org.graphstream.stream.file.FileSink#flush() 107 */ 108 public void flush() throws IOException { 109 } 110 111 /* 112 * (non-Javadoc) 113 * 114 * @see 115 * org.graphstream.stream.file.FileSink#writeAll(org.graphstream.graph.Graph 116 * , java.lang.String) 117 */ 118 public void writeAll(Graph graph, String fileName) throws IOException { 119 FileWriter out = new FileWriter(fileName); 120 writeAll(graph, out); 121 out.close(); 122 } 123 124 /* 125 * (non-Javadoc) 126 * 127 * @see 128 * org.graphstream.stream.file.FileSink#writeAll(org.graphstream.graph.Graph 129 * , java.io.OutputStream) 130 */ 131 public void writeAll(Graph graph, OutputStream stream) throws IOException { 132 OutputStreamWriter out = new OutputStreamWriter(stream); 133 writeAll(graph, out); 134 } 135 136 /* 137 * (non-Javadoc) 138 * 139 * @see 140 * org.graphstream.stream.file.FileSink#writeAll(org.graphstream.graph.Graph 141 * , java.io.Writer) 142 */ 143 public void writeAll(Graph g, Writer w) throws IOException { 144 XMLWriter out = new XMLWriter(); 145 SVGContext ctx = new SVGContext(); 146 147 try { 148 out.start(w); 149 } catch (XMLStreamException e) { 150 throw new IOException(e); 151 } catch (FactoryConfigurationError e) { 152 throw new RuntimeException(e); 153 } 154 155 try { 156 ctx.init(out, g); 157 ctx.writeElements(out, g); 158 ctx.end(out); 159 } catch (XMLStreamException e) { 160 throw new IOException(e); 161 } 162 163 try { 164 out.end(); 165 } catch (XMLStreamException e) { 166 throw new IOException(e); 167 } 168 } 169 170 private static String d(double d) { 171 return String.format(Locale.ROOT, "%f", d); 172 } 173 174 private static double getX(Node n) { 175 if (n.hasNumber("x")) 176 return n.getNumber("x"); 177 178 if (n.hasArray("xy")) { 179 Object[] xy = n.getArray("xy"); 180 181 if (xy != null && xy.length > 0 && xy[0] instanceof Number) 182 return ((Number) xy[0]).doubleValue(); 183 } 184 185 if (n.hasArray("xyz")) { 186 Object[] xyz = n.getArray("xyz"); 187 188 if (xyz != null && xyz.length > 0 && xyz[0] instanceof Number) 189 return ((Number) xyz[0]).doubleValue(); 190 } 191 192 System.err.printf("[WARNING] no x attribute for node \"%s\" %s\n", 193 n.getId(), n.hasAttribute("xyz")); 194 195 return Math.random(); 196 } 197 198 private static double getY(Node n) { 199 if (n.hasNumber("y")) 200 return n.getNumber("y"); 201 202 if (n.hasArray("xy")) { 203 Object[] xy = n.getArray("xy"); 204 205 if (xy != null && xy.length > 1 && xy[1] instanceof Number) 206 return ((Number) xy[1]).doubleValue(); 207 } 208 209 if (n.hasArray("xyz")) { 210 Object[] xyz = n.getArray("xyz"); 211 212 if (xyz != null && xyz.length > 1 && xyz[1] instanceof Number) 213 return ((Number) xyz[1]).doubleValue(); 214 } 215 216 return Math.random(); 217 } 218 219 private static String getSize(Value v) { 220 String u = v.units.name().toLowerCase(); 221 return String.format(Locale.ROOT, "%f%s", v.value, u); 222 } 223 224 private static String getSize(Values v, int index) { 225 String u = v.units.name().toLowerCase(); 226 return String.format(Locale.ROOT, "%f%s", v.get(index), u); 227 } 228 229 static class SVGContext { 230 StyleGroupSet groups; 231 StyleSheet stylesheet; 232 HashMap<StyleGroup, SVGStyle> svgStyles; 233 ViewBox viewBox; 234 235 public SVGContext() { 236 stylesheet = new StyleSheet(); 237 groups = new StyleGroupSet(stylesheet); 238 svgStyles = new HashMap<StyleGroup, SVGStyle>(); 239 viewBox = new ViewBox(0, 0, 1000, 1000); 240 } 241 242 public void init(XMLWriter out, Graph g) throws IOException, 243 XMLStreamException { 244 245 if (g.hasAttribute("ui.stylesheet")) { 246 stylesheet.load(((String) g.getAttribute("ui.stylesheet"))); 247 } 248 249 groups.addElement(g); 250 viewBox.compute(g, groups.getStyleFor(g)); 251 252 out.open("svg"); 253 out.attribute("xmlns", "http://www.w3.org/2000/svg"); 254 out.attribute("xmlns:dc", "http://purl.org/dc/elements/1.1/"); 255 out.attribute("xmlns:cc", "http://creativecommons.org/ns#"); 256 out.attribute("xmlns:rdf", 257 "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); 258 out.attribute("xmlns:svg", "http://www.w3.org/2000/svg"); 259 260 out.attribute("viewBox", String.format(Locale.ROOT, "%f %f %f %f", 261 viewBox.x1, viewBox.y1, viewBox.x2, viewBox.y2)); 262 263 out.attribute("id", g.getId()); 264 out.attribute("version", "1.1"); 265 266 for (Edge e : g.getEachEdge()) { 267 groups.addElement(e); 268 269 if (e.hasAttribute("ui.style")) 270 stylesheet.parseStyleFromString( 271 new Selector(Type.EDGE, e.getId(), null), 272 (String) e.getAttribute("ui.style")); 273 274 groups.checkElementStyleGroup(e); 275 } 276 277 for (Node n : g.getEachNode()) { 278 groups.addElement(n); 279 280 if (n.hasAttribute("ui.style")) 281 stylesheet.parseStyleFromString( 282 new Selector(Type.NODE, n.getId(), null), 283 (String) n.getAttribute("ui.style")); 284 285 groups.checkElementStyleGroup(n); 286 } 287 288 for (StyleGroup group : groups.groups()) 289 svgStyles.put(group, new SVGStyle(group)); 290 291 out.open("defs"); 292 for (SVGStyle svgStyle : svgStyles.values()) 293 svgStyle.writeDef(out); 294 out.close(); 295 } 296 297 public void end(XMLWriter out) throws XMLStreamException { 298 out.close(); 299 } 300 301 public void writeElements(XMLWriter out, Graph g) 302 throws XMLStreamException { 303 out.open("g"); 304 out.attribute("id", "graph-misc"); 305 writeElement(out, g); 306 out.close(); 307 308 Iterator<HashSet<StyleGroup>> it = groups.getZIterator(); 309 310 out.open("g"); 311 out.attribute("id", "elements"); 312 313 while (it.hasNext()) { 314 HashSet<StyleGroup> set = it.next(); 315 316 for (StyleGroup sg : set) 317 for (Element e : sg.elements()) 318 writeElement(out, e); 319 } 320 321 out.close(); 322 } 323 324 public void writeElement(XMLWriter out, Element e) 325 throws XMLStreamException { 326 String id = ""; 327 SVGStyle style = null; 328 String transform = null; 329 330 if (e instanceof Edge) { 331 id = String.format("egde-%s", e.getId()); 332 style = svgStyles.get(groups.getStyleFor((Edge) e)); 333 } else if (e instanceof Node) { 334 id = String.format("node-%s", e.getId()); 335 style = svgStyles.get(groups.getStyleFor((Node) e)); 336 transform = String.format(Locale.ROOT, "translate(%f,%f)", 337 viewBox.convertX((Node) e), viewBox.convertY((Node) e)); 338 } else if (e instanceof Graph) { 339 id = "graph-background"; 340 style = svgStyles.get(groups.getStyleFor((Graph) e)); 341 } 342 343 out.open("g"); 344 out.attribute("id", id); 345 out.open("path"); 346 347 if (style != null) 348 out.attribute("style", style.getElementStyle(e)); 349 350 if (transform != null) 351 out.attribute("transform", transform); 352 353 out.attribute("d", getPath(e, style)); 354 out.close(); 355 356 if (e.hasLabel("label")) 357 writeElementText(out, (String) e.getAttribute("label"), e, 358 style.group); 359 360 out.close(); 361 } 362 363 public void writeElementText(XMLWriter out, String text, Element e, 364 StyleGroup style) throws XMLStreamException { 365 if (style == null 366 || style.getTextVisibilityMode() != StyleConstants.TextVisibilityMode.HIDDEN) { 367 double x, y; 368 369 x = 0; 370 y = 0; 371 372 if (e instanceof Node) { 373 x = viewBox.convertX((Node) e); 374 y = viewBox.convertY((Node) e); 375 } else if (e instanceof Edge) { 376 Node n0, n1; 377 378 n0 = ((Edge) e).getNode0(); 379 n1 = ((Edge) e).getNode0(); 380 381 x = viewBox.convertX((getX(n0) + getX(n1)) / 2); 382 y = viewBox.convertY((getY(n0) + getY(n1)) / 2); 383 } 384 385 out.open("g"); 386 out.open("text"); 387 out.attribute("x", d(x)); 388 out.attribute("y", d(y)); 389 390 if (style != null) { 391 if (style.getTextColorCount() > 0) 392 out.attribute("fill", toHexColor(style.getTextColor(0))); 393 394 switch (style.getTextAlignment()) { 395 case CENTER: 396 out.attribute("text-anchor", "middle"); 397 out.attribute("alignment-baseline", "central"); 398 break; 399 case LEFT: 400 out.attribute("text-anchor", "start"); 401 break; 402 case RIGHT: 403 out.attribute("text-anchor", "end"); 404 break; 405 default: 406 break; 407 } 408 409 switch (style.getTextSize().units) { 410 case PX: 411 case GU: 412 out.attribute("font-size", d(style.getTextSize().value)); 413 break; 414 case PERCENTS: 415 out.attribute("font-size", d(style.getTextSize().value) 416 + "%"); 417 break; 418 } 419 420 if (style.getTextFont() != null) 421 out.attribute("font-family", style.getTextFont()); 422 423 switch (style.getTextStyle()) { 424 case NORMAL: 425 break; 426 case ITALIC: 427 out.attribute("font-style", "italic"); 428 break; 429 case BOLD: 430 out.attribute("font-weight", "bold"); 431 break; 432 case BOLD_ITALIC: 433 out.attribute("font-weight", "bold"); 434 out.attribute("font-style", "italic"); 435 break; 436 } 437 } 438 439 out.characters(text); 440 out.close(); 441 out.close(); 442 } 443 } 444 445 public String getPath(Element e, SVGStyle style) { 446 StringBuilder buffer = new StringBuilder(); 447 448 if (e instanceof Node) { 449 double sx, sy; 450 Values size = style.group.getSize(); 451 452 sx = getValue(size.get(0), size.units, true); 453 454 if (size.getValueCount() > 1) 455 sy = getValue(size.get(1), size.units, false); 456 else 457 sy = getValue(size.get(0), size.units, false); 458 459 switch (style.group.getShape()) { 460 case ROUNDED_BOX: 461 double rx, 462 ry; 463 464 rx = Math.min(5, sx / 2); 465 ry = Math.min(5, sy / 2); 466 467 concat(buffer, " m ", d(-sx / 2 + rx), " ", d(-sy / 2)); 468 concat(buffer, " h ", d(sx - 2 * rx)); 469 concat(buffer, " a ", d(rx), ",", d(ry), " 0 0 1 ", d(rx), 470 ",", d(ry)); 471 concat(buffer, " v ", d(sy - 2 * ry)); 472 concat(buffer, " a ", d(rx), ",", d(ry), " 0 0 1 -", d(rx), 473 ",", d(ry)); 474 concat(buffer, " h ", d(-sx + 2 * rx)); 475 concat(buffer, " a ", d(rx), ",", d(ry), " 0 0 1 -", d(rx), 476 ",-", d(ry)); 477 concat(buffer, " v ", d(-sy + 2 * ry)); 478 concat(buffer, " a ", d(rx), ",", d(ry), " 0 0 1 ", d(rx), 479 "-", d(ry)); 480 concat(buffer, " z"); 481 break; 482 case BOX: 483 concat(buffer, " m ", d(-sx / 2), " ", d(-sy / 2)); 484 concat(buffer, " h ", d(sx)); 485 concat(buffer, " v ", d(sy)); 486 concat(buffer, " h ", d(-sx)); 487 concat(buffer, " z"); 488 break; 489 case DIAMOND: 490 concat(buffer, " m ", d(-sx / 2), " 0"); 491 concat(buffer, " l ", d(sx / 2), " ", d(-sy / 2)); 492 concat(buffer, " l ", d(sx / 2), " ", d(sy / 2)); 493 concat(buffer, " l ", d(-sx / 2), " ", d(sy / 2)); 494 concat(buffer, " z"); 495 break; 496 case TRIANGLE: 497 concat(buffer, " m ", d(0), " ", d(-sy / 2)); 498 concat(buffer, " l ", d(sx / 2), " ", d(sy)); 499 concat(buffer, " h ", d(-sx)); 500 concat(buffer, " z"); 501 break; 502 default: 503 case CIRCLE: 504 concat(buffer, " m ", d(-sx / 2), " 0"); 505 concat(buffer, " a ", d(sx / 2), ",", d(sy / 2), " 0 1 0 ", 506 d(sx), ",0"); 507 concat(buffer, " ", d(sx / 2), ",", d(sy / 2), " 0 1 0 -", 508 d(sx), ",0"); 509 concat(buffer, " z"); 510 break; 511 } 512 } else if (e instanceof Graph) { 513 concat(buffer, " M ", d(viewBox.x1), " ", d(viewBox.y1)); 514 concat(buffer, " L ", d(viewBox.x2), " ", d(viewBox.y1)); 515 concat(buffer, " L ", d(viewBox.x2), " ", d(viewBox.y2)); 516 concat(buffer, " L ", d(viewBox.x1), " ", d(viewBox.y2)); 517 concat(buffer, " Z"); 518 } else if (e instanceof Edge) { 519 Node src, trg; 520 521 double x1, y1; 522 double x2, y2; 523 524 src = ((Edge) e).getSourceNode(); 525 trg = ((Edge) e).getTargetNode(); 526 527 x1 = viewBox.convertX(src); 528 y1 = viewBox.convertY(src); 529 x2 = viewBox.convertX(trg); 530 y2 = viewBox.convertY(trg); 531 532 concat(buffer, " M ", d(x1), " ", d(y1)); 533 concat(buffer, " L ", d(x2), " ", d(y2)); 534 } 535 536 return buffer.toString(); 537 } 538 539 public double getValue(Value v, boolean horizontal) { 540 return getValue(v.value, v.units, horizontal); 541 } 542 543 public double getValue(double d, StyleConstants.Units units, 544 boolean horizontal) { 545 switch (units) { 546 case PX: 547 // TODO 548 return d; 549 case GU: 550 // TODO 551 return d; 552 case PERCENTS: 553 if (horizontal) 554 return (viewBox.x2 - viewBox.x1) * d / 100.0; 555 else 556 return (viewBox.y2 - viewBox.y1) * d / 100.0; 557 } 558 559 return d; 560 } 561 } 562 563 static class ViewBox { 564 double x1, y1, x2, y2; 565 double x3, y3, x4, y4; 566 567 double[] padding = { 0, 0 }; 568 569 ViewBox(double x1, double y1, double x2, double y2) { 570 this.x1 = x1; 571 this.y1 = y1; 572 this.x2 = x2; 573 this.y2 = y2; 574 } 575 576 void compute(Graph g, StyleGroup style) { 577 x3 = y3 = Double.MAX_VALUE; 578 x4 = y4 = Double.MIN_VALUE; 579 580 for (Node n : g.getEachNode()) { 581 x3 = Math.min(x3, getX(n)); 582 y3 = Math.min(y3, getY(n)); 583 584 x4 = Math.max(x4, getX(n)); 585 y4 = Math.max(y4, getY(n)); 586 } 587 588 Values v = style.getPadding(); 589 590 if (v.getValueCount() > 0) { 591 padding[0] = v.get(0); 592 padding[1] = v.getValueCount() > 1 ? v.get(1) : v.get(0); 593 } 594 } 595 596 double convertX(double x) { 597 return (x2 - x1 - 2 * padding[0]) * (x - x3) / (x4 - x3) + x1 598 + padding[0]; 599 } 600 601 double convertX(Node n) { 602 return convertX(getX(n)); 603 } 604 605 double convertY(double y) { 606 return (y2 - y1 - 2 * padding[1]) * (y - y3) / (y4 - y3) + y1 607 + padding[1]; 608 } 609 610 double convertY(Node n) { 611 return convertY(getY(n)); 612 } 613 } 614 615 static class SVGStyle { 616 617 static int gradientId = 0; 618 619 String style; 620 StyleGroup group; 621 boolean gradient; 622 boolean dynfill; 623 624 public SVGStyle(StyleGroup group) throws XMLStreamException { 625 626 this.group = group; 627 this.gradient = false; 628 this.dynfill = false; 629 630 switch (group.getType()) { 631 case EDGE: 632 buildEdgeStyle(); 633 break; 634 case NODE: 635 buildNodeStyle(); 636 break; 637 case GRAPH: 638 buildGraphStyle(); 639 break; 640 case SPRITE: 641 default: 642 break; 643 } 644 } 645 646 void buildNodeStyle() { 647 StringBuilder styleSB = new StringBuilder(); 648 649 switch (group.getFillMode()) { 650 case GRADIENT_RADIAL: 651 case GRADIENT_HORIZONTAL: 652 case GRADIENT_VERTICAL: 653 case GRADIENT_DIAGONAL1: 654 case GRADIENT_DIAGONAL2: 655 concat(styleSB, "fill:url(#%gradient-id%);"); 656 this.gradient = true; 657 break; 658 case PLAIN: 659 concat(styleSB, "fill:", toHexColor(group.getFillColor(0)), ";"); 660 concat(styleSB, "fill-opacity:", d(group.getFillColor(0) 661 .getAlpha() / 255.0), ";"); 662 break; 663 case DYN_PLAIN: 664 dynfill = true; 665 concat(styleSB, "fill:%fill-color%;"); 666 concat(styleSB, "fill-opacity:%fill-opacity%;"); 667 break; 668 case IMAGE_TILED: 669 case IMAGE_SCALED: 670 case IMAGE_SCALED_RATIO_MAX: 671 case IMAGE_SCALED_RATIO_MIN: 672 case NONE: 673 break; 674 } 675 676 concat(styleSB, "fill-rule:nonzero;"); 677 678 if (group.getStrokeMode() != StrokeMode.NONE) { 679 concat(styleSB, "stroke:", toHexColor(group.getStrokeColor(0)), 680 ";"); 681 concat(styleSB, "stroke-width:", 682 getSize(group.getStrokeWidth()), ";"); 683 } 684 685 style = styleSB.toString(); 686 } 687 688 void buildGraphStyle() { 689 buildNodeStyle(); 690 } 691 692 void buildEdgeStyle() { 693 StringBuilder styleSB = new StringBuilder(); 694 695 switch (group.getFillMode()) { 696 case GRADIENT_RADIAL: 697 case GRADIENT_HORIZONTAL: 698 case GRADIENT_VERTICAL: 699 case GRADIENT_DIAGONAL1: 700 case GRADIENT_DIAGONAL2: 701 concat(styleSB, "stroke:url(#%gradient-id%);"); 702 this.gradient = true; 703 break; 704 case PLAIN: 705 case DYN_PLAIN: 706 concat(styleSB, "stroke:", toHexColor(group.getFillColor(0)), 707 ";"); 708 break; 709 case IMAGE_TILED: 710 case IMAGE_SCALED: 711 case IMAGE_SCALED_RATIO_MAX: 712 case IMAGE_SCALED_RATIO_MIN: 713 case NONE: 714 break; 715 } 716 717 concat(styleSB, "stroke-width:", getSize(group.getSize(), 0), ";"); 718 719 style = styleSB.toString(); 720 } 721 722 public void writeDef(XMLWriter out) throws XMLStreamException { 723 if (gradient) { 724 String gid = String.format("gradient%x", gradientId++); 725 String type = "linearGradient"; 726 String x1 = null, x2 = null, y1 = null, y2 = null; 727 728 switch (group.getFillMode()) { 729 case GRADIENT_RADIAL: 730 type = "radialGradient"; 731 break; 732 case GRADIENT_HORIZONTAL: 733 x1 = "0%"; 734 y1 = "50%"; 735 x2 = "100%"; 736 y2 = "50%"; 737 break; 738 case GRADIENT_VERTICAL: 739 x1 = "50%"; 740 y1 = "0%"; 741 x2 = "50%"; 742 y2 = "100%"; 743 break; 744 case GRADIENT_DIAGONAL1: 745 x1 = "0%"; 746 y1 = "0%"; 747 x2 = "100%"; 748 y2 = "100%"; 749 break; 750 case GRADIENT_DIAGONAL2: 751 x1 = "100%"; 752 y1 = "100%"; 753 x2 = "0%"; 754 y2 = "0%"; 755 break; 756 default: 757 break; 758 } 759 760 out.open(type); 761 out.attribute("id", gid); 762 out.attribute("gradientUnits", "objectBoundingBox"); 763 764 if (type.equals("linearGradient")) { 765 out.attribute("x1", x1); 766 out.attribute("y1", y1); 767 out.attribute("x2", x2); 768 out.attribute("y2", y2); 769 } 770 771 for (int i = 0; i < group.getFillColorCount(); i++) { 772 out.open("stop"); 773 out.attribute("stop-color", 774 toHexColor(group.getFillColor(i))); 775 out.attribute("stop-opacity", d(group.getFillColor(i) 776 .getAlpha() / 255.0)); 777 out.attribute( 778 "offset", 779 Double.toString(i 780 / (double) (group.getFillColorCount() - 1))); 781 out.close(); 782 } 783 784 out.close(); 785 786 style = style.replace("%gradient-id%", gid); 787 } 788 } 789 790 public String getElementStyle(Element e) { 791 String st = style; 792 793 if (dynfill) { 794 if (group.getFillColorCount() > 1) { 795 String color, opacity; 796 double d = e.hasNumber("ui.color") ? e 797 .getNumber("ui.color") : 0; 798 799 double a, b; 800 Colors colors = group.getFillColors(); 801 int s = Math.min((int) (d * group.getFillColorCount()), 802 colors.size() - 2); 803 804 a = s / (double) (colors.size() - 1); 805 b = (s + 1) / (double) (colors.size() - 1); 806 807 d = (d - a) / (b - a); 808 809 Color c1 = colors.get(s), c2 = colors.get(s + 1); 810 811 color = String.format( 812 "#%02x%02x%02x", 813 (int) (c1.getRed() + d 814 * (c2.getRed() - c1.getRed())), 815 (int) (c1.getGreen() + d 816 * (c2.getGreen() - c1.getGreen())), 817 (int) (c1.getBlue() + d 818 * (c2.getBlue() - c1.getBlue()))); 819 820 opacity = Double.toString((c1.getAlpha() + d 821 * (c2.getAlpha() - c1.getAlpha())) / 255.0); 822 823 st = st.replace("%fill-color%", color); 824 st = st.replace("%fill-opacity%", opacity); 825 } 826 } 827 828 return st; 829 } 830 } 831 832 static class XMLWriter { 833 XMLStreamWriter out; 834 int depth; 835 boolean closed; 836 837 void start(Writer w) throws XMLStreamException, 838 FactoryConfigurationError, IOException { 839 if (out != null) 840 end(); 841 842 out = XMLOutputFactory.newInstance().createXMLStreamWriter(w); 843 out.writeStartDocument(); 844 } 845 846 void end() throws XMLStreamException { 847 out.writeEndDocument(); 848 out.flush(); 849 out.close(); 850 out = null; 851 } 852 853 void open(String name) throws XMLStreamException { 854 out.writeCharacters("\n"); 855 for (int i = 0; i < depth; i++) 856 out.writeCharacters(" "); 857 858 out.writeStartElement(name); 859 depth++; 860 } 861 862 void close() throws XMLStreamException { 863 out.writeEndElement(); 864 depth--; 865 } 866 867 void attribute(String key, String value) throws XMLStreamException { 868 out.writeAttribute(key, value); 869 } 870 871 void characters(String data) throws XMLStreamException { 872 out.writeCharacters(data); 873 } 874 } 875 876 private static void concat(StringBuilder buffer, Object... args) { 877 if (args != null) { 878 for (int i = 0; i < args.length; i++) 879 buffer.append(args[i].toString()); 880 } 881 } 882 883 private static String toHexColor(Color c) { 884 return String.format("#%02x%02x%02x", c.getRed(), c.getGreen(), 885 c.getBlue()); 886 } 887 888 /* 889 * (non-Javadoc) 890 * 891 * @see 892 * org.graphstream.stream.AttributeSink#edgeAttributeAdded(java.lang.String, 893 * long, java.lang.String, java.lang.String, java.lang.Object) 894 */ 895 public void edgeAttributeAdded(String sourceId, long timeId, String edgeId, 896 String attribute, Object value) { 897 } 898 899 /* 900 * (non-Javadoc) 901 * 902 * @see 903 * org.graphstream.stream.AttributeSink#edgeAttributeChanged(java.lang.String 904 * , long, java.lang.String, java.lang.String, java.lang.Object, 905 * java.lang.Object) 906 */ 907 public void edgeAttributeChanged(String sourceId, long timeId, 908 String edgeId, String attribute, Object oldValue, Object newValue) { 909 } 910 911 /* 912 * (non-Javadoc) 913 * 914 * @see 915 * org.graphstream.stream.AttributeSink#edgeAttributeRemoved(java.lang.String 916 * , long, java.lang.String, java.lang.String) 917 */ 918 public void edgeAttributeRemoved(String sourceId, long timeId, 919 String edgeId, String attribute) { 920 } 921 922 /* 923 * (non-Javadoc) 924 * 925 * @see 926 * org.graphstream.stream.AttributeSink#graphAttributeAdded(java.lang.String 927 * , long, java.lang.String, java.lang.Object) 928 */ 929 public void graphAttributeAdded(String sourceId, long timeId, 930 String attribute, Object value) { 931 } 932 933 /* 934 * (non-Javadoc) 935 * 936 * @see 937 * org.graphstream.stream.AttributeSink#graphAttributeChanged(java.lang. 938 * String, long, java.lang.String, java.lang.Object, java.lang.Object) 939 */ 940 public void graphAttributeChanged(String sourceId, long timeId, 941 String attribute, Object oldValue, Object newValue) { 942 } 943 944 /* 945 * (non-Javadoc) 946 * 947 * @see 948 * org.graphstream.stream.AttributeSink#graphAttributeRemoved(java.lang. 949 * String, long, java.lang.String) 950 */ 951 public void graphAttributeRemoved(String sourceId, long timeId, 952 String attribute) { 953 } 954 955 /* 956 * (non-Javadoc) 957 * 958 * @see 959 * org.graphstream.stream.AttributeSink#nodeAttributeAdded(java.lang.String, 960 * long, java.lang.String, java.lang.String, java.lang.Object) 961 */ 962 public void nodeAttributeAdded(String sourceId, long timeId, String nodeId, 963 String attribute, Object value) { 964 } 965 966 /* 967 * (non-Javadoc) 968 * 969 * @see 970 * org.graphstream.stream.AttributeSink#nodeAttributeChanged(java.lang.String 971 * , long, java.lang.String, java.lang.String, java.lang.Object, 972 * java.lang.Object) 973 */ 974 public void nodeAttributeChanged(String sourceId, long timeId, 975 String nodeId, String attribute, Object oldValue, Object newValue) { 976 } 977 978 /* 979 * (non-Javadoc) 980 * 981 * @see 982 * org.graphstream.stream.AttributeSink#nodeAttributeRemoved(java.lang.String 983 * , long, java.lang.String, java.lang.String) 984 */ 985 public void nodeAttributeRemoved(String sourceId, long timeId, 986 String nodeId, String attribute) { 987 } 988 989 /* 990 * (non-Javadoc) 991 * 992 * @see org.graphstream.stream.ElementSink#edgeAdded(java.lang.String, long, 993 * java.lang.String, java.lang.String, java.lang.String, boolean) 994 */ 995 public void edgeAdded(String sourceId, long timeId, String edgeId, 996 String fromNodeId, String toNodeId, boolean directed) { 997 } 998 999 /* 1000 * (non-Javadoc) 1001 * 1002 * @see org.graphstream.stream.ElementSink#edgeRemoved(java.lang.String, 1003 * long, java.lang.String) 1004 */ 1005 public void edgeRemoved(String sourceId, long timeId, String edgeId) { 1006 } 1007 1008 /* 1009 * (non-Javadoc) 1010 * 1011 * @see org.graphstream.stream.ElementSink#graphCleared(java.lang.String, 1012 * long) 1013 */ 1014 public void graphCleared(String sourceId, long timeId) { 1015 } 1016 1017 /* 1018 * (non-Javadoc) 1019 * 1020 * @see org.graphstream.stream.ElementSink#nodeAdded(java.lang.String, long, 1021 * java.lang.String) 1022 */ 1023 public void nodeAdded(String sourceId, long timeId, String nodeId) { 1024 } 1025 1026 /* 1027 * (non-Javadoc) 1028 * 1029 * @see org.graphstream.stream.ElementSink#nodeRemoved(java.lang.String, 1030 * long, java.lang.String) 1031 */ 1032 public void nodeRemoved(String sourceId, long timeId, String nodeId) { 1033 } 1034 1035 /* 1036 * (non-Javadoc) 1037 * 1038 * @see org.graphstream.stream.ElementSink#stepBegins(java.lang.String, 1039 * long, double) 1040 */ 1041 public void stepBegins(String sourceId, long timeId, double step) { 1042 } 1043}