PageRenderTime 31ms CodeModel.GetById 5ms RepoModel.GetById 1ms app.codeStats 0ms

/src/org/mt4j/util/xml/svg/SVGLoader.java

http://mt4j.googlecode.com/
Java | 2589 lines | 1581 code | 318 blank | 690 comment | 297 complexity | a0e5b4b9ced62d905339e4e815e4d032 MD5 | raw file
Possible License(s): GPL-2.0, IPL-1.0, BSD-3-Clause, LGPL-2.1, Apache-2.0
  1. /***********************************************************************
  2. * mt4j Copyright (c) 2008 - 2009 C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. ***********************************************************************/
  18. package org.mt4j.util.xml.svg;
  19. import java.awt.BasicStroke;
  20. import java.awt.Color;
  21. import java.awt.Graphics;
  22. import java.awt.Graphics2D;
  23. import java.awt.MultipleGradientPaint.CycleMethod;
  24. import java.awt.RadialGradientPaint;
  25. import java.awt.Rectangle;
  26. import java.awt.Stroke;
  27. import java.awt.geom.AffineTransform;
  28. import java.awt.geom.Point2D;
  29. import java.awt.geom.Rectangle2D;
  30. import java.io.File;
  31. import java.io.InputStream;
  32. import java.net.URI;
  33. import java.text.AttributedCharacterIterator;
  34. import java.text.AttributedCharacterIterator.Attribute;
  35. import java.util.ArrayList;
  36. import java.util.Iterator;
  37. import java.util.LinkedList;
  38. import java.util.List;
  39. import java.util.Map;
  40. import java.util.Set;
  41. import java.util.Stack;
  42. import javax.media.opengl.GL;
  43. import javax.media.opengl.glu.GLU;
  44. import javax.swing.JPanel;
  45. import org.apache.batik.bridge.AbstractSVGGradientElementBridge;
  46. import org.apache.batik.bridge.AbstractSVGGradientElementBridge.SVGStopElementBridge;
  47. import org.apache.batik.bridge.AbstractSVGGradientElementBridge.Stop;
  48. import org.apache.batik.bridge.Bridge;
  49. import org.apache.batik.bridge.BridgeContext;
  50. import org.apache.batik.bridge.BridgeException;
  51. import org.apache.batik.bridge.CSSUtilities;
  52. import org.apache.batik.bridge.DocumentLoader;
  53. import org.apache.batik.bridge.GVTBuilder;
  54. import org.apache.batik.bridge.PaintServer;
  55. import org.apache.batik.bridge.SVGTextElementBridge;
  56. import org.apache.batik.bridge.SVGUtilities;
  57. import org.apache.batik.bridge.TextUtilities;
  58. import org.apache.batik.bridge.UnitProcessor;
  59. import org.apache.batik.bridge.UserAgent;
  60. import org.apache.batik.bridge.UserAgentAdapter;
  61. import org.apache.batik.css.engine.SVGCSSEngine;
  62. import org.apache.batik.css.engine.value.ListValue;
  63. import org.apache.batik.css.engine.value.Value;
  64. import org.apache.batik.css.engine.value.svg.ICCColor;
  65. import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
  66. import org.apache.batik.dom.svg.SVGGraphicsElement;
  67. import org.apache.batik.dom.svg.SVGOMCircleElement;
  68. import org.apache.batik.dom.svg.SVGOMClipPathElement;
  69. import org.apache.batik.dom.svg.SVGOMDefsElement;
  70. import org.apache.batik.dom.svg.SVGOMEllipseElement;
  71. import org.apache.batik.dom.svg.SVGOMForeignObjectElement;
  72. import org.apache.batik.dom.svg.SVGOMGElement;
  73. import org.apache.batik.dom.svg.SVGOMLineElement;
  74. import org.apache.batik.dom.svg.SVGOMLinearGradientElement;
  75. import org.apache.batik.dom.svg.SVGOMMaskElement;
  76. import org.apache.batik.dom.svg.SVGOMPathElement;
  77. import org.apache.batik.dom.svg.SVGOMPolygonElement;
  78. import org.apache.batik.dom.svg.SVGOMPolylineElement;
  79. import org.apache.batik.dom.svg.SVGOMRadialGradientElement;
  80. import org.apache.batik.dom.svg.SVGOMRectElement;
  81. import org.apache.batik.dom.svg.SVGOMSVGElement;
  82. import org.apache.batik.dom.svg.SVGOMSwitchElement;
  83. import org.apache.batik.dom.svg.SVGOMTSpanElement;
  84. import org.apache.batik.dom.svg.SVGOMTextElement;
  85. import org.apache.batik.dom.svg.SVGOMToBeImplementedElement;
  86. import org.apache.batik.dom.svg.SVGTextContentSupport;
  87. import org.apache.batik.dom.svg.SVGURIReferenceGraphicsElement;
  88. import org.apache.batik.dom.svg12.BindableElement;
  89. import org.apache.batik.dom.svg12.SVGOMFlowRootElement;
  90. import org.apache.batik.dom.util.XLinkSupport;
  91. import org.apache.batik.ext.awt.MultipleGradientPaint;
  92. import org.apache.batik.ext.awt.MultipleGradientPaint.CycleMethodEnum;
  93. import org.apache.batik.gvt.GraphicsNode;
  94. import org.apache.batik.gvt.TextNode;
  95. import org.apache.batik.gvt.font.AWTGVTFont;
  96. import org.apache.batik.gvt.font.GVTFont;
  97. import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
  98. import org.apache.batik.gvt.text.TextPaintInfo;
  99. import org.apache.batik.parser.PathParser;
  100. import org.apache.batik.parser.TransformListParser;
  101. import org.apache.batik.util.SVGConstants;
  102. import org.apache.batik.util.XMLResourceDescriptor;
  103. import org.mt4j.MTApplication;
  104. import org.mt4j.components.MTComponent;
  105. import org.mt4j.components.TransformSpace;
  106. import org.mt4j.components.bounds.BoundsZPlaneRectangle;
  107. import org.mt4j.components.bounds.IBoundingShape;
  108. import org.mt4j.components.clipping.FillPaint;
  109. import org.mt4j.components.visibleComponents.AbstractVisibleComponent;
  110. import org.mt4j.components.visibleComponents.font.FontManager;
  111. import org.mt4j.components.visibleComponents.font.IFont;
  112. import org.mt4j.components.visibleComponents.shapes.AbstractShape;
  113. import org.mt4j.components.visibleComponents.shapes.GeometryInfo;
  114. import org.mt4j.components.visibleComponents.shapes.MTEllipse;
  115. import org.mt4j.components.visibleComponents.shapes.MTLine;
  116. import org.mt4j.components.visibleComponents.shapes.MTPolygon;
  117. import org.mt4j.components.visibleComponents.shapes.MTRectangle;
  118. import org.mt4j.components.visibleComponents.shapes.MTRectangle.PositionAnchor;
  119. import org.mt4j.components.visibleComponents.shapes.MTRoundRectangle;
  120. import org.mt4j.components.visibleComponents.shapes.MTStencilPolygon;
  121. import org.mt4j.components.visibleComponents.shapes.mesh.MTTriangleMesh;
  122. import org.mt4j.components.visibleComponents.widgets.MTTextArea;
  123. import org.mt4j.input.gestureAction.DefaultDragAction;
  124. import org.mt4j.input.gestureAction.DefaultRotateAction;
  125. import org.mt4j.input.gestureAction.DefaultScaleAction;
  126. import org.mt4j.input.inputProcessors.IGestureEventListener;
  127. import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragProcessor;
  128. import org.mt4j.input.inputProcessors.componentProcessors.rotateProcessor.RotateProcessor;
  129. import org.mt4j.input.inputProcessors.componentProcessors.scaleProcessor.ScaleProcessor;
  130. import org.mt4j.util.HelperMethods;
  131. import org.mt4j.util.MT4jSettings;
  132. import org.mt4j.util.MTColor;
  133. import org.mt4j.util.SwingTextureRenderer;
  134. import org.mt4j.util.logging.ILogger;
  135. import org.mt4j.util.logging.MTLoggerFactory;
  136. import org.mt4j.util.math.ConvexityUtil;
  137. import org.mt4j.util.math.Matrix;
  138. import org.mt4j.util.math.ToolsGeometry;
  139. import org.mt4j.util.math.Vector3D;
  140. import org.mt4j.util.math.Vertex;
  141. import org.mt4j.util.opengl.GLTexture;
  142. import org.mt4j.util.opengl.GluTrianglulator;
  143. import org.w3c.dom.Document;
  144. import org.w3c.dom.Element;
  145. import org.w3c.dom.NamedNodeMap;
  146. import org.w3c.dom.Node;
  147. import org.w3c.dom.NodeList;
  148. import org.w3c.dom.css.CSSPrimitiveValue;
  149. import org.w3c.dom.css.CSSStyleDeclaration;
  150. import org.w3c.dom.css.CSSValue;
  151. import org.w3c.dom.svg.SVGAnimatedLength;
  152. import org.w3c.dom.svg.SVGDocument;
  153. import org.w3c.dom.svg.SVGElement;
  154. import org.w3c.dom.svg.SVGLength;
  155. import org.w3c.dom.svg.SVGLengthList;
  156. import org.w3c.dom.svg.SVGMatrix;
  157. import org.w3c.dom.svg.SVGPoint;
  158. import org.w3c.dom.svg.SVGPointList;
  159. import org.w3c.dom.svg.SVGSVGElement;
  160. import processing.core.PApplet;
  161. import processing.opengl.PGraphicsOpenGL;
  162. /**
  163. * This class can be used to load and display scalable vector graphics (svg) files.
  164. *
  165. * @author Christopher Ruff
  166. */
  167. public class SVGLoader implements SVGConstants{
  168. private static final ILogger logger = MTLoggerFactory.getLogger(SVGLoader.class.getName());
  169. static{
  170. logger.setLevel(ILogger.ERROR);
  171. }
  172. /** The svg doc. */
  173. private SVGDocument svgDoc;
  174. /** The user agent. */
  175. private UserAgent userAgent;
  176. /** The loader. */
  177. private DocumentLoader loader;
  178. /** The ctx. */
  179. private BridgeContext ctx;
  180. /** The builder. */
  181. private GVTBuilder builder;
  182. /** The root gn. */
  183. private GraphicsNode rootGN;
  184. /** The css engine. */
  185. protected SVGCSSEngine cssEngine;
  186. /** The pa. */
  187. private PApplet pa;
  188. /** The opacity stack. */
  189. private Stack<Float> opacityStack;
  190. /** The default drag action. */
  191. private IGestureEventListener defaultDragAction;
  192. /** The default rotate action. */
  193. private IGestureEventListener defaultRotateAction;
  194. /** The default scale action. */
  195. private IGestureEventListener defaultScaleAction;
  196. /** The current local transform matrix. */
  197. private Matrix currentLocalTransformMatrix;
  198. /**
  199. * Instantiates a new batik svg parser.
  200. *
  201. * @param pa the pa
  202. */
  203. public SVGLoader(PApplet pa){
  204. this.pa = pa;
  205. opacityStack = new Stack<Float>();
  206. currentLocalTransformMatrix = new Matrix();
  207. defaultDragAction = new DefaultDragAction();
  208. defaultRotateAction = new DefaultRotateAction();
  209. defaultScaleAction = new DefaultScaleAction();
  210. }
  211. /**
  212. * Loads a "*.svg" file, parses it, creates drawable components and returns the
  213. * toplevel component.
  214. *
  215. * @param filedescr the absolute path of the svg file as a string
  216. *
  217. * @return the MT base component
  218. *
  219. * the created top level component of the svg
  220. */
  221. public MTComponent loadSvg(String filedescr){
  222. return this.getCreatedSvgComponents(this.parseSvg(filedescr));
  223. }
  224. /**
  225. * Uses the batik parser to genererate an svg document from an svg file.
  226. * To create the components in that svg document, call <code>getCreatedSvgComponents(SVGDocument doc)</code>
  227. *
  228. * @param filedescr the filedescr
  229. *
  230. * @return the SVG document
  231. */
  232. public SVGDocument parseSvg(String filedescr){
  233. Document doc;
  234. try {
  235. String parser = XMLResourceDescriptor.getXMLParserClassName();
  236. SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
  237. File file = new File(filedescr);
  238. if (file.exists()){
  239. URI localFileAsUri = file.toURI();
  240. String uri = localFileAsUri.toASCIIString();
  241. doc = f.createDocument(uri);
  242. }else{
  243. InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(filedescr);
  244. if (in == null){
  245. in = pa.getClass().getResourceAsStream(filedescr);
  246. }
  247. doc = f.createDocument(filedescr, in);
  248. /*on it (after casting it to SVGOMDocument) to give it a
  249. URI of some sort. If the document needs to be able to have relative
  250. reference to files on the local file system, give it a URI like
  251. "file:///some/where/file.svg";
  252. */
  253. //FIXME HACK! this seems to help the "org.apache.batik.bridge.BridgeException: Unable to make sense of URL for connection" error
  254. //occuring with windmill.svg if loading from inputstream instead of local file system file
  255. //FIXME but this might create errors when loading external file like images from the relative svg path?
  256. doc.setDocumentURI("") ;
  257. // String sub = filedescr.substring(0, filedescr.lastIndexOf(MTApplication.separator));
  258. // System.out.println("F: " + filedescr + " sub; " + sub);
  259. // svgDoc.setDocumentURI(sub+ MTApplication.separator) ;
  260. }
  261. svgDoc = (SVGDocument)doc;
  262. } catch (Exception ex) {
  263. ex.printStackTrace();
  264. }
  265. //Neccesary? For booting css
  266. try{
  267. userAgent = new UserAgentAdapter();
  268. loader = new DocumentLoader(userAgent);
  269. ctx = new BridgeContext(userAgent, loader);
  270. ctx.setDynamicState(BridgeContext.DYNAMIC); //TODO use static?
  271. builder = new GVTBuilder();
  272. rootGN = builder.build(ctx, svgDoc);
  273. // ctx.getCSSEngineForElement(null).
  274. }catch(Exception e){
  275. e.printStackTrace();
  276. }
  277. return svgDoc;
  278. }
  279. /**
  280. * Creates and returns components of the provided svg document for displaying.
  281. *
  282. * @param svgDoc the svg doc
  283. *
  284. * @return the created svg components
  285. */
  286. public MTComponent getCreatedSvgComponents(SVGDocument svgDoc){
  287. ArrayList<MTComponent> components = new ArrayList<MTComponent>();
  288. opacityStack.push(1.0f);
  289. traverseSVGDoc(svgDoc, components);
  290. opacityStack.pop();
  291. MTComponent[] comps = components.toArray(new MTComponent[components.size()]);
  292. //Only returning the 1st component, since this should be the top-level <svg> element and only 1!?
  293. return comps[0];
  294. }
  295. /**
  296. * Traverse svg doc.
  297. *
  298. * @param node the node
  299. * @param comps the comps
  300. */
  301. private void traverseSVGDoc(Node node, ArrayList<MTComponent> comps){
  302. logger.debug("Traversing: " + node.getNodeName());
  303. //Damit transformationen konsistent sind muss
  304. //jedes tag, da? eine transform attribut hat
  305. //behandelt werden!
  306. //Default
  307. currentLocalTransformMatrix = new Matrix();
  308. //If there is a TRANSFORM attribute parse that and set the
  309. //current transformation matrix to be used with the svg components created after
  310. NamedNodeMap atts = node.getAttributes();
  311. if (atts != null){
  312. for (int i = 0; i < atts.getLength(); i++) {
  313. Node att = atts.item(i);
  314. if (att.getNodeName().equals(SVG_TRANSFORM_ATTRIBUTE)){
  315. CustomTransformHandler transFormHandler = new CustomTransformHandler();
  316. TransformListParser transFormListParser = new TransformListParser();
  317. transFormListParser.setTransformListHandler(transFormHandler);
  318. transFormListParser.parse(att.getNodeValue());
  319. //Overwrite current default matrix if the element has its own
  320. //transform defined, will be used at gfx obj creation
  321. currentLocalTransformMatrix = transFormHandler.getResultMatrix();
  322. }
  323. }
  324. }
  325. // logger.debug("Node: " + node.getNodeName() + " Class: " + node.getClass());
  326. //For opacity inheritance
  327. if (node instanceof SVGGraphicsElement){
  328. SVGGraphicsElement svgGfx = (SVGGraphicsElement)node;
  329. //Handle inherited opacity settings
  330. float opac = queryPrimitiveFloatValue(svgGfx, "opacity", 1f);
  331. opacityStack.push(opac *= opacityStack.peek());
  332. }
  333. // if G (GROUP) element, add all children to this element
  334. if ( node instanceof SVGOMGElement
  335. || node instanceof SVGSVGElement
  336. || node instanceof SVGOMSVGElement
  337. ){
  338. // SVGOMGElement gElem = (SVGOMGElement)node;
  339. SVGElement gElem = (SVGElement)node;
  340. MTComponent group = new MTComponent(pa);
  341. group.setName(gElem.getTagName());
  342. // Element viewPort = gElem.getViewportElement();
  343. // logger.debug("Viewport " + viewPort.getNodeName());
  344. //Set the <g> group to composite, meaning that it will
  345. //be returned at picking, when one of the children gets picked
  346. group.setComposite(true);
  347. group.setLocalMatrix(currentLocalTransformMatrix);
  348. //IF its <svg> element get the transform
  349. //(to honor the viewBox and the width/height attributes
  350. if (node instanceof SVGOMSVGElement ){
  351. SVGOMSVGElement svgGom = ((SVGOMSVGElement)node);
  352. Element viewPort = svgGom.getViewportElement();
  353. if (viewPort != null)
  354. logger.debug("Viewport " + viewPort.getNodeName());
  355. // SVGMatrix mat = svgGom.getScreenCTM();
  356. SVGAnimatedLength widthA = svgGom.getWidth();
  357. SVGAnimatedLength heightA = svgGom.getHeight();
  358. SVGLength w = widthA.getBaseVal();
  359. float width = w.getValue();
  360. SVGLength h = heightA.getBaseVal();
  361. float height = h.getValue();
  362. logger.debug("-> SVG Width: " + width + " Height: " + height);
  363. SVGMatrix mat = svgGom.getCTM();
  364. /*
  365. logger.debug("mat: " + mat.toString());
  366. logger.debug(mat.getA());
  367. logger.debug(mat.getB());
  368. logger.debug(mat.getC());
  369. logger.debug(mat.getD());
  370. logger.debug(mat.getE());
  371. logger.debug(mat.getF());
  372. SVGRect bbox = svgGom.getBBox();
  373. logger.debug("BBOx: X:" + bbox.getX() + " Y:" + bbox.getY() + " Width:" + bbox.getWidth() + " Height:" + bbox.getHeight());
  374. */
  375. //Hack, because if no width/height is specified default of 1.0
  376. //is assumed by batik -> things may get scaled too small
  377. if ( !(width == 1 && height == 1) ){
  378. currentLocalTransformMatrix = new Matrix(mat.getA(), mat.getC(), 0, mat.getE(),
  379. mat.getB(), mat.getD(), 0, mat.getF(),
  380. 0, 0, 1, 0,
  381. 0, 0, 0, 1
  382. );
  383. //logger.debug("Matrix: " + currentLocalTransformMatrix);
  384. group.setLocalMatrix(currentLocalTransformMatrix);
  385. }
  386. }
  387. //Make the group pickable and manipulatable
  388. group.setPickable(true);
  389. group.registerInputProcessor(new DragProcessor(pa));
  390. group.setGestureAllowance(DragProcessor.class, true);
  391. group.addGestureListener(DragProcessor.class, (IGestureEventListener)defaultDragAction);
  392. group.registerInputProcessor(new RotateProcessor(pa));
  393. group.addGestureListener(RotateProcessor.class, defaultRotateAction);
  394. group.registerInputProcessor(new ScaleProcessor(pa));
  395. group.addGestureListener(ScaleProcessor.class, defaultScaleAction);
  396. ArrayList<MTComponent> groupChildren = new ArrayList<MTComponent>();
  397. //Traverse the children and add them to a new arraylist
  398. traverseChildren(gElem, groupChildren);
  399. MTComponent[] childComps = groupChildren.toArray(new MTComponent[groupChildren.size()]);
  400. //Add the children to the group
  401. group.addChildren(childComps);
  402. //Add the group to the arraylist of the parent
  403. comps.add(group);
  404. }else{//If NOT GROUP
  405. if (node instanceof SVGGraphicsElement){
  406. SVGGraphicsElement svgGfxElem = (SVGGraphicsElement)node;
  407. //IF node isnt a group node just add it to the passed in comps arraylist
  408. try{
  409. //Create a component from the graphicsnode and add it to the parents arraylist
  410. MTComponent liveComponent = handleGraphicsNode(svgGfxElem);
  411. if (liveComponent != null){
  412. comps.add(liveComponent);
  413. }
  414. }catch(Exception e){
  415. logger.error("Error handling svg node: " + svgGfxElem.getTagName());
  416. e.printStackTrace();
  417. }
  418. }
  419. //FIXME IMPLEMENT
  420. if (node instanceof SVGOMTSpanElement){
  421. SVGOMTSpanElement tSpanElement = (SVGOMTSpanElement)node;
  422. }
  423. //FIXME TEST
  424. if (node instanceof SVGOMTextElement){
  425. boolean useVectorFont = false;
  426. SVGOMTextElement textElement = (SVGOMTextElement)node;
  427. //Get <text> position values (can be a list)
  428. List<Float> xValues = getSVGLengthListAsFloat(textElement.getX().getBaseVal());
  429. List<Float> yValues = getSVGLengthListAsFloat(textElement.getY().getBaseVal());
  430. // /*//Not used
  431. String textContent = TextUtilities.getElementContent(textElement);
  432. textContent = textContent.replaceAll("\\n","");
  433. textContent = textContent.trim();
  434. // */
  435. /*
  436. //TODO USE?
  437. textElement.getTextLength();
  438. textElement.getRotate();
  439. */
  440. if (textElement.getSVGContext() instanceof SVGTextElementBridge){
  441. SVGTextElementBridge b = (SVGTextElementBridge)textElement.getSVGContext();
  442. GraphicsNode gr = b.createGraphicsNode(ctx, textElement);
  443. TextNode tNode = (TextNode)gr;
  444. b.buildGraphicsNode(ctx, textElement, tNode);
  445. List<?> textRuns = tNode.getTextRuns();
  446. logger.debug("Text runs: " + textRuns);
  447. //Get font size
  448. float fontSize = b.getFontSize();
  449. logger.debug("Text:" + " x:" + xValues.get(0) + " y:" + yValues.get(0) + " FontSize: " + fontSize + " Text: '" + textContent + "'");
  450. //Get font FILL
  451. Value fillOpacValue = CSSUtilities.getComputedStyle(textElement, SVGCSSEngine.FILL_OPACITY_INDEX);
  452. float computedfillOpac = PaintServer.convertOpacity(fillOpacValue);
  453. Value fillIndexValue = CSSUtilities.getComputedStyle(textElement, SVGCSSEngine.FILL_INDEX);
  454. Object fill = SVGLoader.getFillOrStroke(textElement, fillIndexValue, computedfillOpac, ctx);
  455. MTColor fillColor = new MTColor(150,150,150,255);
  456. if (fill instanceof java.awt.Color) {
  457. java.awt.Color color = (Color) fill;
  458. fillColor.setColor(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
  459. }
  460. //Get STROKE
  461. // Stroke Opacity \\
  462. Value strokeOpacValue = CSSUtilities.getComputedStyle(textElement, SVGCSSEngine.STROKE_OPACITY_INDEX);
  463. float computedStrokeOpacity = PaintServer.convertOpacity(strokeOpacValue);
  464. // Stroke java.awt.Color \\
  465. Value strokeIndexValue = CSSUtilities.getComputedStyle(textElement, SVGCSSEngine.STROKE_INDEX);
  466. Object stroke = SVGLoader.getFillOrStroke(textElement, strokeIndexValue, computedStrokeOpacity, ctx);
  467. MTColor strokeColor = new MTColor(fillColor.getR(), fillColor.getG(), fillColor.getB(), fillColor.getAlpha());
  468. if (stroke instanceof java.awt.Color) {
  469. java.awt.Color color = (Color) stroke;
  470. strokeColor.setColor(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
  471. }
  472. //Get the font family
  473. Value fontFamilyValue = CSSUtilities.getComputedStyle(textElement, SVGCSSEngine.FONT_FAMILY_INDEX);
  474. String fontFamily = "arial"; //DEFAULT
  475. if (fontFamilyValue instanceof ListValue) {
  476. ListValue listValue = (ListValue) fontFamilyValue;
  477. Value firstValue = listValue.item(0); //Can be a List? -> take only the first one..
  478. if (firstValue != null)
  479. fontFamily = firstValue.getStringValue();
  480. }
  481. logger.debug("Font family: " + fontFamily);
  482. IFont font;
  483. if (useVectorFont)
  484. //Vector font
  485. font = FontManager.getInstance().createFont(pa,
  486. "arial.ttf", Math.round(fontSize), fillColor);
  487. else
  488. //Bitmap font
  489. font = FontManager.getInstance().createFont(pa,
  490. // "Arial", Math.round(fontSize),
  491. fontFamily, Math.round(fontSize), fillColor);
  492. // /*
  493. IFont fontToUse = font;
  494. IFont lastUsedFont = fontToUse;
  495. List<MTTextArea> textAreas = new ArrayList<MTTextArea>();
  496. AttributedCharacterIterator iter = tNode.getAttributedCharacterIterator();
  497. if (font != null && iter != null){ //To avoid not loaded fonts or if text ist empty
  498. for (int i = iter.getBeginIndex(); i < iter.getEndIndex(); i++) {
  499. char currentChar = iter.setIndex(i);
  500. Set<Attribute> keys = iter.getAllAttributeKeys();
  501. Map<Attribute, Object> charAtts = iter.getAttributes();
  502. Object baseLineShift = charAtts.get(SVGTextElementBridge.BASELINE_SHIFT);
  503. Object paintInfo = charAtts.get(SVGTextElementBridge.PAINT_INFO);
  504. Object charX = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.X);
  505. Object charY = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.Y);
  506. Object charDX = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.DX);
  507. Object charDY = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.DY);
  508. Object charRotation = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.ROTATION);
  509. Object gvtFont = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.GVT_FONT);
  510. Object gvtFonts = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.GVT_FONTS);
  511. Object gvtFontFamilies = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
  512. Object textCompoundDelimiter = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER);
  513. Object verticalOrientation = charAtts.get(GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION);
  514. logger.debug("Character: " + currentChar + " CharX:" + charX + " CharY: " + charY + " CharDX: " + charDX + " CharDY: " + charDY + " Font: " + gvtFont + " Fonts: " + gvtFonts + " FontFamilies: " + gvtFontFamilies);
  515. AWTGVTFont awtGvtFont = (AWTGVTFont)gvtFont;
  516. if (awtGvtFont != null)
  517. logger.debug("CharfontSize: " + awtGvtFont.getSize());
  518. //FIXME REMOVE, Not working always 0,0
  519. SVGPoint startPosOfChar = SVGTextContentSupport.getStartPositionOfChar(textElement, i);
  520. /////////////////////////////////////
  521. //Get the character information - font, colors
  522. String newFamilyName = fontFamily;
  523. float newFontSize = fontSize;
  524. MTColor newFillColor = new MTColor(fillColor);
  525. MTColor newStrokeColor = new MTColor(strokeColor);
  526. boolean charHasColorInfo = false;
  527. boolean charHasFontInfo = false;
  528. //Get chars paint info
  529. if (paintInfo != null && paintInfo instanceof TextPaintInfo){
  530. charHasColorInfo = true;
  531. TextPaintInfo texInfo = (TextPaintInfo)paintInfo;
  532. if (texInfo.fillPaint instanceof java.awt.Color){
  533. java.awt.Color color = (Color)texInfo.fillPaint;
  534. newFillColor.setColor(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
  535. }
  536. if (texInfo.strokePaint instanceof java.awt.Color){
  537. java.awt.Color color = (Color)texInfo.strokePaint;
  538. newStrokeColor.setColor(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
  539. }
  540. }
  541. //Get the chars font family and size
  542. GVTFont aGvtFont = null;
  543. if (gvtFonts!=null){
  544. if (gvtFonts instanceof List) {
  545. List<?> fonts = (List<?>) gvtFonts;
  546. for (Object o : fonts) {
  547. if (o instanceof GVTFont) {
  548. aGvtFont = (GVTFont) o;
  549. //logger.debug("Char font family: " + aGvtFont.getFamilyName() + " Size:" + aGvtFont.getSize());
  550. }
  551. }
  552. }
  553. }
  554. if (aGvtFont != null){
  555. charHasFontInfo = true;
  556. newFamilyName = aGvtFont.getFamilyName();
  557. newFontSize = aGvtFont.getSize();
  558. }else{
  559. logger.error("Character: " + currentChar + " has no font attached.");
  560. }
  561. if (charHasColorInfo && charHasFontInfo){
  562. logger.debug("Character '" + currentChar + "'-> has font info -> load font!" +
  563. " Family: " + newFamilyName +
  564. " Fontsize: " + Math.round(newFontSize) +
  565. " FillColor: " + newFillColor +
  566. " StrokeColor: " + newStrokeColor);
  567. if (useVectorFont)
  568. fontToUse = FontManager.getInstance().createFont(pa,
  569. "arial.ttf", Math.round(newFontSize), newFillColor);
  570. else
  571. fontToUse = FontManager.getInstance().createFont(pa, //uses cached font if available
  572. // "Arial", Math.round(fontSize),
  573. newFamilyName, Math.round(newFontSize), newFillColor);
  574. if (fontToUse == null){
  575. fontToUse = font;
  576. }
  577. }else{
  578. fontToUse = font;
  579. }
  580. boolean fontChanged = !FontManager.isFontsAreEqual(fontToUse, lastUsedFont);
  581. lastUsedFont = fontToUse;
  582. // //FIXME REMOVE TEST
  583. // fontChanged = true;
  584. ///////////////////////////////////////
  585. boolean textPositionChanged = charX != null || charY != null || charDX != null || charDY != null;
  586. //TODO if we forceAnewTextarea because of font change but ther is NO NEW POSITION, we
  587. //have to set the textareas anchor to the lower left
  588. //TODO problem if we have a tspan centered and a next tspan without new position
  589. //-> the first tspan textarea gets centered on the position
  590. //but we would have to treat them (all in the same line) as 1 textarea when center positioning!
  591. //FIXME there are slight differences because we use a different SPACE character length and no font KERNING!
  592. //FIXME DO WIDHTOUT USERDATA
  593. //FIXME bitmap font has no top border, vector has.. why?
  594. //TODO -> eventuell doch in handleSvgNode machen?
  595. //-> statt graphicsnode /stylable node ?bergeben? - SVGOMTextElement is nicht instanceof graphicsnode..
  596. // we have to check font/color etc at every character, not only at new positon because
  597. //pos doesent change at tspans without new posinfo
  598. //check if equal to last used font and if equal original text font
  599. if ( fontChanged || textPositionChanged
  600. ){ //Make a new textarea if the text position changed or if the font changed at the current character
  601. MTTextArea previousTextArea = null;
  602. if (!textAreas.isEmpty()){
  603. previousTextArea = textAreas.get(textAreas.size()-1);
  604. }
  605. float newXPos = 0;
  606. float newYPos = 0 ;
  607. //If there is a previous text, get its ending coordinates
  608. //for the DX and DY shift info for the next text area
  609. if (previousTextArea != null){
  610. PositionAnchor oldAnchor = previousTextArea.getAnchor();
  611. // previousTextArea.setAnchor(PositionAnchor.LOWER_RIGHT);
  612. previousTextArea.setAnchor(PositionAnchor.UPPER_LEFT);
  613. //Calculate last/current textposition for DX and DY use
  614. //add up the last textareas start position end position(width)
  615. Vector3D lastPos = previousTextArea.getPosition(TransformSpace.LOCAL);
  616. // lastPos.addLocal(new Vector3D(previousTextArea.getWidthXY(TransformSpace.LOCAL) - 1 * previousTextArea.getInnerPaddingLeft(),0));
  617. lastPos.addLocal(new Vector3D(previousTextArea.getWidthXY(TransformSpace.LOCAL) - 2 * previousTextArea.getInnerPaddingLeft(),0));
  618. // newXPos = lastPos.x - previousTextArea.getInnerPaddingLeft();
  619. newXPos = lastPos.x;
  620. newXPos += (Float)previousTextArea.getUserData("XPos");
  621. newYPos = lastPos.y;
  622. // newYPos -= previousTextArea.getInnerPaddingTop();
  623. // newYPos += fontToUse.getFontMaxDescent(); //FIXME WHY NEVESSARY?
  624. newYPos += (Float)previousTextArea.getUserData("YPos");
  625. previousTextArea.setAnchor(oldAnchor);
  626. }
  627. //IF absolute x or y is present overwrite the position values from the last textarea
  628. if (charX != null)
  629. newXPos = (Float)charX;
  630. if (charY != null)
  631. newYPos = (Float)charY;
  632. if (charDX != null)
  633. newXPos += (Float)charDX;
  634. if (charDY != null)
  635. newYPos += (Float)charDY;
  636. // Create the text area \\
  637. MTTextArea t = new MTTextArea(pa, fontToUse);
  638. t.setNoFill(true);
  639. t.setNoStroke(true);
  640. textAreas.add(t);
  641. try{
  642. t.setLocalMatrix(new Matrix(currentLocalTransformMatrix));
  643. }catch(Exception e){
  644. logger.error(e.getMessage());
  645. }
  646. //FIXME TEST
  647. // if (previousTextArea != null && !textPositionChange){
  648. // t.setAnchor(PositionAnchor.LOWER_LEFT);
  649. // t.setUserData("posRelParent", new Vector3D(newXPos , newYPos - fontToUse.getFontMaxDescent() , 0));
  650. // logger.debug("Character '" + currentChar + "' -> Anchor: LOWER_LEFT");
  651. // }else{
  652. Value v = CSSUtilities.getComputedStyle(textElement, SVGCSSEngine.TEXT_ANCHOR_INDEX);
  653. //INFO: we have to move the BASELINE of the text to the svg position
  654. //The textarea is usually fontmaxascent+fontmaxdescent+2*innerPadding big!
  655. switch (v.getStringValue().charAt(0)) {
  656. case 'e':
  657. t.setAnchor(PositionAnchor.LOWER_RIGHT);
  658. t.setUserData("posRelParent", new Vector3D((newXPos + t.getInnerPaddingLeft()) , newYPos - fontToUse.getFontMaxDescent() + t.getInnerPaddingTop() , 0));
  659. // t.setPositionRelativeToParent(new Vector3D(newXPos, newYPos - font.getFontMaxDescent() , 0));
  660. logger.debug("Character '" + currentChar + "' -> Anchor: LOWER_RIGHT");
  661. break;
  662. case 'm': //text-anchor="middle"
  663. t.setAnchor(PositionAnchor.CENTER);
  664. // t.setUserData("posRelParent", new Vector3D(newXPos, newYPos - fontToUse.getFontMaxAscent()*0.5f - fontToUse.getFontMaxDescent()*0.5f , 0));
  665. // t.setUserData("posRelParent", new Vector3D(newXPos, newYPos - fontToUse.getFontAbsoluteHeight()*0.5f + t.getInnerPaddingTop() , 0));
  666. // t.setPositionRelativeToParent(new Vector3D(newXPos, newYPos - font.getFontMaxAscent()*0.5f - font.getFontMaxDescent()*0.5f, 0)); //- font.getFontMaxAscent()*0.5f
  667. logger.debug("Character '" + currentChar + "' -> Anchor: CENTER");
  668. t.setUserData("posRelParent", new Vector3D((newXPos), (newYPos - fontToUse.getFontMaxDescent() + t.getInnerPaddingTop()) - t.getHeightXY(TransformSpace.LOCAL)/2f , 0));
  669. break;
  670. default: //text-anchor="start" //default!
  671. t.setAnchor(PositionAnchor.LOWER_LEFT);
  672. // t.setUserData("posRelParent", new Vector3D(newXPos -t.getInnerPaddingLeft(), newYPos - fontToUse.getFontMaxDescent() + t.getInnerPaddingTop() , 0));
  673. t.setUserData("posRelParent", new Vector3D(newXPos -t.getInnerPaddingLeft(), newYPos - fontToUse.getFontMaxDescent() + t.getInnerPaddingTop() , 0));
  674. // t.setAnchor(PositionAnchor.UPPER_LEFT);
  675. // t.setUserData("posRelParent", new Vector3D(newXPos -t.getInnerPaddingLeft(), newYPos, 0));
  676. // t.setPositionRelativeToParent(new Vector3D(newXPos, newYPos - font.getFontMaxDescent() , 0));
  677. logger.debug("Character '" + currentChar + "' -> Anchor: LOWER_LEFT");
  678. }
  679. t.setUserData("XPos", newXPos);
  680. t.setUserData("YPos", newYPos);
  681. // }
  682. }
  683. //Add character to the current textarea in the list
  684. if (!textAreas.isEmpty()){
  685. textAreas.get(textAreas.size()-1).appendCharByUnicode(Character.toString(currentChar));
  686. }
  687. }
  688. //Set the positions of the textareas
  689. for (MTTextArea textArea : textAreas) {
  690. logger.debug("Adding text area at: " + (Vector3D) textArea.getUserData("posRelParent"));
  691. textArea.setPositionRelativeToParent((Vector3D) textArea.getUserData("posRelParent"));
  692. }
  693. comps.addAll(textAreas);
  694. }
  695. /*
  696. //This gets only the text of this hierarchy level
  697. StringBuffer result = new StringBuffer();
  698. for (Node n = textElement.getFirstChild();
  699. n != null;
  700. n = n.getNextSibling()) {
  701. switch (n.getNodeType()) {
  702. case Node.ELEMENT_NODE:
  703. break;
  704. case Node.CDATA_SECTION_NODE:
  705. case Node.TEXT_NODE:
  706. result.append(n.getNodeValue());
  707. }
  708. }
  709. logger.debug("TEXTTTT2: " + result);
  710. */
  711. // */////////////////////
  712. }
  713. }
  714. }
  715. if (node instanceof SVGGraphicsElement){
  716. //Remove inherited opacity attribute from stack
  717. opacityStack.pop();
  718. }
  719. //Traverse the children, not if it was a group element
  720. //because then the children are already
  721. //traversed in the if (group) block above
  722. if ( !(node instanceof SVGOMGElement)
  723. && !(node instanceof SVGSVGElement)
  724. && !(node instanceof SVGOMSVGElement)
  725. ){
  726. traverseChildren(node, comps);
  727. }
  728. }
  729. /**
  730. * Traverse children.
  731. *
  732. * @param node the node
  733. * @param comps the comps
  734. */
  735. private void traverseChildren(Node node, ArrayList<MTComponent> comps){
  736. //Check the children
  737. NodeList nl = node.getChildNodes();
  738. for (int i = 0; i < nl.getLength(); i++) {
  739. Node currentNode = nl.item(i);
  740. traverseSVGDoc(currentNode, comps);
  741. }
  742. }
  743. /**
  744. * Handle graphics node.
  745. *
  746. * @param gfxElem the gfx elem
  747. *
  748. * @return the mT base component
  749. */
  750. private MTComponent handleGraphicsNode(SVGGraphicsElement gfxElem){
  751. MTComponent returnComp = null;
  752. // logger.debug("Handle Element: " + gfxElem.getTagName());
  753. //Print all css properties and values
  754. // logger.debug("Style Css Text: " + style.getCssText());
  755. // SVG Defaults \\
  756. float fillR = 255;
  757. float fillG = 255;
  758. float fillB = 255;
  759. boolean noFill = false;
  760. float strokeR = 0;
  761. float strokeG = 0;
  762. float strokeB = 0;
  763. float strokeWidth = 1.0f;
  764. boolean noStroke = false;
  765. float strokeOpacity = 1;
  766. float fillOpacity = 1;
  767. int windingRule = GLU.GLU_TESS_WINDING_NONZERO;
  768. // SVG Defaults \\
  769. // Opacity, not as a style attribute but a separate
  770. // as group opacity doesnt get computed right, so we
  771. // mannually track it on a stack
  772. float opacity = opacityStack.peek();
  773. //logger.debug("INHERITED OPACITY: " + opacity);
  774. // FILL-RULE \\
  775. Value fillRuleValue = CSSUtilities.getComputedStyle(gfxElem, SVGCSSEngine.FILL_RULE_INDEX);
  776. String fillRule = fillRuleValue.getStringValue();
  777. if (fillRule.equalsIgnoreCase("nonzero")){
  778. windingRule = GLU.GLU_TESS_WINDING_NONZERO;
  779. }else if (fillRule.equalsIgnoreCase("evenodd")){
  780. windingRule = GLU.GLU_TESS_WINDING_ODD;
  781. }else{
  782. windingRule = GLU.GLU_TESS_WINDING_NONZERO;
  783. }
  784. //logger.debug("fillRule: " + fillRule);
  785. // Fill Opacity \\
  786. fillOpacity = PaintServer.convertOpacity(CSSUtilities.getComputedStyle(gfxElem, SVGCSSEngine.FILL_OPACITY_INDEX));
  787. //Multiplicate inherited opacity with this components opacities
  788. fillOpacity *= opacity;
  789. //Save for eventual lineargradient creation later that needs the not interpolated value
  790. float originalFillOpacity = fillOpacity;
  791. //logger.debug("fill opacity unnormalized: " + fillOpacity);
  792. // Fill java.awt.Color \\
  793. Value fillIndexValue = CSSUtilities.getComputedStyle(gfxElem, SVGCSSEngine.FILL_INDEX);
  794. Object fill = SVGLoader.getFillOrStroke(gfxElem, fillIndexValue, fillOpacity, ctx);
  795. SVGOMLinearGradientElement linearGradient = null;
  796. SVGOMRadialGradientElement radialGradient = null;
  797. if (fill instanceof java.awt.Color) {
  798. java.awt.Color color = (Color) fill;
  799. fillR = color.getRed();
  800. fillG = color.getGreen();
  801. fillB = color.getBlue();
  802. fillOpacity = color.getAlpha();
  803. noFill = false;
  804. //logger.debug("Fill: " + color + " a=" + fillOpacity);
  805. }else if (fill instanceof SVGOMLinearGradientElement) {
  806. //TODO cache gradients so dass man nicht immer neu den gleichen
  807. //machen muss!
  808. linearGradient = (SVGOMLinearGradientElement) fill;
  809. noFill = false;
  810. }else if (fill instanceof SVGOMRadialGradientElement) {
  811. //TODO!! //FIXME TEST
  812. radialGradient = (SVGOMRadialGradientElement)fill;
  813. noFill = false;
  814. }else{
  815. noFill = true;
  816. }
  817. // Stroke Opacity \\
  818. strokeOpacity = PaintServer.convertOpacity(CSSUtilities.getComputedStyle(gfxElem, SVGCSSEngine.STROKE_OPACITY_INDEX));
  819. // Multiplicate inherited opacity with this components group opacities
  820. strokeOpacity *= opacity;
  821. // Stroke java.awt.Color \\
  822. Value strokeIndexValue = CSSUtilities.getComputedStyle(gfxElem, SVGCSSEngine.STROKE_INDEX);
  823. Object stroke = SVGLoader.getFillOrStroke(gfxElem, strokeIndexValue, strokeOpacity, ctx);
  824. if (stroke instanceof java.awt.Color) {
  825. java.awt.Color color = (Color) stroke;
  826. strokeR = color.getRed();
  827. strokeG = color.getGreen();
  828. strokeB = color.getBlue();
  829. strokeOpacity = color.getAlpha();
  830. noStroke = false;
  831. }else{
  832. noStroke = true;
  833. strokeR = fillR;
  834. strokeG = fillG;
  835. strokeB = fillB;
  836. }
  837. // Stroke Width \\
  838. Stroke s = PaintServer.convertStroke(gfxElem);
  839. if (s != null){
  840. if (s instanceof BasicStroke) {
  841. BasicStroke basicStroke = (BasicStroke) s;
  842. strokeWidth = basicStroke.getLineWidth();
  843. }
  844. }else{
  845. strokeWidth = 0.0f;
  846. noStroke = true;
  847. }
  848. /*
  849. logger.debug("Fill COL: " + fillR + " " + fillG + " " + fillB + " " fillopacity);
  850. logger.debug("STROKE COL: " + strokeR + " " + strokeG + " " + strokeB + " " strokeOpacity);
  851. */
  852. // CHECK WHAT KIND OF GRAPHICS ELEMENT IT IS AND CREATE IT \\
  853. if (gfxElem instanceof SVGOMPathElement){
  854. SVGOMPathElement pathElem = (SVGOMPathElement)gfxElem;
  855. //FIXME handle clip-paths in the future
  856. if (isUnderClipPath(pathElem)){
  857. logger.error("Discarding clip-path path element. Not implemented.");
  858. return null;
  859. }
  860. //Create the shape
  861. AbstractShape pathComp = getLivePathComponent(pathElem, noFill, windingRule);
  862. try{
  863. pathComp.setLocalMatrix(currentLocalTransformMatrix);
  864. }catch(Exception e){
  865. logger.error(e.getMessage());
  866. }
  867. returnComp = pathComp;
  868. }else if (gfxElem instanceof SVGOMPolygonElement){
  869. SVGOMPolygonElement polygonElem = (SVGOMPolygonElement)gfxElem;
  870. //Create the shape
  871. AbstractShape comp = getLivePolygonComponent(polygonElem, noFill, windingRule);
  872. try{
  873. comp.setLocalMatrix(currentLocalTransformMatrix);
  874. }catch(Exception e){
  875. logger.error(e.getMessage());
  876. }
  877. returnComp = comp;
  878. }else if (gfxElem instanceof SVGOMPolylineElement){
  879. SVGOMPolylineElement polyLineElem = (SVGOMPolylineElement)gfxElem;
  880. //Create Vertex[] from points
  881. SVGPointList pointList = polyLineElem.getPoints();
  882. Vertex[] vertices = new Vertex[pointList.getNumberOfItems()];
  883. for (int i = 0; i < pointList.getNumberOfItems(); i++) {
  884. SVGPoint p = pointList.getItem(i);
  885. vertices[i] = new Vertex(p.getX(), p.getY(),0);
  886. }
  887. //Create the shape
  888. AbstractShape comp = createPoly(vertices);
  889. try{
  890. comp.setLocalMatrix(currentLocalTransformMatrix);
  891. }catch(Exception e){
  892. logger.error(e.getMessage());
  893. }
  894. returnComp = comp;
  895. }else if (gfxElem instanceof SVGOMRectElement){
  896. SVGOMRectElement rectElem = (SVGOMRectElement)gfxElem;
  897. if (isUnderClipPath(rectElem)){
  898. logger.error("discarding clip-path Rect");
  899. return null;
  900. }
  901. float x = rectElem.getX().getBaseVal().getValue();
  902. float y = rectElem.getY().getBaseVal().getValue();
  903. float width = rectElem.getWidth().getBaseVal().getValue();
  904. float height = rectElem.getHeight().getBaseVal().getValue();
  905. float rx = rectElem.getRx().getBaseVal().getValue();
  906. float ry = rectElem.getRy().getBaseVal().getValue();
  907. AbstractShape comp;
  908. //Create a normal rectangle or a round rectangle
  909. if (rx != 0.0f || ry != 0.0f){
  910. if (rx > width/2 )
  911. rx = width/2;
  912. if (ry > height/2 )
  913. ry = height/2;
  914. comp = new MTRoundRectangle(pa,x,y, 0,width,height, rx, ry);
  915. }else{
  916. comp = new MTRectangle(pa,x, y,width, height);
  917. }
  918. try{
  919. comp.setLocalMatrix(currentLocalTransformMatrix);
  920. }catch(Exception e){
  921. logger.error(e.getMessage());
  922. }
  923. returnComp = comp;
  924. }else if (gfxElem instanceof SVGOMEllipseElement){
  925. SVGOMEllipseElement ellipseElem = (SVGOMEllipseElement)gfxElem;
  926. float cx = ellipseElem.getCx().getBaseVal().getValue();
  927. float cy = ellipseElem.getCy().getBaseVal().getValue();
  928. float r = ellipseElem.getRx().getBaseVal().getValue();
  929. float r2 = ellipseElem.getRy().getBaseVal().getValue();
  930. Vertex middlePoint = new Vertex(cx,cy,0);
  931. //Apply transformation, transform centerpoint and the radii
  932. try{
  933. middlePoint.transform(currentLocalTransformMatrix);
  934. }catch(Exception e){
  935. logger.error(e.getMessage());
  936. }
  937. //somehow the circle radii need to be doubled
  938. //or else theyre too small => processing bug?
  939. // r*=2;
  940. // r2*=2;
  941. MTEllipse comp = new MTEllipse(pa, middlePoint, r, r2);
  942. returnComp = comp;
  943. }else if (gfxElem instanceof SVGOMCircleElement){
  944. SVGOMCircleElement circleElem = (SVGOMCircleElement)gfxElem;
  945. float cx = circleElem.getCx().getBaseVal().getValue();
  946. float cy = circleElem.getCy().getBaseVal().getValue();
  947. float r = circleElem.getR().getBaseVal().getValue();
  948. float r2 = circleElem.getR().getBaseVal().getValue();
  949. Vertex middlePoint = new Vertex(cx,cy,0);
  950. //Apply transformation, transform centerpoint and the radii
  951. try{
  952. middlePoint.transform(currentLocalTransformMatrix);
  953. }catch(Exception e){
  954. logger.error(e.getMessage());
  955. }
  956. //somehow the circle radii need to be doubled
  957. //or else theyre too small => processing bug?
  958. // r*=2;
  959. // r2*=2;
  960. MTEllipse comp = new MTEllipse(pa, middlePoint, r, r2);
  961. returnComp = comp;
  962. }else if (gfxElem instanceof SVGOMLineElement){
  963. SVGOMLineElement line = (SVGOMLineElement)gfxElem;
  964. float x1 = line.getX1().getBaseVal().getValue();
  965. float y1 = line.getY1().getBaseVal().getValue();
  966. float x2 = line.getX2().getBaseVal().getValue();
  967. float y2 = line.getY2().getBaseVal().getValue();
  968. //logger.debug("Line x1: " + x1 + ",y1:" + y1 + ",x2:" + x2 + ",y2:" + y2);
  969. MTLine comp = new MTLine(pa, x1,y1 ,x2,y2);
  970. try{
  971. comp.setLocalMatrix(currentLocalTransformMatrix);
  972. }catch(Exception e){
  973. logger.error(e.getMessage());
  974. }
  975. returnComp = comp;
  976. }else if (gfxElem instanceof SVGOMClipPathElement){
  977. }else if (gfxElem instanceof SVGOMDefsElement){
  978. }else if (gfxElem instanceof SVGOMMaskElement){
  979. }else if (gfxElem instanceof SVGOMSwitchElement){
  980. }else if (gfxElem instanceof SVGOMFlowRootElement){
  981. }else if (gfxElem instanceof SVGURIReferenceGraphicsElement){
  982. }else if (gfxElem instanceof BindableElement){
  983. }else if (gfxElem instanceof SVGOMForeignObjectElement){
  984. }else if (gfxElem instanceof SVGOMToBeImplementedElement){
  985. }
  986. //Do the finishing touch of the svg graphics element
  987. if (returnComp != null){
  988. returnComp.setName(gfxElem.getTagName());
  989. //Set style infos
  990. if (returnComp instanceof AbstractVisibleComponent){
  991. AbstractVisibleComponent comp = (AbstractVisibleComponent)returnComp;
  992. //Set Fill
  993. comp.setFillColor(new MTColor(fillR, fillG, fillB, fillOpacity));
  994. comp.setNoFill(noFill);
  995. //Set Stroke
  996. comp.setStrokeColor(new MTColor(strokeR, strokeG, strokeB, strokeOpacity));
  997. //Opengl cant handle big lines well
  998. //So cap at width 3
  999. if (strokeWidth > 2.0f)
  1000. strokeWidth = 2.0f;
  1001. comp.setStrokeWeight(strokeWidth);
  1002. comp.setNoStroke(noStroke);
  1003. //Other
  1004. comp.setDrawSmooth(true);
  1005. comp.setPickable(false);
  1006. //Hack for smoothing non stroked components with a stroke same as fillcolor
  1007. if (comp.isNoStroke()
  1008. && linearGradient == null
  1009. ){
  1010. comp.setStrokeColor(new MTColor(fillR, fillG, fillB, fillOpacity)); //fillOpacity
  1011. comp.setStrokeWeight(0.6f);
  1012. //Ellipse doesent smooth right with 0.1f strokeweight
  1013. if (comp instanceof MTEllipse){
  1014. comp.setStrokeWeight(1.0f);
  1015. }
  1016. comp.setNoStroke(false);
  1017. }
  1018. //Some settings for Geometric shapes (actually should all be)
  1019. if (comp instanceof AbstractShape ){
  1020. AbstractShape shape = (AbstractShape)comp;
  1021. //Set a bounding rectangle to check first at picking
  1022. if (shape.getVerticesLocal().length >= 3){
  1023. shape.setBoundsBehaviour(AbstractShape.BOUNDS_CHECK_THEN_GEOMETRY_CHECK);
  1024. //shape.setBoundingShape(new BoundsZPlaneRectangle(shape)); //already done by override, (ie svgpoly)
  1025. //Create amd apply the linear gradient if existant and if we are in opengl rendering mode
  1026. if (MT4jSettings.getInstance().isOpenGlMode()){
  1027. if (linearGradient != null){
  1028. FillPaint gradient = this.createLinearGradient(linearGradient, gfxElem, originalFillOpacity, shape);
  1029. if (gradient != null){
  1030. shape.setFillPaint(gradient);
  1031. }
  1032. }
  1033. if (radialGradient != null){
  1034. FillPaint gradient = this.createRadialGradient(radialGradient, gfxElem, opacity, shape);
  1035. if (gradient != null){
  1036. shape.setFillPaint(gradient);
  1037. }
  1038. }
  1039. //Per default use direct gl drawing and displaylists in OGL mode
  1040. if (pa instanceof MTApplication) {
  1041. MTApplication app = (MTApplication) pa;
  1042. app.invokeLater(new InvokeLaterAction(shape));
  1043. }
  1044. }
  1045. //IF shape has no or only 1 vertex return null
  1046. }else if (shape.getVerticesLocal().length < 2){
  1047. return null;
  1048. }else{
  1049. shape.setBoundsBehaviour(AbstractShape.BOUNDS_DONT_USE);
  1050. shape.setBounds(null);
  1051. // shape.setUseDirectGL(false);
  1052. }
  1053. //Allow for picking the shape
  1054. shape.setPickable(true);
  1055. //Assign default gestures
  1056. // shape.assignGestureClassAndAction(DragGestureAnalyzer.class, defaultDragAction);
  1057. // shape.registerInputAnalyzer(new DragDetector(pa));
  1058. // shape.setGestureAllowance(DragDetector.class, true);
  1059. // shape.addGestureListener(DragDetector.class, (IGestureEventListener)defaultDragAction);
  1060. // shape.registerInputAnalyzer(new RotationDetector(pa));
  1061. // shape.addGestureListener(RotationDetector.class, new DefaultRotateAction());
  1062. // shape.registerInputAnalyzer(new ScaleDetector(pa));
  1063. // shape.addGestureListener(ScaleDetector.class, new DefaultScaleAction());
  1064. }
  1065. }
  1066. }
  1067. return returnComp;
  1068. }
  1069. private class InvokeLaterAction implements Runnable{
  1070. private AbstractShape shape;
  1071. public InvokeLaterAction(AbstractShape shape) {
  1072. super();
  1073. this.shape = shape;
  1074. }
  1075. //@Override
  1076. public void run() {
  1077. shape.setUseDirectGL(true);
  1078. shape.generateAndUseDisplayLists();
  1079. }
  1080. }
  1081. /**
  1082. *
  1083. * @param paintedElement the element interested in a Paint
  1084. * @param paintDef the paint definition
  1085. * @param opacity the opacity to consider for the Paint
  1086. * @param ctx the bridge context
  1087. */
  1088. public static Object getFillOrStroke(Element paintedElement,
  1089. Value paintDef,
  1090. float opacity,
  1091. BridgeContext ctx) {
  1092. if (paintDef.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
  1093. switch (paintDef.getPrimitiveType()) {
  1094. case CSSPrimitiveValue.CSS_IDENT:
  1095. return null; // none
  1096. case CSSPrimitiveValue.CSS_RGBCOLOR:
  1097. return PaintServer.convertColor(paintDef, opacity);
  1098. case CSSPrimitiveValue.CSS_URI:
  1099. String uri = paintDef.getStringValue();
  1100. Element paintElement = ctx.getReferencedElement(paintedElement, uri);
  1101. //logger.debug("Fill -> Uri: \"" + uri + "\" -> Referenced Element: \"" + paintElement.getNodeName() + "\" Class: \"" + paintElement.getClass() + "\"");
  1102. if (paintElement instanceof SVGOMLinearGradientElement){
  1103. SVGOMLinearGradientElement linearGradient = (SVGOMLinearGradientElement)paintElement;
  1104. return linearGradient;
  1105. // Bridge bridge = ctx.getBridge(paintElement);
  1106. // logger.debug("Bridge: " + bridge.getLocalName() + " Class: " + bridge.getClass());
  1107. // SVGLinearGradientElementBridge l = (SVGLinearGradientElementBridge)bridge;
  1108. // SVGOMLinearGradientElement linearGradient = (SVGOMLinearGradientElement)paintElement;
  1109. // SVGAnimatedEnumeration spreadMethod = linearGradient.getSpreadMethod();
  1110. }else if (paintElement instanceof SVGOMRadialGradientElement){
  1111. SVGOMRadialGradientElement radialGradElement = (SVGOMRadialGradientElement)paintElement;
  1112. // logger.error("Radial gradient encountered -> Not supported yet.");
  1113. return radialGradElement;
  1114. }else{
  1115. logger.error("Couldnt read referenced Fill or Stroke from URI.");
  1116. return null;
  1117. }
  1118. /*
  1119. return PaintServer.convertURIPaint(paintedElement,
  1120. paintedNode,
  1121. paintDef,
  1122. opacity,
  1123. ctx);
  1124. */
  1125. default:
  1126. throw new IllegalArgumentException
  1127. ("Paint argument is not an appropriate CSS value");
  1128. }
  1129. } else { // List
  1130. Value v = paintDef.item(0);
  1131. switch (v.getPrimitiveType()) {
  1132. case CSSPrimitiveValue.CSS_RGBCOLOR:
  1133. return PaintServer.convertRGBICCColor(paintedElement, v,
  1134. (ICCColor)paintDef.item(1),
  1135. opacity, ctx);
  1136. case CSSPrimitiveValue.CSS_URI: {
  1137. // Paint result = PaintServer.silentConvertURIPaint(paintedElement,
  1138. // paintedNode,
  1139. // v, opacity, ctx);
  1140. // if (result != null)
  1141. // return result;
  1142. String uri = v.getStringValue();
  1143. Element paintElement = ctx.getReferencedElement(paintedElement, uri);
  1144. //logger.debug("Fill -> Uri: \"" + uri + "\" -> Referenced Element: \"" + paintElement.getNodeName() + "\" Class: \"" + paintElement.getClass() + "\"");
  1145. if (paintElement instanceof SVGOMLinearGradientElement){
  1146. SVGOMLinearGradientElement linearGradient = (SVGOMLinearGradientElement)paintElement;
  1147. return linearGradient;
  1148. }else if (paintElement instanceof SVGOMRadialGradientElement){
  1149. SVGOMRadialGradientElement radialGradElement = (SVGOMRadialGradientElement)paintElement;
  1150. // logger.error("Radial gradient encountered -> Not supported yet.");
  1151. return radialGradElement;
  1152. }
  1153. v = paintDef.item(1);
  1154. switch (v.getPrimitiveType()) {
  1155. case CSSPrimitiveValue.CSS_IDENT:
  1156. return null; // none
  1157. case CSSPrimitiveValue.CSS_RGBCOLOR:
  1158. if (paintDef.getLength() == 2) {
  1159. return PaintServer.convertColor(v, opacity);
  1160. } else {
  1161. return PaintServer.convertRGBICCColor(paintedElement, v,
  1162. (ICCColor)paintDef.item(2),
  1163. opacity, ctx);
  1164. }
  1165. default:
  1166. throw new IllegalArgumentException
  1167. ("Paint argument is not an appropriate CSS value");
  1168. }
  1169. }
  1170. default:
  1171. // can't be reached
  1172. throw new IllegalArgumentException
  1173. ("Paint argument is not an appropriate CSS value");
  1174. }
  1175. }
  1176. }
  1177. //TODO
  1178. // - spreadMethod implement linear
  1179. // - linearGradient klasse machen mit der man ein gradient erstellen kann
  1180. // mit stops[] offsets[] colors[] xy, bbox/userSpace
  1181. // - wie shapes ohne stroke mit outline= gradient zeichnen f?r antialiasing?
  1182. // evtl gradientshape normal zeichnen, aber mit realshape clipmasken?
  1183. private FillPaint createRadialGradient(Element paintElement, SVGGraphicsElement gfxElem, float opacity, AbstractShape shape){
  1184. //Get the <stop> elements
  1185. List<Stop> stops = this.extractStops(paintElement, opacity, ctx);
  1186. // if no stops are defined, painting is the same as 'none'
  1187. if (stops == null) {
  1188. return null;
  1189. }
  1190. int stopLength = stops.size();
  1191. // if one stops is defined, painting is the same as a single color
  1192. if (stopLength == 1) {
  1193. return null;
  1194. }
  1195. float [] offsets = new float[stopLength];
  1196. java.awt.Color [] colors = new java.awt.Color[stopLength];
  1197. Iterator<Stop> iter = stops.iterator();
  1198. for (int i=0; iter.hasNext(); ++i) {
  1199. Stop stop = iter.next();
  1200. offsets[i] = stop.offset;
  1201. colors[i] = stop.color;
  1202. }
  1203. //Get the spread method of the gradient
  1204. MultipleGradientPaint.CycleMethodEnum spreadMethod = getSpreadMethod(paintElement);
  1205. //'color-interpolation' CSS property
  1206. MultipleGradientPaint.ColorSpaceEnum colorSpace = CSSUtilities.convertColorInterpolation(paintElement);
  1207. //Get the gradient transform - //'gradientTransform' attribute - default is an Identity matrix
  1208. AffineTransform transform = getGradientTransform(paintElement);
  1209. //////////////////////////////////buildgradient function
  1210. // 'cx' attribute - default is 50%
  1211. String cxStr = SVGUtilities.getChainableAttributeNS
  1212. (paintElement, null, SVG_CX_ATTRIBUTE, ctx);
  1213. if (cxStr.length() == 0) {
  1214. cxStr = SVG_RADIAL_GRADIENT_CX_DEFAULT_VALUE;
  1215. }
  1216. // 'cy' attribute - default is 50%
  1217. String cyStr = SVGUtilities.getChainableAttributeNS
  1218. (paintElement, null, SVG_CY_ATTRIBUTE, ctx);
  1219. if (cyStr.length() == 0) {
  1220. cyStr = SVG_RADIAL_GRADIENT_CY_DEFAULT_VALUE;
  1221. }
  1222. // 'r' attribute - default is 50%
  1223. String rStr = SVGUtilities.getChainableAttributeNS
  1224. (paintElement, null, SVG_R_ATTRIBUTE, ctx);
  1225. if (rStr.length() == 0) {
  1226. rStr = SVG_RADIAL_GRADIENT_R_DEFAULT_VALUE;
  1227. }
  1228. // 'fx' attribute - default is same as cx
  1229. String fxStr = SVGUtilities.getChainableAttributeNS
  1230. (paintElement, null, SVG_FX_ATTRIBUTE, ctx);
  1231. if (fxStr.length() == 0) {
  1232. fxStr = cxStr;
  1233. }
  1234. // 'fy' attribute - default is same as cy
  1235. String fyStr = SVGUtilities.getChainableAttributeNS
  1236. (paintElement, null, SVG_FY_ATTRIBUTE, ctx);
  1237. if (fyStr.length() == 0) {
  1238. fyStr = cyStr;
  1239. }
  1240. // 'gradientUnits' attribute - default is objectBoundingBox
  1241. short coordSystemType;
  1242. String s = SVGUtilities.getChainableAttributeNS
  1243. (paintElement, null, SVG_GRADIENT_UNITS_ATTRIBUTE, ctx);
  1244. if (s.length() == 0) {
  1245. coordSystemType = SVGUtilities.OBJECT_BOUNDING_BOX;
  1246. } else {
  1247. coordSystemType = SVGUtilities.parseCoordinateSystem(paintElement, SVG_GRADIENT_UNITS_ATTRIBUTE, s, ctx);
  1248. }
  1249. UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, paintElement);
  1250. float r = SVGUtilities.convertLength(rStr,
  1251. SVG_R_ATTRIBUTE,
  1252. coordSystemType,
  1253. uctx);
  1254. // A value of zero will cause the area to be painted as a single color
  1255. // using the color and opacity of the last gradient stop.
  1256. if (r == 0) {
  1257. return null;
  1258. }
  1259. Point2D c = SVGUtilities.convertPoint(cxStr,
  1260. SVG_CX_ATTRIBUTE,
  1261. cyStr,
  1262. SVG_CY_ATTRIBUTE,
  1263. coordSystemType,
  1264. uctx);
  1265. Point2D f = SVGUtilities.convertPoint(fxStr,
  1266. SVG_FX_ATTRIBUTE,
  1267. fyStr,
  1268. SVG_FY_ATTRIBUTE,
  1269. coordSystemType,
  1270. uctx);
  1271. //Get gradient vector
  1272. logger.debug("C: " + c + " F: " +f);
  1273. CycleMethod awtCycleMethod = CycleMethod.NO_CYCLE;
  1274. if (spreadMethod == MultipleGradientPaint.REPEAT){
  1275. awtCycleMethod = CycleMethod.REPEAT;
  1276. }else if(spreadMethod == MultipleGradientPaint.REFLECT){
  1277. awtCycleMethod = CycleMethod.REFLECT;
  1278. }
  1279. if (pa instanceof MTApplication) {
  1280. MTApplication app = (MTApplication) pa;
  1281. //Calculate a bounding rectangle from the rotated shape
  1282. BoundsZPlaneRectangle boundsZ = new BoundsZPlaneRectangle(shape, shape.getVerticesLocal());
  1283. Vector3D[] boundsVecs = boundsZ.getVectorsLocal();
  1284. float bBoxWidth = boundsZ.getWidthXY(TransformSpace.LOCAL);//boundsVecs[1].x - boundsVecs[0].x;
  1285. float bBoxHeight = boundsZ.getHeightXY(TransformSpace.LOCAL);//boundsVecs[2].y - boundsVecs[1].y;
  1286. SwingTextureRenderer swingTex;
  1287. final MTRectangle rectangle;
  1288. //Trial to make the texture as big as the bigger side of the bounding rectangle of the shape
  1289. //to allow for automatic texture stretching to fit when texture is applied
  1290. int size = -1;
  1291. if (coordSystemType == SVGUtilities.OBJECT_BOUNDING_BOX){
  1292. if (bBoxWidth >= bBoxHeight){
  1293. size = Math.round(bBoxWidth);
  1294. r *= bBoxWidth;
  1295. }else{
  1296. size = Math.round(bBoxHeight);
  1297. r *= bBoxHeight;
  1298. }
  1299. AffineTransform Mx = new AffineTransform();
  1300. Rectangle2D bounds = new Rectangle(Math.round(boundsVecs[0].x), Math.round(boundsVecs[0].y), size, size);
  1301. if (bounds != null) {
  1302. //we dont translate the center and focal point
  1303. //instead we create the gradient mask shape at that position
  1304. // Mx.translate(bounds.getX(), bounds.getY());
  1305. Mx.scale(bounds.getWidth(), bounds.getHeight());
  1306. }
  1307. Mx.concatenate(transform);
  1308. transform = Mx;
  1309. //Transform gradient vector points with gradientTransform
  1310. transform.transform(c, c);
  1311. transform.transform(f, f);
  1312. GradientPanel gradPanel = new GradientPanel(size, size, r, offsets, colors, (float)c.getX(), (float)c.getY(), (float)f.getX(), (float)f.getY(), awtCycleMethod);
  1313. // GradientPanel gradPanel = new GradientPanel(bBoxWidth, bBoxHeight, r, offsets, colors, (float)c.getX(), (float)c.getY(), (float)f.getX(), (float)f.getY());
  1314. swingTex = new SwingTextureRenderer(app, gradPanel);
  1315. swingTex.scheduleRefresh();
  1316. rectangle = new MTRectangle(pa, new Vertex(boundsVecs[0]), bBoxWidth, bBoxHeight);
  1317. rectangle.setName("Swing texture rendering");
  1318. rectangle.setTexture(swingTex.getTextureToRenderTo());
  1319. rectangle.setNoStroke(true);
  1320. rectangle.setPickable(false);
  1321. rectangle.setFillDrawMode(GL.GL_QUADS);
  1322. //Use displaylist by default for gradientshape
  1323. if (MT4jSettings.getInstance().isOpenGlMode()){
  1324. app.invokeLater(new InvokeLaterAction(rectangle));
  1325. }
  1326. //FIXME REMOVE TEST
  1327. /*//Draw the shape we draw in swing
  1328. MTRectangle rectanglePaintedComp = new MTRectangle(new Vertex(boundsVecs[0]), size, size, pa);
  1329. rectanglePaintedComp.setName("rectanglePaintedComp");
  1330. rectanglePaintedComp.setTexture(swingTex.getTextureToRenderTo());
  1331. rectanglePaintedComp.setFillColor(255, 255, 255, 150);
  1332. shape.addChild(rectanglePaintedComp);
  1333. */
  1334. }else{
  1335. //coordsystemtype = userSpaceOnUse!
  1336. //FIXME Problem at userOnSpace with proportional length (%)
  1337. //seems we have to take the width/height from the viewbox then!? and use bounding box code above? but we have to recalculate absoulte values then..
  1338. //Since we draw the gradient at 0,0 we have to transform the gradient points to there
  1339. AffineTransform Mx = new AffineTransform();
  1340. Mx.translate(-boundsVecs[0].x, -boundsVecs[0].y);
  1341. Mx.concatenate(transform);
  1342. transform = Mx;
  1343. //Transform gradient points with gradientTransform
  1344. transform.transform(c, c);
  1345. transform.transform(f, f);
  1346. // GradientPanel gradPanel = new GradientPanel(size, size, r, offsets, colors, (float)c.getX(), (float)c.getY(), (float)f.getX(), (float)f.getY());
  1347. GradientPanel gradPanel = new GradientPanel(bBoxWidth, bBoxHeight, r, offsets, colors, (float)c.getX(), (float)c.getY(), (float)f.getX(), (float)f.getY(), awtCycleMethod);
  1348. swingTex = new SwingTextureRenderer(app, gradPanel);
  1349. swingTex.scheduleRefresh();
  1350. rectangle = new MTRectangle(pa, new Vertex(boundsVecs[0]), bBoxWidth, bBoxHeight);
  1351. final GLTexture tex = swingTex.getTextureToRenderTo();
  1352. rectangle.setName("Swing texture rendering");
  1353. rectangle.setTexture(tex);
  1354. rectangle.setNoStroke(true);
  1355. rectangle.setPickable(false);
  1356. // /*//
  1357. if (MT4jSettings.getInstance().isOpenGlMode()){
  1358. app.invokeLater(new InvokeLaterAction(rectangle));
  1359. }
  1360. // */
  1361. //FIXME REMOVE TEST
  1362. /*//Draw the shape we draw in swing
  1363. MTRectangle rectanglePaintedComp = new MTRectangle(new Vertex(boundsVecs[0]), bBoxWidth, bBoxHeight, pa);
  1364. rectanglePaintedComp.setName("rectanglePaintedComp");
  1365. rectanglePaintedComp.setTexture(swingTex.getTextureToRenderTo());
  1366. rectanglePaintedComp.setFillColor(255, 255, 255, 150);
  1367. shape.addChild(rectanglePaintedComp);
  1368. */
  1369. }
  1370. FillPaint gradStencil = new FillPaint(((PGraphicsOpenGL)pa.g).gl, rectangle);
  1371. return gradStencil;
  1372. // return null;
  1373. }
  1374. return null;
  1375. }
  1376. /**
  1377. * Helper class to paint a radial gradient with java2D into a texture.
  1378. */
  1379. private class GradientPanel extends JPanel{
  1380. private float width;
  1381. private float height;
  1382. private float[] offsets;
  1383. private java.awt.Color[] colors;
  1384. private float cx;
  1385. private float cy;
  1386. private float fx;
  1387. private float fy;
  1388. private float radius;
  1389. private CycleMethod cycleMethod;
  1390. public GradientPanel(float width, float height, float radius, float[] offsets,
  1391. Color[] colors, float cx, float cy, float fx, float fy,
  1392. CycleMethod cycleMethod
  1393. ) {
  1394. super();
  1395. this.width = width;
  1396. this.height = height;
  1397. this.radius = radius;
  1398. this.offsets = offsets;
  1399. this.colors = colors;
  1400. this.cx = cx;
  1401. this.cy = cy;
  1402. this.fx = fx;
  1403. this.fy = fy;
  1404. this.cycleMethod = cycleMethod;
  1405. this.setSize(Math.round(width), Math.round(height));
  1406. }
  1407. protected void paintComponent(Graphics g) {
  1408. super.paintComponent(g);
  1409. Graphics2D g2 = (Graphics2D)g;
  1410. int w = getWidth();
  1411. int h = getHeight();
  1412. Rectangle r = new Rectangle(0, 0, w, h); //original
  1413. RadialGradientPaint rgp = new RadialGradientPaint(
  1414. cx, cy,
  1415. // 400, 250,
  1416. radius,
  1417. // 100,
  1418. fx, fy,
  1419. // 400, 250,
  1420. offsets,
  1421. colors,
  1422. cycleMethod);
  1423. g2.setPaint(rgp);
  1424. g2.fill(r);
  1425. }
  1426. }
  1427. private FillPaint createLinearGradient(Element paintElement, SVGGraphicsElement gfxElem, float opacity, AbstractShape shape){
  1428. // stop elements
  1429. List<Stop> stops = this.extractStops(paintElement, opacity, ctx);
  1430. // if no stops are defined, painting is the same as 'none'
  1431. if (stops == null) {
  1432. return null;
  1433. }
  1434. int stopLength = stops.size();
  1435. // if one stops is defined, painting is the same as a single color
  1436. if (stopLength == 1) {
  1437. return null;
  1438. }
  1439. //Get the spread method of the gradient
  1440. MultipleGradientPaint.CycleMethodEnum spreadMethod = getSpreadMethod(paintElement);
  1441. //'color-interpolation' CSS property
  1442. MultipleGradientPaint.ColorSpaceEnum colorSpace = CSSUtilities.convertColorInterpolation(paintElement);
  1443. //Get the gradient transform - //'gradientTransform' attribute - default is an Identity matrix
  1444. AffineTransform transform = getGradientTransform(paintElement);
  1445. //logger.debug("Gradienttransform: " + transform);
  1446. //////////////////////////////////buildgradient function
  1447. // 'x1' attribute - default is 0%
  1448. String x1Str = SVGUtilities.getChainableAttributeNS(paintElement, null, SVG_X1_ATTRIBUTE, ctx);
  1449. if (x1Str.length() == 0) {
  1450. x1Str = SVG_LINEAR_GRADIENT_X1_DEFAULT_VALUE;
  1451. }
  1452. // 'y1' attribute - default is 0%
  1453. String y1Str = SVGUtilities.getChainableAttributeNS(paintElement, null, SVG_Y1_ATTRIBUTE, ctx);
  1454. if (y1Str.length() == 0) {
  1455. y1Str = SVG_LINEAR_GRADIENT_Y1_DEFAULT_VALUE;
  1456. }
  1457. // 'x2' attribute - default is 100%
  1458. String x2Str = SVGUtilities.getChainableAttributeNS(paintElement, null, SVG_X2_ATTRIBUTE, ctx);
  1459. if (x2Str.length() == 0) {
  1460. x2Str = SVG_LINEAR_GRADIENT_X2_DEFAULT_VALUE;
  1461. }
  1462. // 'y2' attribute - default is 0%
  1463. String y2Str = SVGUtilities.getChainableAttributeNS(paintElement, null, SVG_Y2_ATTRIBUTE, ctx);
  1464. if (y2Str.length() == 0) {
  1465. y2Str = SVG_LINEAR_GRADIENT_Y2_DEFAULT_VALUE;
  1466. }
  1467. // 'gradientUnits' attribute - default is objectBoundingBox
  1468. short coordSystemType;
  1469. String s2 = SVGUtilities.getChainableAttributeNS(paintElement, null, SVG_GRADIENT_UNITS_ATTRIBUTE, ctx);
  1470. if (s2.length() == 0) {
  1471. coordSystemType = SVGUtilities.OBJECT_BOUNDING_BOX;
  1472. } else {
  1473. coordSystemType = SVGUtilities.parseCoordinateSystem(paintElement, SVG_GRADIENT_UNITS_ATTRIBUTE, s2, ctx);
  1474. }
  1475. // additional transform to move to objectBoundingBox coordinate system
  1476. //TODO gradienttransform
  1477. // if (coordSystemType == SVGUtilities.OBJECT_BOUNDING_BOX) {
  1478. // transform = SVGUtilities.toObjectBBox(transform, gfxElem);
  1479. // }
  1480. UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, gfxElem);
  1481. Point2D p1 = SVGUtilities.convertPoint(x1Str,
  1482. SVG_X1_ATTRIBUTE,
  1483. y1Str,
  1484. SVG_Y1_ATTRIBUTE,
  1485. coordSystemType,
  1486. uctx);
  1487. Point2D p2 = SVGUtilities.convertPoint(x2Str,
  1488. SVG_X2_ATTRIBUTE,
  1489. y2Str,
  1490. SVG_Y2_ATTRIBUTE,
  1491. coordSystemType,
  1492. uctx);
  1493. //Transform gradient vector points with gradientTransform
  1494. Point2D tp1 = null;
  1495. tp1 = transform.transform(p1, tp1);
  1496. p1 = tp1;
  1497. Point2D tp2 = null;
  1498. tp2 = transform.transform(p2, tp2);
  1499. p2 = tp2;
  1500. //Get gradient vector
  1501. logger.debug("P1: " + p1 + " P2: " + p2);
  1502. Vector3D ref = new Vector3D(1,0,0);
  1503. Vector3D vP1 = new Vector3D((float)p1.getX(), (float)p1.getY(), 0);
  1504. Vector3D vP2 = new Vector3D((float)p2.getX(), (float)p2.getY(), 0);
  1505. Vector3D gradVect = vP2.getSubtracted(vP1);
  1506. //logger.debug("Gradient vector: " + gradVect);
  1507. //Get gradient vector angle to rotate the shape to be gradiented to the horizontal vector 1,0,0
  1508. //Algorithm for linear gradients used here:
  1509. //1. rotate the shape so that the gradient vector in the shape is parallel to 1,0,0
  1510. //2. calc bounding rectangle of shape + gradient rectangle (made of the x1,y1 x2,y2 gradient endpoints)
  1511. //3. create quads with colored vertices at each (now horizontal) stop along the gradient
  1512. //4. rotate the gradient shape with the quads back so it faces the original gradient vector direction
  1513. float gradAngle = Vector3D.angleBetween(ref, gradVect);
  1514. gradAngle = PApplet.degrees(gradAngle);
  1515. Vector3D cross = ref.getCross(gradVect);
  1516. //Get the direction of rotation
  1517. if (cross.getZ() < 0){
  1518. gradAngle*=-1;
  1519. }
  1520. logger.debug("Gradient angle: " + gradAngle + "?");
  1521. logger.debug("Stops:");
  1522. for (Stop stop : stops)
  1523. logger.debug(" Stop -> Offset: " + stop.offset + " java.awt.Color: " + stop.color);
  1524. if (coordSystemType == SVGUtilities.USER_SPACE_ON_USE){
  1525. return this.setUpRotatedGradientUserSpace(shape, gradAngle, stops, p1, p2);
  1526. }else{
  1527. return this.setUpRotatedGradientBBox(shape, gradAngle, stops);
  1528. }
  1529. // If x1 = x2 and y1 = y2, then the area to be painted will be painted
  1530. // as a single color using the color and opacity of the last gradient
  1531. // stop.
  1532. // if (p1.getX() == p2.getX() && p1.getY() == p2.getY()) {
  1533. // return colors[colors.length-1];
  1534. // } else {
  1535. // return new LinearGradientPaint(p1,
  1536. // p2,
  1537. // offsets,
  1538. // colors,
  1539. // spreadMethod,
  1540. // colorSpace,
  1541. // transform);
  1542. // }
  1543. }
  1544. private FillPaint setUpRotatedGradientUserSpace(AbstractShape testShape, float angle, List<Stop> stops, Point2D p1, Point2D p2){
  1545. GL gl = ((PGraphicsOpenGL)pa.g).gl;
  1546. float gradAngle = angle;
  1547. float invAngle = angle*-1;
  1548. //Get copy of shapes vertices
  1549. Vertex[] shapeVertsCopy = Vertex.getDeepVertexArrayCopy(testShape.getGeometryInfo().getVertices());
  1550. //Rotate the vertices in the inverse direction of the gradients vector angle
  1551. shapeVertsCopy = (Vertex[]) Vertex.rotateZVectorArray(shapeVertsCopy, testShape.getCenterPointLocal(), invAngle);
  1552. Vertex vP1 = new Vertex((float)p1.getX(), (float)p1.getY(), 0);
  1553. Vertex vP2 = new Vertex((float)p2.getX(), (float)p2.getY(), 0);
  1554. float gradientRectWidth = vP2.getSubtracted(vP1).length();
  1555. Vertex[] gradientRectVerts = new Vertex[]{
  1556. vP1,
  1557. new Vertex((float)p2.getX(), (float)p1.getY(),0),
  1558. vP2,
  1559. new Vertex((float)p1.getX(), (float)p2.getY(),0)
  1560. };
  1561. //Rotate the vertices in the inverse direction of the gradients vector angle
  1562. gradientRectVerts = (Vertex[]) Vertex.rotateZVectorArray(gradientRectVerts, testShape.getCenterPointLocal(), invAngle);
  1563. //Copy the rotated bounding shape vertices and the rotated gradient rectangle vertices into one array
  1564. Vertex[] shapeAndGradVerts = new Vertex[shapeVertsCopy.length + gradientRectVerts.length];
  1565. System.arraycopy(shapeVertsCopy, 0, shapeAndGradVerts, 0, shapeVertsCopy.length);
  1566. System.arraycopy(gradientRectVerts, 0, shapeAndGradVerts, shapeVertsCopy.length, gradientRectVerts.length);
  1567. //Create a temporary polygon with the roated vertices to calc BBox
  1568. MTPolygon inverseRotatedShape = new MTPolygon(pa, shapeAndGradVerts);
  1569. //Calculate a bounding rectangle from the rotated shape
  1570. BoundsZPlaneRectangle inverseRotatedBounds = new BoundsZPlaneRectangle(inverseRotatedShape);
  1571. Vector3D[] invBoundsVecs = inverseRotatedBounds.getVectorsLocal();
  1572. //logger.debug("Gradient Rectangle width: " + gradientRectWidth);
  1573. //Get the positions where the offsets are on the gradient vector
  1574. // float bBoxWidth = invBoundsVecs[1].x - invBoundsVecs[0].x;
  1575. // logger.debug("BBox width: " + bBoxWidth);
  1576. // float w = bBoxWidth/*/100*/;
  1577. List<Float> xStops = new ArrayList<Float>();
  1578. //- Go through stops
  1579. //- multiply stop offsets with bbox width to get the position on gradient vector
  1580. //logger.debug("->Gradient Vector stop positions:");
  1581. for(Stop stop : stops){
  1582. float offsetStopPosition = gradientRectWidth * stop.offset; //position auf gradient vector, stop(0) = vP1.x + offest
  1583. xStops.add(offsetStopPosition);
  1584. //logger.debug(" Offset-Stop-Position: " + offsetStopPosition);
  1585. }
  1586. //Calc new gradient polygon vertices with vertices at the stop locations
  1587. Vertex[] newBounds = new Vertex[(xStops.size()-1) * 4];
  1588. for (int i = 0; i < xStops.size()-1; i++) {
  1589. float offset = xStops.get(i);
  1590. Color stopColor = stops.get(i).color;
  1591. float nextOffset = xStops.get(i+1);
  1592. Color nextStopColor = stops.get(i+1).color;
  1593. newBounds[i*4] = new Vertex(vP1.x + offset, invBoundsVecs[0].y,0, stopColor.getRed(), stopColor.getGreen(), stopColor.getBlue(), stopColor.getAlpha());
  1594. newBounds[i*4+1] = new Vertex(vP1.x + nextOffset, invBoundsVecs[0].y,0, nextStopColor.getRed(), nextStopColor.getGreen(), nextStopColor.getBlue(), nextStopColor.getAlpha());
  1595. newBounds[i*4+2] = new Vertex(vP1.x + nextOffset, invBoundsVecs[2].y,0, nextStopColor.getRed(), nextStopColor.getGreen(), nextStopColor.getBlue(), nextStopColor.getAlpha());
  1596. newBounds[i*4+3] = new Vertex(vP1.x + offset, invBoundsVecs[2].y,0, stopColor.getRed(), stopColor.getGreen(), stopColor.getBlue(), stopColor.getAlpha());
  1597. }
  1598. //Put gradient rectangle quads into a list
  1599. List<Vertex> gradientRectQuads = new ArrayList<Vertex>();
  1600. for (Vertex vertex : newBounds) {
  1601. gradientRectQuads.add(vertex);
  1602. }
  1603. /* Bounding shape with gradient rectangle inside (can also overlap outlines)
  1604. invBoundsVecs[0] invBoundsVecs[1]
  1605. | _______________ |
  1606. | |_____| |
  1607. | | G | |
  1608. | vp1|____>|vp2 |
  1609. |____|_____|____|
  1610. */
  1611. //Calc rectangle bands (quads) to fill the gradient shape with the gradVect end colors if the gradient vector is smaller than the shape to draw
  1612. List<Vertex> leftQuad = new ArrayList<Vertex>();
  1613. if (vP1.x > invBoundsVecs[0].x){
  1614. //upper left of bounding rect
  1615. Vertex v1 = new Vertex(invBoundsVecs[0].x, invBoundsVecs[0].y, 0, newBounds[0].getR(), newBounds[0].getG(), newBounds[0].getB(), newBounds[0].getA());
  1616. //first stop on gradient vector upper
  1617. Vertex v2 = new Vertex(newBounds[0].x, newBounds[0].y, 0, newBounds[0].getR(), newBounds[0].getG(), newBounds[0].getB(), newBounds[0].getA());
  1618. //first stop on gradient vector lower
  1619. Vertex v3 = new Vertex(newBounds[3].x, newBounds[3].y, 0, newBounds[3].getR(), newBounds[3].getG(), newBounds[3].getB(), newBounds[3].getA());
  1620. //down left of bounding rect
  1621. Vertex v4 = new Vertex(invBoundsVecs[3].x, invBoundsVecs[3].y, 0, newBounds[0].getR(), newBounds[0].getG(), newBounds[0].getB(), newBounds[0].getA());
  1622. leftQuad.add(v1);
  1623. leftQuad.add(v2);
  1624. leftQuad.add(v3);
  1625. leftQuad.add(v4);
  1626. }
  1627. //Add Right quad if gradient rectangle is smaler than overall bounds
  1628. List<Vertex> rightQuad = new ArrayList<Vertex>();
  1629. if (vP2.x < invBoundsVecs[1].x){
  1630. Vertex gradientRectUpperRight = newBounds[newBounds.length-3];
  1631. Vertex gradientRectLowerRight = newBounds[newBounds.length-2];
  1632. Vertex v1 = new Vertex(gradientRectUpperRight.x, gradientRectUpperRight.y, 0, gradientRectUpperRight.getR(), gradientRectUpperRight.getG(), gradientRectUpperRight.getB(), gradientRectUpperRight.getA());
  1633. Vertex v2 = new Vertex(invBoundsVecs[1].x, invBoundsVecs[1].y, 0, gradientRectUpperRight.getR(), gradientRectUpperRight.getG(), gradientRectUpperRight.getB(), gradientRectUpperRight.getA());
  1634. Vertex v3 = new Vertex(invBoundsVecs[2].x, invBoundsVecs[2].y, 0, gradientRectUpperRight.getR(), gradientRectUpperRight.getG(), gradientRectUpperRight.getB(), gradientRectUpperRight.getA());
  1635. Vertex v4 = new Vertex(gradientRectLowerRight.x, gradientRectLowerRight.y, 0, gradientRectUpperRight.getR(), gradientRectUpperRight.getG(), gradientRectUpperRight.getB(), gradientRectUpperRight.getA());
  1636. rightQuad.add(v1);
  1637. rightQuad.add(v2);
  1638. rightQuad.add(v3);
  1639. rightQuad.add(v4);
  1640. }
  1641. //Create new array for gradient shape with all quads inside
  1642. List<Vertex> allGradientShapeVerts = new ArrayList<Vertex>();
  1643. allGradientShapeVerts.addAll(leftQuad);
  1644. allGradientShapeVerts.addAll(gradientRectQuads);
  1645. allGradientShapeVerts.addAll(rightQuad);
  1646. newBounds = allGradientShapeVerts.toArray(new Vertex[allGradientShapeVerts.size()]);
  1647. //Rotate the vectors of the calculated bounding rect back to the original angle
  1648. newBounds = (Vertex[]) Vector3D.rotateZVectorArray(newBounds, testShape.getCenterPointLocal(), gradAngle);
  1649. //Create gradient shape to paint over the real shape
  1650. MTPolygon p = new MTPolygon(pa, newBounds);
  1651. p.setNoStroke(true);
  1652. p.setPickable(false);
  1653. p.setStrokeWeight(testShape.getStrokeWeight());
  1654. p.setFillDrawMode(GL.GL_QUADS);
  1655. //Use displaylist by default for gradientshape
  1656. p.generateAndUseDisplayLists();
  1657. FillPaint gradStencil = new FillPaint(gl, p);
  1658. return gradStencil;
  1659. }
  1660. private FillPaint setUpRotatedGradientBBox(AbstractShape testShape, float angle, List<Stop> stops){
  1661. GL gl = ((PGraphicsOpenGL)pa.g).gl;
  1662. float gradAngle = angle;
  1663. //Get copy of shapes vertices
  1664. Vertex[] shapeVertsCopy = Vertex.getDeepVertexArrayCopy(testShape.getGeometryInfo().getVertices());
  1665. //Rotate the vertices in the inverse direction of the gradients vector angle
  1666. shapeVertsCopy = (Vertex[]) Vertex.rotateZVectorArray(shapeVertsCopy, testShape.getCenterPointLocal(), -gradAngle);
  1667. //Create a temporary polygon with the roated vertices to calc BBox
  1668. MTPolygon inverseRotatedShape = new MTPolygon(pa, shapeVertsCopy);
  1669. //Calculate a bounding rectangle from the rotated shape
  1670. BoundsZPlaneRectangle inverseRotatedBounds = new BoundsZPlaneRectangle(inverseRotatedShape);
  1671. Vector3D[] invBoundsVecs = inverseRotatedBounds.getVectorsLocal();
  1672. //Get the positions where the offsets are on the gradient vector
  1673. float bBoxWidth = invBoundsVecs[1].x - invBoundsVecs[0].x;
  1674. logger.debug("BBox width: " + bBoxWidth);
  1675. float w = bBoxWidth/*/100*/;
  1676. List<Float> xStops = new ArrayList<Float>();
  1677. //Go through stops and multiply stop offset with bbox width to get the position
  1678. for(Stop stop : stops){
  1679. float offsetStopPosition = w * stop.offset;
  1680. xStops.add(offsetStopPosition);
  1681. logger.debug("OffsetStopPosition: " + offsetStopPosition);
  1682. }
  1683. //Calc new gradient polygon vertices with vertices at the stop locations
  1684. Vertex[] newBounds = new Vertex[(xStops.size()-1) * 4];
  1685. for (int i = 0; i < xStops.size()-1; i++) {
  1686. float offset = xStops.get(i);
  1687. Color stopColor = stops.get(i).color;
  1688. float nextOffset = xStops.get(i+1);
  1689. Color nextStopColor = stops.get(i+1).color;
  1690. newBounds[i*4] = new Vertex(invBoundsVecs[0].x + offset, invBoundsVecs[0].y,0, stopColor.getRed(), stopColor.getGreen(), stopColor.getBlue(), stopColor.getAlpha());
  1691. newBounds[i*4+1] = new Vertex(invBoundsVecs[0].x + nextOffset, invBoundsVecs[0].y,0, nextStopColor.getRed(), nextStopColor.getGreen(), nextStopColor.getBlue(), nextStopColor.getAlpha());
  1692. newBounds[i*4+2] = new Vertex(invBoundsVecs[0].x + nextOffset, invBoundsVecs[2].y,0, nextStopColor.getRed(), nextStopColor.getGreen(), nextStopColor.getBlue(), nextStopColor.getAlpha());
  1693. newBounds[i*4+3] = new Vertex(invBoundsVecs[0].x + offset, invBoundsVecs[2].y,0, stopColor.getRed(), stopColor.getGreen(), stopColor.getBlue(), stopColor.getAlpha());
  1694. }
  1695. /*
  1696. logger.debug("->New bounds:");
  1697. for (int i = 0; i < newBounds.length; i++) {
  1698. Vertex vertex = newBounds[i];
  1699. logger.debug(vertex);
  1700. }
  1701. */
  1702. //Rotate the vectors of the calculated bounding rect back to the original angle
  1703. newBounds = (Vertex[]) Vector3D.rotateZVectorArray(newBounds, testShape.getCenterPointLocal(), gradAngle);
  1704. //Create gradient shape to paint over the real shape
  1705. MTPolygon p = new MTPolygon(pa, newBounds);
  1706. p.setNoStroke(true);
  1707. p.setPickable(false);
  1708. p.setFillDrawMode(GL.GL_QUADS);
  1709. p.setStrokeWeight(testShape.getStrokeWeight());
  1710. //Use displaylist by default for gradientshape
  1711. p.generateAndUseDisplayLists();
  1712. FillPaint gradStencil = new FillPaint(gl, p);
  1713. return gradStencil;
  1714. }
  1715. private CycleMethodEnum getSpreadMethod(Element paintElement){
  1716. String s = "";
  1717. //SPREADMETHOD 'spreadMethod' attribute - default is pad
  1718. CycleMethodEnum spreadMethod = MultipleGradientPaint.NO_CYCLE;
  1719. s = SVGUtilities.getChainableAttributeNS(paintElement, null, SVG_SPREAD_METHOD_ATTRIBUTE, ctx);
  1720. if (s.length() != 0) {
  1721. // spreadMethod = AbstractSVGGradientElementBridge.convertSpreadMethod(paintElement, s, ctx);
  1722. if (SVG_REPEAT_VALUE.equals(s)) {
  1723. spreadMethod = MultipleGradientPaint.REPEAT;
  1724. }else
  1725. if (SVG_REFLECT_VALUE.equals(s)) {
  1726. spreadMethod = MultipleGradientPaint.REFLECT;
  1727. }else
  1728. if (SVG_PAD_VALUE.equals(s)) {
  1729. spreadMethod = MultipleGradientPaint.NO_CYCLE;
  1730. }else
  1731. throw new BridgeException(ctx, paintElement, "ERR_ATTRIBUTE_VALUE_MALFORMED", new Object[] {SVG_SPREAD_METHOD_ATTRIBUTE, s});
  1732. }
  1733. return spreadMethod;
  1734. }
  1735. private AffineTransform getGradientTransform(Element paintElement){
  1736. String s = "";
  1737. //'gradientTransform' attribute - default is an Identity matrix
  1738. AffineTransform transform;
  1739. s = SVGUtilities.getChainableAttributeNS(paintElement, null, SVG_GRADIENT_TRANSFORM_ATTRIBUTE, ctx);
  1740. if (s.length() != 0) {
  1741. transform = SVGUtilities.convertTransform(paintElement, SVG_GRADIENT_TRANSFORM_ATTRIBUTE, s, ctx);
  1742. } else {
  1743. transform = new AffineTransform();
  1744. }
  1745. return transform;
  1746. }
  1747. /**
  1748. * Returns the stops elements of the specified gradient
  1749. * element. Stops can be children of the gradients or defined on
  1750. * one of its 'ancestor' (linked with the xlink:href attribute).
  1751. *
  1752. * @param paintElement the gradient element
  1753. * @param opacity the opacity
  1754. * @param ctx the bridge context to use
  1755. */
  1756. protected List<Stop> extractStops(Element paintElement, float opacity, BridgeContext ctx) {
  1757. //List<Object> refs = new LinkedList<Object>();
  1758. for (;;) {
  1759. List<Stop> stops = extractLocalStop(paintElement, opacity, ctx);
  1760. if (stops != null) {
  1761. boolean zeroOffset = false;
  1762. boolean oneOffset = false;
  1763. boolean zeroOffsetAdded = false;
  1764. boolean oneOffsetAdded = false;
  1765. //Stops in svg dont have to have ending stops at 0.0 and 1.0
  1766. //but we need them so we add them ourselves if not present
  1767. for (Stop stop :stops){
  1768. if (stop.offset == 0.0){
  1769. zeroOffset = true;
  1770. }
  1771. if (stop.offset == 1.0){
  1772. oneOffset = true;
  1773. }
  1774. }
  1775. //Add a stop for beginning and end if not existant
  1776. if (!zeroOffset){
  1777. logger.debug("No offset at 0.0 location -> adding it.");
  1778. stops.add(0, new AbstractSVGGradientElementBridge.Stop(new java.awt.Color(0,0,0,0), 0.0f));
  1779. zeroOffsetAdded = true;
  1780. }
  1781. if (!oneOffset){
  1782. logger.debug("No offset at 1.0 location -> adding it.");
  1783. stops.add(stops.size(), new AbstractSVGGradientElementBridge.Stop(new java.awt.Color(0,0,0,0), 1.0f));
  1784. oneOffsetAdded = true;
  1785. }
  1786. //Sort stops by offset position from 0.0 to 1.0
  1787. List<GradientStop> gradientStops = new ArrayList<GradientStop>();
  1788. for (Stop stop :stops){
  1789. gradientStops.add(new GradientStop(stop.offset, stop.color));
  1790. }
  1791. GradientStop[] gradStopArr = gradientStops.toArray(new GradientStop[gradientStops.size()]);
  1792. HelperMethods.quicksort(gradStopArr);
  1793. //Create new, sorted stop list, clamp color from self created zero/one offsets to nearest color
  1794. List<Stop> sortedStopList = new ArrayList<Stop>();
  1795. for (int i = 0; i < gradStopArr.length; i++) {
  1796. GradientStop gradientStop = gradStopArr[i];
  1797. if (zeroOffsetAdded
  1798. && i == 0
  1799. && gradStopArr.length >= i+1
  1800. && gradStopArr[i+1] != null
  1801. ){
  1802. gradientStop.color = gradStopArr[i+1].color;
  1803. }
  1804. if (oneOffsetAdded
  1805. && i == gradStopArr.length-1
  1806. //&& gradStopArr.length >= i+1
  1807. && gradStopArr[i-1] != null
  1808. ){
  1809. gradientStop.color = gradStopArr[i-1].color;
  1810. }
  1811. sortedStopList.add(new AbstractSVGGradientElementBridge.Stop(gradientStop.color, gradientStop.offset));
  1812. }
  1813. return sortedStopList;
  1814. // return stops; // stop elements found, exit
  1815. }
  1816. String uri = XLinkSupport.getXLinkHref(paintElement);
  1817. if (uri.length() == 0) {
  1818. return null; // no xlink:href found, exit
  1819. }
  1820. // check if there is circular dependencies
  1821. /*
  1822. String baseURI = XMLBaseSupport.getCascadedXMLBase(paintElement);
  1823. ParsedURL purl = new ParsedURL(baseURI, uri);
  1824. if (contains(refs, purl)) {
  1825. throw new BridgeException(paintElement,
  1826. ERR_XLINK_HREF_CIRCULAR_DEPENDENCIES,
  1827. new Object[] {uri});
  1828. }
  1829. refs.add(purl);
  1830. */
  1831. paintElement = ctx.getReferencedElement(paintElement, uri);
  1832. }
  1833. }
  1834. /**
  1835. * To compare stop offsets
  1836. * @author Chris
  1837. */
  1838. private class GradientStop implements Comparable<GradientStop>{
  1839. float offset;
  1840. Color color;
  1841. public GradientStop(float offset, java.awt.Color color2){
  1842. this.offset = offset;
  1843. this.color = color2;
  1844. }
  1845. //@Override
  1846. public int compareTo(GradientStop o) {
  1847. if (this.offset < o.offset){
  1848. return -1;
  1849. }
  1850. else if(offset == o.offset){
  1851. return 0;
  1852. }
  1853. else if(this.offset > o.offset){
  1854. return 1;
  1855. }else{
  1856. return 0;
  1857. }
  1858. }
  1859. }
  1860. /**
  1861. * Returns a list of <tt>Stop</tt> elements, children of the
  1862. * specified paintElement can have or null if any.
  1863. *
  1864. * @param gradientElement the paint element
  1865. * @param opacity the opacity
  1866. * @param ctx the bridge context
  1867. */
  1868. protected static List<Stop> extractLocalStop(Element gradientElement, float opacity, BridgeContext ctx) {
  1869. LinkedList<Stop> stops = null;
  1870. Stop previous = null;
  1871. for (Node n = gradientElement.getFirstChild(); n != null; n = n.getNextSibling()){
  1872. if ((n.getNodeType() != Node.ELEMENT_NODE)) {
  1873. continue;
  1874. }
  1875. Element e = (Element)n;
  1876. Bridge bridge = ctx.getBridge(e);
  1877. if (bridge == null || !(bridge instanceof SVGStopElementBridge)) {
  1878. continue;
  1879. }
  1880. Stop stop = ((SVGStopElementBridge)bridge).createStop(ctx, gradientElement, e, opacity);
  1881. if (stops == null) {
  1882. stops = new LinkedList<Stop>();
  1883. }
  1884. if (previous != null) {
  1885. if (stop.offset < previous.offset) {
  1886. stop.offset = previous.offset;
  1887. }
  1888. }
  1889. stops.add(stop);
  1890. previous = stop;
  1891. }
  1892. return stops;
  1893. }
  1894. private List<Float> getSVGLengthListAsFloat(SVGLengthList valueList){
  1895. List<Float> values = new ArrayList<Float>();
  1896. for (int i = 0; i < valueList.getNumberOfItems(); i++) {
  1897. values.add(valueList.getItem(i).getValue());
  1898. }
  1899. if (values.isEmpty()){
  1900. values.add(0f);
  1901. }
  1902. return values;
  1903. }
  1904. /**
  1905. * Tries to retrieve a css property as a float number.
  1906. * If it fails, it returns the provided defaultvalue.
  1907. *
  1908. * @param gfxElem the gfx elem
  1909. * @param queryProperty the query property
  1910. * @param defaultValue the default value
  1911. *
  1912. * @return the float
  1913. */
  1914. private float queryPrimitiveFloatValue(SVGGraphicsElement gfxElem, String queryProperty, float defaultValue){
  1915. float returnValue = defaultValue;
  1916. CSSStyleDeclaration style = gfxElem.getOwnerSVGElement().getComputedStyle(gfxElem, "");
  1917. CSSValue cssValue = (CSSValue) style.getPropertyCSSValue(queryProperty);
  1918. // logger.debug("CSSValue.getCssText() of proerty " + queryProperty + ": " + cssValue.getCssText());
  1919. if (cssValue != null){
  1920. if (cssValue.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE){
  1921. try{
  1922. org.w3c.dom.css.CSSPrimitiveValue v = (org.w3c.dom.css.CSSPrimitiveValue)cssValue;
  1923. if (v.getCssValueType() == CSSPrimitiveValue.CSS_NUMBER){
  1924. returnValue = v.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
  1925. // logger.debug(queryProperty + ": " + returnValue);
  1926. }
  1927. }catch(Exception e){
  1928. logger.error(e.getMessage());
  1929. }
  1930. }
  1931. }
  1932. return returnValue;
  1933. }
  1934. /**
  1935. * Creates a polygon from an SVGOMPolygonElement element.
  1936. *
  1937. * @param polyElem the poly elem
  1938. * @param noFill the no fill
  1939. * @param windingRule the winding rule
  1940. *
  1941. * @return the live polygon component
  1942. */
  1943. private AbstractShape getLivePolygonComponent(SVGOMPolygonElement polyElem, boolean noFill, int windingRule){
  1944. AbstractShape returnComponent = null;
  1945. //Create Vertex array from points
  1946. SVGPointList pointList = polyElem.getPoints();
  1947. Vertex[] vertices = new Vertex[pointList.getNumberOfItems()];
  1948. for (int i = 0; i < pointList.getNumberOfItems(); i++) {
  1949. SVGPoint p = pointList.getItem(i);
  1950. vertices[i] = new Vertex(p.getX(), p.getY(),0);
  1951. }
  1952. //If polygon isnt closed, close it with the first vertex
  1953. if (!vertices[0].equalsVector(vertices[vertices.length-1])){
  1954. Vertex[] closedVertices = new Vertex[vertices.length+1];
  1955. System.arraycopy(vertices, 0, closedVertices, 0, vertices.length);
  1956. closedVertices[closedVertices.length-1] = (Vertex)vertices[0].getCopy();
  1957. vertices = closedVertices;
  1958. }
  1959. int convexity = ConvexityUtil.classifyPolygon2(vertices.length, vertices);
  1960. switch (convexity) {
  1961. case ConvexityUtil.NotConvexDegenerate:
  1962. case ConvexityUtil.NotConvex:
  1963. //If not filled, we dont worry about non-simple polygons
  1964. if (noFill){
  1965. returnComponent = createPoly(vertices);
  1966. }else{
  1967. ArrayList<Vertex[]> contours = new ArrayList<Vertex[]>();
  1968. contours.add(vertices);
  1969. // returnComponent = createStencilPoly(vertices, contours);
  1970. returnComponent = createComplexPoly(contours, windingRule);
  1971. }
  1972. break;
  1973. case ConvexityUtil.ConvexDegenerate:
  1974. case ConvexityUtil.ConvexCW:
  1975. case ConvexityUtil.ConvexCCW:
  1976. returnComponent = createPoly(vertices);
  1977. break;
  1978. default:
  1979. break;
  1980. }
  1981. return returnComponent;
  1982. }
  1983. /**
  1984. * Creates a polygon from a SVGOMPathElement.
  1985. *
  1986. * @param pathElem the path elem
  1987. * @param noFill the no fill
  1988. * @param windingRule the winding rule
  1989. *
  1990. * @return the live path component
  1991. */
  1992. private AbstractShape getLivePathComponent(SVGOMPathElement pathElem, boolean noFill, int windingRule){
  1993. AbstractShape returnComponent = null;
  1994. CustomPathHandler pathHandler = new CustomPathHandler();
  1995. PathParser pathParser = new PathParser();
  1996. //pathHandler.setVerbose(true);
  1997. /*
  1998. SVGPathSegList pathSegList = pathElem.getPathSegList();
  1999. SVGPathSeg seg = pathSegList.getItem(pathSegList.getNumberOfItems()-1);
  2000. logger.debug(seg.getPathSegTypeAsLetter());
  2001. */
  2002. //Parse the "d" attribute
  2003. String dAttValue = pathElem.getAttribute("d");
  2004. pathParser.setPathHandler(pathHandler);
  2005. pathParser.parse(dAttValue);
  2006. //Get the Vertices of the path
  2007. Vertex[] originalPointsArray = pathHandler.getPathPointsArray();
  2008. //Get Sub-Paths
  2009. ArrayList<Vertex[]> contours = pathHandler.getContours();
  2010. // For stencil-trick-polygons!! \\
  2011. // /*
  2012. //Get path vertices points
  2013. LinkedList<Vertex> pathPoints = pathHandler.getPathPoints();
  2014. if (pathHandler.getReverseMoveToStack().size() <= 1){
  2015. //nicht adden
  2016. }else{
  2017. pathPoints.addAll(pathHandler.getReverseMoveToStack());
  2018. }
  2019. Vertex[] pathVertsStencilPrepared = pathPoints.toArray(new Vertex[pathPoints.size()]);
  2020. // */
  2021. //Check if path vertices are empty
  2022. if (originalPointsArray.length == 0){
  2023. logger.debug("Empty path vertex array -> aborting");
  2024. return null;
  2025. }
  2026. //TODO actually should calculate the real vertices from the vezier ones and then check
  2027. //for convexity..else there might be false positives
  2028. Vertex[] v;
  2029. // if (containsBeziers(v)){
  2030. // v = Tools3D.createVertexArrFromBezierArr(originalPointsArray, 11);
  2031. // }
  2032. v = originalPointsArray;
  2033. //Check for convexity
  2034. int convexity = ConvexityUtil.classifyPolygon2(v.length, v);
  2035. switch (convexity) {
  2036. case ConvexityUtil.NotConvexDegenerate:
  2037. // logger.debug("not Convex Degenerate");
  2038. case ConvexityUtil.NotConvex:
  2039. // logger.debug("not convex");
  2040. //If not filled, we can createa non stenciled polygon with no filling for better
  2041. //performance
  2042. if (noFill){
  2043. returnComponent = createPoly(originalPointsArray);
  2044. }else{
  2045. returnComponent = createComplexPoly(contours, windingRule);
  2046. }
  2047. break;
  2048. case ConvexityUtil.ConvexDegenerate:
  2049. // logger.debug("convex degenerate");
  2050. case ConvexityUtil.ConvexCW:
  2051. // logger.debug("convex clockwise");
  2052. case ConvexityUtil.ConvexCCW:
  2053. // logger.debug("convex counterclockwise");
  2054. returnComponent = createPoly(originalPointsArray);
  2055. break;
  2056. default:
  2057. break;
  2058. }
  2059. return returnComponent;
  2060. }
  2061. /**
  2062. * Creates a Stencil-Trick-Polygon.
  2063. *
  2064. * @param stencilPreparedVerts the stencil prepared verts
  2065. * @param subPaths the sub paths
  2066. *
  2067. * @return the abstract shape
  2068. */
  2069. private AbstractShape createStencilPoly(Vertex[] stencilPreparedVerts, ArrayList<Vertex[]> subPaths) {
  2070. // logger.debug("Create stencil poly");
  2071. //Blow up vertex array, that will be used for picking etc
  2072. //to at least be of size == 3 for generating normals
  2073. if (stencilPreparedVerts.length <3){
  2074. Vertex[] newVerts = new Vertex[3];
  2075. if (stencilPreparedVerts.length == 2){
  2076. newVerts[0] = stencilPreparedVerts[0];
  2077. newVerts[1] = stencilPreparedVerts[1];
  2078. newVerts[2] = (Vertex)stencilPreparedVerts[1].getCopy();
  2079. stencilPreparedVerts = newVerts;
  2080. }else if (stencilPreparedVerts.length == 1){
  2081. newVerts[0] = stencilPreparedVerts[0];
  2082. newVerts[1] = (Vertex)stencilPreparedVerts[1].getCopy();
  2083. newVerts[2] = (Vertex)stencilPreparedVerts[1].getCopy();
  2084. stencilPreparedVerts = newVerts;
  2085. }else{
  2086. //ERROR
  2087. }
  2088. }
  2089. MTStencilPolygon newShape = new MTStencilPolygon(pa, stencilPreparedVerts, subPaths);
  2090. return newShape;
  2091. }
  2092. /**
  2093. * Creates a Complex (tesselated) polygon.
  2094. *
  2095. * @param contours the contours
  2096. * @param windingRule the winding rule
  2097. *
  2098. * @return the abstract shape
  2099. */
  2100. private AbstractShape createComplexPoly(ArrayList<Vertex[]> contours, int windingRule) {
  2101. int segments = 10;
  2102. List<Vertex[]> bezierContours = ToolsGeometry.createVertexArrFromBezierVertexArrays(contours, segments);
  2103. GluTrianglulator triangulator = new GluTrianglulator(pa);
  2104. // MTTriangleMesh mesh = triangulator.toTriangleMesh(bezierContours, windingRule);
  2105. triangulator.tesselate(bezierContours, windingRule);
  2106. List<Vertex> tris = triangulator.getTriList();
  2107. Vertex[] verts = tris.toArray(new Vertex[tris.size()]);
  2108. GeometryInfo geom = new GeometryInfo(pa, verts);
  2109. // MTTriangleMesh mesh = new MTTriangleMesh(pa, geom);
  2110. MTTriangleMesh mesh = new SVGMesh(pa, geom);
  2111. //TODO put outline contourse in own class SVGMesh!
  2112. //not belonging in general mesh class
  2113. mesh.setOutlineContours(bezierContours);
  2114. triangulator.deleteTess(); //Delete triangulator (C++ object)
  2115. return mesh;
  2116. }
  2117. private class SvgPolygon extends MTPolygon{
  2118. public SvgPolygon(Vertex[] vertices, PApplet applet) {
  2119. super(applet, vertices);
  2120. }
  2121. protected IBoundingShape computeDefaultBounds() {
  2122. //Use z plane bounding rect instead default boundingsphere for svg!
  2123. return new BoundsZPlaneRectangle(this);
  2124. }
  2125. @Override
  2126. protected void setDefaultGestureActions() {
  2127. }
  2128. }
  2129. private class SVGMesh extends MTTriangleMesh{
  2130. public SVGMesh(PApplet applet, GeometryInfo geometryInfo) {
  2131. super(applet, geometryInfo, false);
  2132. }
  2133. @Override
  2134. protected IBoundingShape computeDefaultBounds() {
  2135. //Use z plane bounding rect instead default boundingsphere for svg!
  2136. return new BoundsZPlaneRectangle(this);
  2137. }
  2138. @Override
  2139. protected void setDefaultGestureActions() {
  2140. }
  2141. }
  2142. // /**
  2143. // * Creates a Complex (tesselated) polygon
  2144. // * @param contours
  2145. // * @param windingRule
  2146. // * @return
  2147. // */
  2148. // private AbstractShape createComplexPoly(ArrayList<Vertex[]> contours, int windingRule) {
  2149. //// logger.debug("Create createComplexPoly poly");
  2150. //// if (contours.get(0).length <3)
  2151. //// logger.error("<3");
  2152. //
  2153. // //Blow up first contour, that will be used for picking etc
  2154. // //to at least be of size == 3 for generating normals
  2155. // if (contours.get(0).length <3){
  2156. // if (contours.get(0).length == 2){
  2157. // Vertex[] v = new Vertex[3];
  2158. // v[0] = contours.get(0)[0];
  2159. // v[1] = contours.get(0)[1];
  2160. // v[2] = (Vertex)contours.get(0)[1].getCopy();
  2161. // Vertex[] c = contours.get(0);
  2162. // c = v;
  2163. // }else if (contours.get(0).length == 1){
  2164. // Vertex[] v = new Vertex[3];
  2165. // v[0] = contours.get(0)[0];
  2166. // v[1] = (Vertex)contours.get(0)[0].getCopy();
  2167. // v[2] = (Vertex)contours.get(0)[0].getCopy();
  2168. // Vertex[] c = contours.get(0);
  2169. // c = v;
  2170. // }else{
  2171. // //ERROR
  2172. // }
  2173. // }
  2174. //
  2175. // MTComplexPolygon newShape = new MTComplexPolygon(contours, pa);
  2176. // newShape.setWindingRule(windingRule);
  2177. // return newShape;
  2178. // }
  2179. /**
  2180. * Creates a plain, normal polygon.
  2181. *
  2182. * @param vertices the vertices
  2183. *
  2184. * @return the abstract shape
  2185. */
  2186. private AbstractShape createPoly(Vertex[] vertices) {
  2187. // logger.debug("Create poly");
  2188. Vertex[] verts = vertices;
  2189. if (ToolsGeometry.containsBezierVertices(verts))
  2190. verts = ToolsGeometry.createVertexArrFromBezierArr(verts, 13);
  2191. //Blow up vertex array, that will be used for picking etc
  2192. //to at least be of size == 3 for generating normals
  2193. if (verts.length <3){
  2194. Vertex[] newVerts = new Vertex[3];
  2195. if (verts.length == 2){
  2196. newVerts[0] = verts[0];
  2197. newVerts[1] = verts[1];
  2198. newVerts[2] = (Vertex)verts[1].getCopy();
  2199. verts = newVerts;
  2200. }else if (verts.length == 1){
  2201. newVerts[0] = verts[0];
  2202. newVerts[1] = (Vertex)verts[0].getCopy();
  2203. newVerts[2] = (Vertex)verts[0].getCopy();
  2204. verts = newVerts;
  2205. }else{
  2206. //ERROR
  2207. }
  2208. }
  2209. //For lines or polygons do this
  2210. // return new MTPolygon(verts , pa);
  2211. return new SvgPolygon(verts,pa);
  2212. }
  2213. /**
  2214. * Checks whether the given element is located inside a clip-path element.
  2215. * Used to determine whether to draw the component or not.
  2216. *
  2217. * @param element the element
  2218. *
  2219. * @return true, if checks if is under clip path
  2220. */
  2221. private boolean isUnderClipPath(Node element){
  2222. if (element.getParentNode() == null)
  2223. return false;
  2224. while (element.getParentNode() != null ) {
  2225. Node parent = element.getParentNode();
  2226. if (parent.getNodeName().equals(SVG_CLIP_PATH_TAG))
  2227. return true;
  2228. element = parent;
  2229. }
  2230. return false;
  2231. }
  2232. /**
  2233. * Gets the inherited opacity.
  2234. *
  2235. * @param svgElem the svg elem
  2236. * @param element the element
  2237. *
  2238. * @return the inherited opacity
  2239. */
  2240. private float getInheritedOpacity(SVGSVGElement svgElem, Node element){
  2241. float returnOpactiy = 1.0f;
  2242. if (element.getParentNode() == null)
  2243. return returnOpactiy;
  2244. //Get attribute of this element
  2245. try{
  2246. if (element instanceof SVGGraphicsElement){
  2247. SVGGraphicsElement gfx = (SVGGraphicsElement)element;
  2248. float opacity = ((CSSPrimitiveValue)svgElem.getComputedStyle(gfx, "").getPropertyCSSValue(("opacity"))).getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
  2249. returnOpactiy *= opacity;
  2250. logger.debug(gfx.getTagName() + ": found opacity: " + opacity);
  2251. }
  2252. }catch(Exception e){
  2253. e.printStackTrace();
  2254. }
  2255. while (element.getParentNode() != null ) {
  2256. Node parent = element.getParentNode();
  2257. try{
  2258. if (parent instanceof SVGGraphicsElement){
  2259. SVGGraphicsElement gfx = (SVGGraphicsElement)parent;
  2260. float opacity = ((CSSPrimitiveValue)svgElem.getComputedStyle(gfx, "").getPropertyCSSValue(("opacity"))).getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
  2261. returnOpactiy *= opacity;
  2262. logger.debug(gfx.getTagName() + ": found opacity: " + opacity);
  2263. }
  2264. }catch(Exception e){
  2265. e.printStackTrace();
  2266. }
  2267. element = parent;
  2268. }
  2269. return returnOpactiy;
  2270. }
  2271. }