You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DrawSimpleShape.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.sl.draw;
  16. import static org.apache.poi.sl.draw.DrawPaint.fillPaintWorkaround;
  17. import java.awt.BasicStroke;
  18. import java.awt.Color;
  19. import java.awt.Graphics2D;
  20. import java.awt.Paint;
  21. import java.awt.geom.AffineTransform;
  22. import java.awt.geom.Ellipse2D;
  23. import java.awt.geom.Path2D;
  24. import java.awt.geom.Rectangle2D;
  25. import java.io.InputStream;
  26. import java.util.ArrayList;
  27. import java.util.Collection;
  28. import java.util.HashMap;
  29. import java.util.List;
  30. import java.util.Map;
  31. import javax.xml.bind.JAXBContext;
  32. import javax.xml.bind.JAXBElement;
  33. import javax.xml.bind.Unmarshaller;
  34. import javax.xml.stream.EventFilter;
  35. import javax.xml.stream.XMLEventReader;
  36. import javax.xml.stream.XMLInputFactory;
  37. import javax.xml.stream.events.StartElement;
  38. import javax.xml.stream.events.XMLEvent;
  39. import org.apache.poi.sl.draw.binding.CTCustomGeometry2D;
  40. import org.apache.poi.sl.draw.geom.Context;
  41. import org.apache.poi.sl.draw.geom.CustomGeometry;
  42. import org.apache.poi.sl.draw.geom.Outline;
  43. import org.apache.poi.sl.draw.geom.Path;
  44. import org.apache.poi.sl.usermodel.LineDecoration;
  45. import org.apache.poi.sl.usermodel.LineDecoration.DecorationShape;
  46. import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize;
  47. import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
  48. import org.apache.poi.sl.usermodel.Shadow;
  49. import org.apache.poi.sl.usermodel.SimpleShape;
  50. import org.apache.poi.util.IOUtils;
  51. import org.apache.poi.util.StaxHelper;
  52. import org.apache.poi.util.Units;
  53. public class DrawSimpleShape extends DrawShape {
  54. private static final double DECO_SIZE_POW = 1.5d;
  55. public DrawSimpleShape(SimpleShape<?,?> shape) {
  56. super(shape);
  57. }
  58. @Override
  59. public void draw(Graphics2D graphics) {
  60. if (getAnchor(graphics, getShape()) == null) {
  61. return;
  62. }
  63. DrawPaint drawPaint = DrawFactory.getInstance(graphics).getPaint(getShape());
  64. Paint fill = drawPaint.getPaint(graphics, getShape().getFillStyle().getPaint());
  65. Paint line = drawPaint.getPaint(graphics, getShape().getStrokeStyle().getPaint());
  66. BasicStroke stroke = getStroke(); // the stroke applies both to the shadow and the shape
  67. graphics.setStroke(stroke);
  68. Collection<Outline> elems = computeOutlines(graphics);
  69. // first paint the shadow
  70. drawShadow(graphics, elems, fill, line);
  71. // then fill the shape interior
  72. if (fill != null) {
  73. for (Outline o : elems) {
  74. if (o.getPath().isFilled()){
  75. Paint fillMod = drawPaint.getPaint(graphics, getShape().getFillStyle().getPaint(), o.getPath().getFill());
  76. if (fillMod != null) {
  77. graphics.setPaint(fillMod);
  78. java.awt.Shape s = o.getOutline();
  79. graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
  80. fillPaintWorkaround(graphics, s);
  81. }
  82. }
  83. }
  84. }
  85. // then draw any content within this shape (text, image, etc.)
  86. drawContent(graphics);
  87. // then stroke the shape outline
  88. if(line != null) {
  89. graphics.setPaint(line);
  90. graphics.setStroke(stroke);
  91. for(Outline o : elems){
  92. if(o.getPath().isStroked()){
  93. java.awt.Shape s = o.getOutline();
  94. graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
  95. graphics.draw(s);
  96. }
  97. }
  98. }
  99. // draw line decorations
  100. drawDecoration(graphics, line, stroke);
  101. }
  102. protected void drawDecoration(Graphics2D graphics, Paint line, BasicStroke stroke) {
  103. if(line == null) {
  104. return;
  105. }
  106. graphics.setPaint(line);
  107. List<Outline> lst = new ArrayList<>();
  108. LineDecoration deco = getShape().getLineDecoration();
  109. Outline head = getHeadDecoration(graphics, deco, stroke);
  110. if (head != null) {
  111. lst.add(head);
  112. }
  113. Outline tail = getTailDecoration(graphics, deco, stroke);
  114. if (tail != null) {
  115. lst.add(tail);
  116. }
  117. for(Outline o : lst){
  118. java.awt.Shape s = o.getOutline();
  119. Path p = o.getPath();
  120. graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
  121. if(p.isFilled()) {
  122. graphics.fill(s);
  123. }
  124. if(p.isStroked()) {
  125. graphics.draw(s);
  126. }
  127. }
  128. }
  129. protected Outline getTailDecoration(Graphics2D graphics, LineDecoration deco, BasicStroke stroke) {
  130. if (deco == null || stroke == null) {
  131. return null;
  132. }
  133. DecorationSize tailLength = deco.getTailLength();
  134. if (tailLength == null) {
  135. tailLength = DecorationSize.MEDIUM;
  136. }
  137. DecorationSize tailWidth = deco.getTailWidth();
  138. if (tailWidth == null) {
  139. tailWidth = DecorationSize.MEDIUM;
  140. }
  141. double lineWidth = Math.max(2.5, stroke.getLineWidth());
  142. Rectangle2D anchor = getAnchor(graphics, getShape());
  143. double x2 = anchor.getX() + anchor.getWidth(),
  144. y2 = anchor.getY() + anchor.getHeight();
  145. double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());
  146. AffineTransform at = new AffineTransform();
  147. java.awt.Shape tailShape = null;
  148. Path p = null;
  149. Rectangle2D bounds;
  150. final double scaleY = Math.pow(DECO_SIZE_POW, tailWidth.ordinal()+1.);
  151. final double scaleX = Math.pow(DECO_SIZE_POW, tailLength.ordinal()+1.);
  152. DecorationShape tailShapeEnum = deco.getTailShape();
  153. if (tailShapeEnum == null) {
  154. return null;
  155. }
  156. switch (tailShapeEnum) {
  157. case OVAL:
  158. p = new Path();
  159. tailShape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
  160. bounds = tailShape.getBounds2D();
  161. at.translate(x2 - bounds.getWidth() / 2, y2 - bounds.getHeight() / 2);
  162. at.rotate(alpha, bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2);
  163. break;
  164. case STEALTH:
  165. case ARROW:
  166. p = new Path(false, true);
  167. Path2D.Double arrow = new Path2D.Double();
  168. arrow.moveTo((-lineWidth * scaleX), (-lineWidth * scaleY / 2));
  169. arrow.lineTo(0, 0);
  170. arrow.lineTo((-lineWidth * scaleX), (lineWidth * scaleY / 2));
  171. tailShape = arrow;
  172. at.translate(x2, y2);
  173. at.rotate(alpha);
  174. break;
  175. case TRIANGLE:
  176. p = new Path();
  177. Path2D.Double triangle = new Path2D.Double();
  178. triangle.moveTo((-lineWidth * scaleX), (-lineWidth * scaleY / 2));
  179. triangle.lineTo(0, 0);
  180. triangle.lineTo((-lineWidth * scaleX), (lineWidth * scaleY / 2));
  181. triangle.closePath();
  182. tailShape = triangle;
  183. at.translate(x2, y2);
  184. at.rotate(alpha);
  185. break;
  186. default:
  187. break;
  188. }
  189. if (tailShape != null) {
  190. tailShape = at.createTransformedShape(tailShape);
  191. }
  192. return tailShape == null ? null : new Outline(tailShape, p);
  193. }
  194. protected Outline getHeadDecoration(Graphics2D graphics, LineDecoration deco, BasicStroke stroke) {
  195. if (deco == null || stroke == null) {
  196. return null;
  197. }
  198. DecorationSize headLength = deco.getHeadLength();
  199. if (headLength == null) {
  200. headLength = DecorationSize.MEDIUM;
  201. }
  202. DecorationSize headWidth = deco.getHeadWidth();
  203. if (headWidth == null) {
  204. headWidth = DecorationSize.MEDIUM;
  205. }
  206. double lineWidth = Math.max(2.5, stroke.getLineWidth());
  207. Rectangle2D anchor = getAnchor(graphics, getShape());
  208. double x1 = anchor.getX(), y1 = anchor.getY();
  209. double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());
  210. AffineTransform at = new AffineTransform();
  211. java.awt.Shape headShape = null;
  212. Path p = null;
  213. Rectangle2D bounds;
  214. final double scaleY = Math.pow(DECO_SIZE_POW, headWidth.ordinal()+1.);
  215. final double scaleX = Math.pow(DECO_SIZE_POW, headLength.ordinal()+1.);
  216. DecorationShape headShapeEnum = deco.getHeadShape();
  217. if (headShapeEnum == null) {
  218. return null;
  219. }
  220. switch (headShapeEnum) {
  221. case OVAL:
  222. p = new Path();
  223. headShape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
  224. bounds = headShape.getBounds2D();
  225. at.translate(x1 - bounds.getWidth() / 2, y1 - bounds.getHeight() / 2);
  226. at.rotate(alpha, bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2);
  227. break;
  228. case STEALTH:
  229. case ARROW:
  230. p = new Path(false, true);
  231. Path2D.Double arrow = new Path2D.Double();
  232. arrow.moveTo((lineWidth * scaleX), (-lineWidth * scaleY / 2));
  233. arrow.lineTo(0, 0);
  234. arrow.lineTo((lineWidth * scaleX), (lineWidth * scaleY / 2));
  235. headShape = arrow;
  236. at.translate(x1, y1);
  237. at.rotate(alpha);
  238. break;
  239. case TRIANGLE:
  240. p = new Path();
  241. Path2D.Double triangle = new Path2D.Double();
  242. triangle.moveTo((lineWidth * scaleX), (-lineWidth * scaleY / 2));
  243. triangle.lineTo(0, 0);
  244. triangle.lineTo((lineWidth * scaleX), (lineWidth * scaleY / 2));
  245. triangle.closePath();
  246. headShape = triangle;
  247. at.translate(x1, y1);
  248. at.rotate(alpha);
  249. break;
  250. default:
  251. break;
  252. }
  253. if (headShape != null) {
  254. headShape = at.createTransformedShape(headShape);
  255. }
  256. return headShape == null ? null : new Outline(headShape, p);
  257. }
  258. public BasicStroke getStroke() {
  259. return getStroke(getShape().getStrokeStyle());
  260. }
  261. protected void drawShadow(
  262. Graphics2D graphics
  263. , Collection<Outline> outlines
  264. , Paint fill
  265. , Paint line
  266. ) {
  267. Shadow<?,?> shadow = getShape().getShadow();
  268. if (shadow == null || (fill == null && line == null)) {
  269. return;
  270. }
  271. SolidPaint shadowPaint = shadow.getFillStyle();
  272. Color shadowColor = DrawPaint.applyColorTransform(shadowPaint.getSolidColor());
  273. double shapeRotation = getShape().getRotation();
  274. if (getShape().getFlipVertical()) {
  275. shapeRotation += 180;
  276. }
  277. double angle = shadow.getAngle() - shapeRotation;
  278. double dist = shadow.getDistance();
  279. double dx = dist * Math.cos(Math.toRadians(angle));
  280. double dy = dist * Math.sin(Math.toRadians(angle));
  281. graphics.translate(dx, dy);
  282. for (Outline o : outlines) {
  283. java.awt.Shape s = o.getOutline();
  284. Path p = o.getPath();
  285. graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
  286. graphics.setPaint(shadowColor);
  287. if (fill != null && p.isFilled()) {
  288. fillPaintWorkaround(graphics, s);
  289. } else if (line != null && p.isStroked()) {
  290. graphics.draw(s);
  291. }
  292. }
  293. graphics.translate(-dx, -dy);
  294. }
  295. protected Collection<Outline> computeOutlines(Graphics2D graphics) {
  296. final SimpleShape<?,?> sh = getShape();
  297. List<Outline> lst = new ArrayList<>();
  298. CustomGeometry geom = sh.getGeometry();
  299. if(geom == null) {
  300. return lst;
  301. }
  302. Rectangle2D anchor = getAnchor(graphics, sh);
  303. if(anchor == null) {
  304. return lst;
  305. }
  306. for (Path p : geom) {
  307. double w = p.getW(), h = p.getH(), scaleX, scaleY;
  308. if (w == -1) {
  309. w = Units.toEMU(anchor.getWidth());
  310. scaleX = Units.toPoints(1);
  311. } else if (anchor.getWidth() == 0) {
  312. scaleX = 1;
  313. } else {
  314. scaleX = anchor.getWidth() / w;
  315. }
  316. if (h == -1) {
  317. h = Units.toEMU(anchor.getHeight());
  318. scaleY = Units.toPoints(1);
  319. } else if (anchor.getHeight() == 0) {
  320. scaleY = 1;
  321. } else {
  322. scaleY = anchor.getHeight() / h;
  323. }
  324. // the guides in the shape definitions are all defined relative to each other,
  325. // so we build the path starting from (0,0).
  326. final Rectangle2D pathAnchor = new Rectangle2D.Double(0,0,w,h);
  327. Context ctx = new Context(geom, pathAnchor, sh);
  328. java.awt.Shape gp = p.getPath(ctx);
  329. // translate the result to the canvas coordinates in points
  330. AffineTransform at = new AffineTransform();
  331. at.translate(anchor.getX(), anchor.getY());
  332. at.scale(scaleX, scaleY);
  333. java.awt.Shape canvasShape = at.createTransformedShape(gp);
  334. lst.add(new Outline(canvasShape, p));
  335. }
  336. return lst;
  337. }
  338. @Override
  339. protected SimpleShape<?,?> getShape() {
  340. return (SimpleShape<?,?>)shape;
  341. }
  342. }