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.

HwmfDraw.java 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  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.hwmf.record;
  16. import java.awt.Shape;
  17. import java.awt.geom.Arc2D;
  18. import java.awt.geom.Area;
  19. import java.awt.geom.Dimension2D;
  20. import java.awt.geom.Ellipse2D;
  21. import java.awt.geom.Line2D;
  22. import java.awt.geom.Path2D;
  23. import java.awt.geom.PathIterator;
  24. import java.awt.geom.Point2D;
  25. import java.awt.geom.Rectangle2D;
  26. import java.awt.geom.RoundRectangle2D;
  27. import java.io.IOException;
  28. import java.util.ArrayList;
  29. import java.util.List;
  30. import org.apache.poi.hwmf.draw.HwmfGraphics;
  31. import org.apache.poi.hwmf.draw.HwmfGraphics.FillDrawStyle;
  32. import org.apache.poi.util.Internal;
  33. import org.apache.poi.util.LittleEndianConsts;
  34. import org.apache.poi.util.LittleEndianInputStream;
  35. public class HwmfDraw {
  36. /**
  37. * The META_MOVETO record sets the output position in the playback device context to a specified
  38. * point.
  39. */
  40. public static class WmfMoveTo implements HwmfRecord {
  41. protected final Point2D point = new Point2D.Double();
  42. @Override
  43. public HwmfRecordType getWmfRecordType() {
  44. return HwmfRecordType.moveTo;
  45. }
  46. @Override
  47. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  48. return readPointS(leis, point);
  49. }
  50. @Override
  51. public void draw(HwmfGraphics ctx) {
  52. ctx.getProperties().setLocation(point);
  53. }
  54. @Override
  55. public String toString() {
  56. return pointToString(point);
  57. }
  58. }
  59. /**
  60. * The META_LINETO record draws a line from the drawing position that is defined in the playback
  61. * device context up to, but not including, the specified point.
  62. */
  63. public static class WmfLineTo implements HwmfRecord {
  64. protected final Point2D point = new Point2D.Double();
  65. @Override
  66. public HwmfRecordType getWmfRecordType() {
  67. return HwmfRecordType.lineTo;
  68. }
  69. @Override
  70. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  71. return readPointS(leis, point);
  72. }
  73. @Override
  74. public void draw(HwmfGraphics ctx) {
  75. Point2D start = ctx.getProperties().getLocation();
  76. Line2D line = new Line2D.Double(start, point);
  77. ctx.draw(line);
  78. ctx.getProperties().setLocation(point);
  79. }
  80. @Override
  81. public String toString() {
  82. return pointToString(point);
  83. }
  84. }
  85. /**
  86. * The META_POLYGON record paints a polygon consisting of two or more vertices connected by
  87. * straight lines. The polygon is outlined by using the pen and filled by using the brush and polygon fill
  88. * mode that are defined in the playback device context.
  89. */
  90. public static class WmfPolygon implements HwmfRecord {
  91. protected Path2D poly;
  92. @Override
  93. public HwmfRecordType getWmfRecordType() {
  94. return HwmfRecordType.polygon;
  95. }
  96. @Override
  97. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  98. /**
  99. * A 16-bit signed integer that defines the number of points in the array.
  100. */
  101. int numberofPoints = leis.readShort();
  102. poly = new Path2D.Double(Path2D.WIND_EVEN_ODD, numberofPoints);
  103. for (int i=0; i<numberofPoints; i++) {
  104. // A 16-bit signed integer that defines the horizontal (x) coordinate of the point.
  105. int x = leis.readShort();
  106. // A 16-bit signed integer that defines the vertical (y) coordinate of the point.
  107. int y = leis.readShort();
  108. if (i==0) {
  109. poly.moveTo(x, y);
  110. } else {
  111. poly.lineTo(x, y);
  112. }
  113. }
  114. return LittleEndianConsts.SHORT_SIZE+numberofPoints*LittleEndianConsts.INT_SIZE;
  115. }
  116. @Override
  117. public void draw(HwmfGraphics ctx) {
  118. Path2D p = (Path2D)poly.clone();
  119. // don't close the path
  120. p.setWindingRule(ctx.getProperties().getWindingRule());
  121. switch (getFillDrawStyle()) {
  122. case FILL:
  123. ctx.fill(p);
  124. break;
  125. case DRAW:
  126. ctx.draw(p);
  127. break;
  128. case FILL_DRAW:
  129. ctx.fill(p);
  130. ctx.draw(p);
  131. break;
  132. }
  133. }
  134. @Override
  135. public String toString() {
  136. return "{ poly: "+polyToString(poly)+" }";
  137. }
  138. /**
  139. * @return true, if the shape should be filled
  140. */
  141. protected FillDrawStyle getFillDrawStyle() {
  142. return FillDrawStyle.FILL;
  143. }
  144. }
  145. /**
  146. * The META_POLYLINE record draws a series of line segments by connecting the points in the
  147. * specified array.
  148. */
  149. public static class WmfPolyline extends WmfPolygon {
  150. @Override
  151. public HwmfRecordType getWmfRecordType() {
  152. return HwmfRecordType.polyline;
  153. }
  154. @Override
  155. protected FillDrawStyle getFillDrawStyle() {
  156. return FillDrawStyle.DRAW;
  157. }
  158. }
  159. /**
  160. * The META_ELLIPSE record draws an ellipse. The center of the ellipse is the center of the specified
  161. * bounding rectangle. The ellipse is outlined by using the pen and is filled by using the brush; these
  162. * are defined in the playback device context.
  163. */
  164. public static class WmfEllipse implements HwmfRecord {
  165. protected final Rectangle2D bounds = new Rectangle2D.Double();
  166. @Override
  167. public HwmfRecordType getWmfRecordType() {
  168. return HwmfRecordType.ellipse;
  169. }
  170. @Override
  171. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  172. return readBounds(leis, bounds);
  173. }
  174. @Override
  175. public void draw(HwmfGraphics ctx) {
  176. ctx.fill(getShape());
  177. }
  178. protected Ellipse2D getShape() {
  179. return new Ellipse2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight());
  180. }
  181. @Override
  182. public String toString() {
  183. return boundsToString(bounds);
  184. }
  185. }
  186. /**
  187. * The META_FRAMEREGION record draws a border around a specified region using a specified brush.
  188. */
  189. public static class WmfFrameRegion implements HwmfRecord {
  190. /**
  191. * A 16-bit unsigned integer used to index into the WMF Object Table to get
  192. * the region to be framed.
  193. */
  194. protected int regionIndex;
  195. /**
  196. * A 16-bit unsigned integer used to index into the WMF Object Table to get the
  197. * Brush to use for filling the region.
  198. */
  199. protected int brushIndex;
  200. /**
  201. * A 16-bit signed integer that defines the height, in logical units, of the
  202. * region frame.
  203. */
  204. protected int height;
  205. /**
  206. * A 16-bit signed integer that defines the width, in logical units, of the
  207. * region frame.
  208. */
  209. protected int width;
  210. @Override
  211. public HwmfRecordType getWmfRecordType() {
  212. return HwmfRecordType.frameRegion;
  213. }
  214. @Override
  215. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  216. regionIndex = leis.readUShort();
  217. brushIndex = leis.readUShort();
  218. height = leis.readShort();
  219. width = leis.readShort();
  220. return 4*LittleEndianConsts.SHORT_SIZE;
  221. }
  222. @Override
  223. public void draw(HwmfGraphics ctx) {
  224. ctx.applyObjectTableEntry(brushIndex);
  225. ctx.applyObjectTableEntry(regionIndex);
  226. Rectangle2D inner = ctx.getProperties().getRegion().getBounds();
  227. double x = inner.getX()-width;
  228. double y = inner.getY()-height;
  229. double w = inner.getWidth()+2*width;
  230. double h = inner.getHeight()+2*height;
  231. Rectangle2D outer = new Rectangle2D.Double(x,y,w,h);
  232. Area frame = new Area(outer);
  233. frame.subtract(new Area(inner));
  234. ctx.fill(frame);
  235. }
  236. }
  237. /**
  238. * The META_POLYPOLYGON record paints a series of closed polygons. Each polygon is outlined by
  239. * using the pen and filled by using the brush and polygon fill mode; these are defined in the playback
  240. * device context. The polygons drawn by this function can overlap.
  241. */
  242. public static class WmfPolyPolygon implements HwmfRecord {
  243. protected final List<Path2D> polyList = new ArrayList<>();
  244. @Override
  245. public HwmfRecordType getWmfRecordType() {
  246. return HwmfRecordType.polyPolygon;
  247. }
  248. @Override
  249. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  250. // see http://secunia.com/gfx/pdf/SA31675_BA.pdf ;)
  251. /**
  252. * A 16-bit unsigned integer that defines the number of polygons in the object.
  253. */
  254. int numberOfPolygons = leis.readUShort();
  255. /**
  256. * A NumberOfPolygons array of 16-bit unsigned integers that define the number of
  257. * points for each polygon in the object.
  258. */
  259. int[] pointsPerPolygon = new int[numberOfPolygons];
  260. int size = LittleEndianConsts.SHORT_SIZE;
  261. for (int i=0; i<numberOfPolygons; i++) {
  262. pointsPerPolygon[i] = leis.readUShort();
  263. size += LittleEndianConsts.SHORT_SIZE;
  264. }
  265. for (int nPoints : pointsPerPolygon) {
  266. /**
  267. * An array of 16-bit signed integers that define the coordinates of the polygons.
  268. * (Note: MS-WMF wrongly says unsigned integers ...)
  269. */
  270. Path2D poly = new Path2D.Double(Path2D.WIND_EVEN_ODD, nPoints);
  271. for (int i=0; i<nPoints; i++) {
  272. int x = leis.readShort();
  273. int y = leis.readShort();
  274. size += 2*LittleEndianConsts.SHORT_SIZE;
  275. if (i == 0) {
  276. poly.moveTo(x, y);
  277. } else {
  278. poly.lineTo(x, y);
  279. }
  280. }
  281. poly.closePath();
  282. polyList.add(poly);
  283. }
  284. return size;
  285. }
  286. @Override
  287. public void draw(HwmfGraphics ctx) {
  288. Shape shape = getShape(ctx);
  289. if (shape == null) {
  290. return;
  291. }
  292. switch (getFillDrawStyle()) {
  293. case DRAW:
  294. ctx.draw(shape);
  295. break;
  296. case FILL:
  297. ctx.fill(shape);
  298. break;
  299. case FILL_DRAW:
  300. ctx.fill(shape);
  301. ctx.draw(shape);
  302. break;
  303. }
  304. }
  305. protected FillDrawStyle getFillDrawStyle() {
  306. // Each polygon SHOULD be outlined using the current pen, and filled using the current brush and
  307. // polygon fill mode that are defined in the playback device context. The polygons defined by this
  308. // record can overlap.
  309. return FillDrawStyle.FILL_DRAW;
  310. }
  311. /**
  312. * @return true, if a polyline should be closed, i.e. is a polygon
  313. */
  314. protected boolean isClosed() {
  315. return true;
  316. }
  317. protected Shape getShape(HwmfGraphics ctx) {
  318. int windingRule = ctx.getProperties().getWindingRule();
  319. if (isClosed()) {
  320. Area area = null;
  321. for (Path2D poly : polyList) {
  322. Path2D p = (Path2D)poly.clone();
  323. p.setWindingRule(windingRule);
  324. Area newArea = new Area(p);
  325. if (area == null) {
  326. area = newArea;
  327. } else {
  328. area.exclusiveOr(newArea);
  329. }
  330. }
  331. return area;
  332. } else {
  333. Path2D path = new Path2D.Double();
  334. path.setWindingRule(windingRule);
  335. for (Path2D poly : polyList) {
  336. path.append(poly, false);
  337. }
  338. return path;
  339. }
  340. }
  341. @Override
  342. public String toString() {
  343. final StringBuilder sb = new StringBuilder();
  344. sb.append("{ polyList: [");
  345. boolean isFirst = true;
  346. for (Path2D p : polyList) {
  347. if (!isFirst) {
  348. sb.append(",");
  349. }
  350. isFirst = false;
  351. sb.append("{ points: ");
  352. sb.append(polyToString(p));
  353. sb.append(" }");
  354. }
  355. sb.append(" }");
  356. return sb.toString();
  357. }
  358. }
  359. /**
  360. * The META_RECTANGLE record paints a rectangle. The rectangle is outlined by using the pen and
  361. * filled by using the brush that are defined in the playback device context.
  362. */
  363. public static class WmfRectangle implements HwmfRecord {
  364. protected final Rectangle2D bounds = new Rectangle2D.Double();
  365. @Override
  366. public HwmfRecordType getWmfRecordType() {
  367. return HwmfRecordType.frameRegion;
  368. }
  369. @Override
  370. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  371. return readBounds(leis, bounds);
  372. }
  373. @Override
  374. public void draw(HwmfGraphics ctx) {
  375. ctx.fill(bounds);
  376. }
  377. @Override
  378. public String toString() {
  379. return boundsToString(bounds);
  380. }
  381. }
  382. /**
  383. * The META_SETPIXEL record sets the pixel at the specified coordinates to the specified color.
  384. */
  385. public static class WmfSetPixel implements HwmfRecord {
  386. /**
  387. * A ColorRef Object that defines the color value.
  388. */
  389. protected final HwmfColorRef colorRef = new HwmfColorRef();
  390. protected final Point2D point = new Point2D.Double();
  391. @Override
  392. public HwmfRecordType getWmfRecordType() {
  393. return HwmfRecordType.setPixel;
  394. }
  395. @Override
  396. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  397. int size = colorRef.init(leis);
  398. return size+ readPointS(leis, point);
  399. }
  400. @Override
  401. public void draw(HwmfGraphics ctx) {
  402. Shape s = new Rectangle2D.Double(point.getX(), point.getY(), 1, 1);
  403. ctx.fill(s);
  404. }
  405. }
  406. /**
  407. * The META_ROUNDRECT record paints a rectangle with rounded corners. The rectangle is outlined
  408. * using the pen and filled using the brush, as defined in the playback device context.
  409. */
  410. public static class WmfRoundRect implements HwmfRecord {
  411. /**
  412. * A 16-bit signed integer that defines the height, in logical coordinates, of the
  413. * ellipse used to draw the rounded corners.
  414. */
  415. protected int height;
  416. /**
  417. * A 16-bit signed integer that defines the width, in logical coordinates, of the
  418. * ellipse used to draw the rounded corners.
  419. */
  420. protected int width;
  421. protected final Rectangle2D bounds = new Rectangle2D.Double();
  422. @Override
  423. public HwmfRecordType getWmfRecordType() {
  424. return HwmfRecordType.roundRect;
  425. }
  426. @Override
  427. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  428. height = leis.readShort();
  429. width = leis.readShort();
  430. return 2*LittleEndianConsts.SHORT_SIZE+readBounds(leis, bounds);
  431. }
  432. @Override
  433. public void draw(HwmfGraphics ctx) {
  434. ctx.fill(getShape());
  435. }
  436. protected RoundRectangle2D getShape() {
  437. return new RoundRectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), width, height);
  438. }
  439. }
  440. /**
  441. * The META_ARC record draws an elliptical arc.
  442. */
  443. public static class WmfArc implements HwmfRecord {
  444. /** starting point of the arc */
  445. protected final Point2D startPoint = new Point2D.Double();
  446. /** ending point of the arc */
  447. protected final Point2D endPoint = new Point2D.Double();
  448. /** the bounding rectangle */
  449. protected final Rectangle2D bounds = new Rectangle2D.Double();
  450. @Override
  451. public HwmfRecordType getWmfRecordType() {
  452. return HwmfRecordType.arc;
  453. }
  454. @Override
  455. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  456. readPointS(leis, endPoint);
  457. readPointS(leis, startPoint);
  458. readBounds(leis, bounds);
  459. return 8*LittleEndianConsts.SHORT_SIZE;
  460. }
  461. @Override
  462. public void draw(HwmfGraphics ctx) {
  463. Shape s = getShape();
  464. switch (getFillDrawStyle()) {
  465. case FILL:
  466. ctx.fill(s);
  467. break;
  468. case DRAW:
  469. ctx.draw(s);
  470. break;
  471. case FILL_DRAW:
  472. ctx.fill(s);
  473. ctx.draw(s);
  474. break;
  475. }
  476. }
  477. protected FillDrawStyle getFillDrawStyle() {
  478. switch (getWmfRecordType()) {
  479. default:
  480. case arc:
  481. return FillDrawStyle.DRAW;
  482. case chord:
  483. case pie:
  484. return FillDrawStyle.FILL_DRAW;
  485. }
  486. }
  487. protected Arc2D getShape() {
  488. double startAngle = Math.toDegrees(Math.atan2(-(startPoint.getY() - bounds.getCenterY()), startPoint.getX() - bounds.getCenterX()));
  489. double endAngle = Math.toDegrees(Math.atan2(-(endPoint.getY() - bounds.getCenterY()), endPoint.getX() - bounds.getCenterX()));
  490. double arcAngle = (endAngle - startAngle) + (endAngle - startAngle > 0 ? 0 : 360);
  491. if (startAngle < 0) {
  492. startAngle += 360;
  493. }
  494. boolean fillShape;
  495. int arcClosure;
  496. switch (getWmfRecordType()) {
  497. default:
  498. case arc:
  499. arcClosure = Arc2D.OPEN;
  500. break;
  501. case chord:
  502. arcClosure = Arc2D.CHORD;
  503. break;
  504. case pie:
  505. arcClosure = Arc2D.PIE;
  506. break;
  507. }
  508. return new Arc2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), startAngle, arcAngle, arcClosure);
  509. }
  510. @Override
  511. public String toString() {
  512. Arc2D arc = getShape();
  513. return
  514. "{ startPoint: "+pointToString(startPoint)+
  515. ", endPoint: "+pointToString(endPoint)+
  516. ", startAngle: "+arc.getAngleStart()+
  517. ", extentAngle: "+arc.getAngleExtent()+
  518. ", bounds: "+boundsToString(bounds)+
  519. " }";
  520. }
  521. }
  522. /**
  523. * The META_PIE record draws a pie-shaped wedge bounded by the intersection of an ellipse and two
  524. * radials. The pie is outlined by using the pen and filled by using the brush that are defined in the
  525. * playback device context.
  526. */
  527. public static class WmfPie extends WmfArc {
  528. @Override
  529. public HwmfRecordType getWmfRecordType() {
  530. return HwmfRecordType.pie;
  531. }
  532. }
  533. /**
  534. * The META_CHORD record draws a chord, which is defined by a region bounded by the intersection of
  535. * an ellipse with a line segment. The chord is outlined using the pen and filled using the brush
  536. * that are defined in the playback device context.
  537. */
  538. public static class WmfChord extends WmfArc {
  539. @Override
  540. public HwmfRecordType getWmfRecordType() {
  541. return HwmfRecordType.chord;
  542. }
  543. }
  544. /**
  545. * The META_SELECTOBJECT record specifies a graphics object for the playback device context. The
  546. * new object replaces the previous object of the same type, unless if the previous object is a palette
  547. * object. If the previous object is a palette object, then the META_SELECTPALETTE record must be
  548. * used instead of the META_SELECTOBJECT record, as the META_SELECTOBJECT record does not
  549. * support replacing the palette object type.
  550. */
  551. public static class WmfSelectObject implements HwmfRecord {
  552. /**
  553. * A 16-bit unsigned integer used to index into the WMF Object Table to
  554. * get the object to be selected.
  555. */
  556. protected int objectIndex;
  557. @Override
  558. public HwmfRecordType getWmfRecordType() {
  559. return HwmfRecordType.selectObject;
  560. }
  561. @Override
  562. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  563. objectIndex = leis.readUShort();
  564. return LittleEndianConsts.SHORT_SIZE;
  565. }
  566. @Override
  567. public void draw(HwmfGraphics ctx) {
  568. ctx.applyObjectTableEntry(objectIndex);
  569. }
  570. @Override
  571. public String toString() {
  572. return "{ index: "+objectIndex +" }";
  573. }
  574. }
  575. static int readBounds(LittleEndianInputStream leis, Rectangle2D bounds) {
  576. /**
  577. * The 16-bit signed integers that defines the corners of the bounding rectangle.
  578. */
  579. int bottom = leis.readShort();
  580. int right = leis.readShort();
  581. int top = leis.readShort();
  582. int left = leis.readShort();
  583. int x = Math.min(left, right);
  584. int y = Math.min(top, bottom);
  585. int w = Math.abs(left - right - 1);
  586. int h = Math.abs(top - bottom - 1);
  587. bounds.setRect(x, y, w, h);
  588. return 4 * LittleEndianConsts.SHORT_SIZE;
  589. }
  590. static int readRectS(LittleEndianInputStream leis, Rectangle2D bounds) {
  591. /**
  592. * The 16-bit signed integers that defines the corners of the bounding rectangle.
  593. */
  594. int left = leis.readShort();
  595. int top = leis.readShort();
  596. int right = leis.readShort();
  597. int bottom = leis.readShort();
  598. int x = Math.min(left, right);
  599. int y = Math.min(top, bottom);
  600. int w = Math.abs(left - right - 1);
  601. int h = Math.abs(top - bottom - 1);
  602. bounds.setRect(x, y, w, h);
  603. return 4 * LittleEndianConsts.SHORT_SIZE;
  604. }
  605. static int readPointS(LittleEndianInputStream leis, Point2D point) {
  606. /** a signed integer that defines the x/y-coordinate, in logical units. */
  607. int y = leis.readShort();
  608. int x = leis.readShort();
  609. point.setLocation(x, y);
  610. return 2*LittleEndianConsts.SHORT_SIZE;
  611. }
  612. static String polyToString(Path2D poly) {
  613. StringBuilder sb = new StringBuilder();
  614. sb.append("[");
  615. final PathIterator iter = poly.getPathIterator(null);
  616. double[] pnts = new double[6];
  617. while (!iter.isDone()) {
  618. int segType = iter.currentSegment(pnts);
  619. switch (segType) {
  620. case PathIterator.SEG_MOVETO:
  621. sb.append("{ type: 'move', x: "+pnts[0]+", y: "+pnts[1]+" }, ");
  622. break;
  623. case PathIterator.SEG_LINETO:
  624. sb.append("{ type: 'lineto', x: "+pnts[0]+", y: "+pnts[1]+" }, ");
  625. break;
  626. case PathIterator.SEG_QUADTO:
  627. sb.append("{ type: 'quad', x1: "+pnts[0]+", y1: "+pnts[1]+", x2: "+pnts[2]+", y2: "+pnts[3]+" }, ");
  628. break;
  629. case PathIterator.SEG_CUBICTO:
  630. sb.append("{ type: 'cubic', x1: "+pnts[0]+", y1: "+pnts[1]+", x2: "+pnts[2]+", y2: "+pnts[3]+", x3: "+pnts[4]+", y3: "+pnts[5]+" }, ");
  631. break;
  632. case PathIterator.SEG_CLOSE:
  633. sb.append("{ type: 'close' }, ");
  634. break;
  635. }
  636. iter.next();
  637. }
  638. sb.append("]");
  639. return sb.toString();
  640. }
  641. @Internal
  642. public static String pointToString(Point2D point) {
  643. return "{ x: "+point.getX()+", y: "+point.getY()+" }";
  644. }
  645. @Internal
  646. public static String boundsToString(Rectangle2D bounds) {
  647. return "{ x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+" }";
  648. }
  649. @Internal
  650. public static String dimToString(Dimension2D dim) {
  651. return "{ w: "+dim.getWidth()+", h: "+dim.getHeight()+" }";
  652. }
  653. @Internal
  654. public static Rectangle2D normalizeBounds(Rectangle2D bounds) {
  655. return (bounds.getWidth() >= 0 && bounds.getHeight() >= 0) ? bounds
  656. : new Rectangle2D.Double(
  657. bounds.getWidth() >= 0 ? bounds.getMinX() : bounds.getMaxX(),
  658. bounds.getHeight() >= 0 ? bounds.getMinY() : bounds.getMaxY(),
  659. Math.abs(bounds.getWidth()),
  660. Math.abs(bounds.getHeight())
  661. );
  662. }
  663. }