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.

HwmfWindowing.java 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  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 static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
  17. import static org.apache.poi.hwmf.record.HwmfDraw.dimToString;
  18. import static org.apache.poi.hwmf.record.HwmfDraw.normalizeBounds;
  19. import static org.apache.poi.hwmf.record.HwmfDraw.pointToString;
  20. import static org.apache.poi.hwmf.record.HwmfDraw.readBounds;
  21. import static org.apache.poi.hwmf.record.HwmfDraw.readPointS;
  22. import java.awt.Shape;
  23. import java.awt.geom.Area;
  24. import java.awt.geom.Dimension2D;
  25. import java.awt.geom.Point2D;
  26. import java.awt.geom.Rectangle2D;
  27. import java.io.IOException;
  28. import org.apache.poi.hwmf.draw.HwmfDrawProperties;
  29. import org.apache.poi.hwmf.draw.HwmfGraphics;
  30. import org.apache.poi.util.Dimension2DDouble;
  31. import org.apache.poi.util.LittleEndianConsts;
  32. import org.apache.poi.util.LittleEndianInputStream;
  33. public class HwmfWindowing {
  34. /**
  35. * The META_SETVIEWPORTORG record defines the viewport origin in the playback device context.
  36. */
  37. public static class WmfSetViewportOrg implements HwmfRecord {
  38. protected final Point2D origin = new Point2D.Double();
  39. @Override
  40. public HwmfRecordType getWmfRecordType() {
  41. return HwmfRecordType.setViewportOrg;
  42. }
  43. @Override
  44. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  45. return readPointS(leis, origin);
  46. }
  47. @Override
  48. public void draw(HwmfGraphics ctx) {
  49. final HwmfDrawProperties prop = ctx.getProperties();
  50. Rectangle2D old = prop.getViewport();
  51. double oldX = (old == null ? 0 : old.getX());
  52. double oldY = (old == null ? 0 : old.getY());
  53. if (oldX != origin.getX() || oldY != origin.getY()) {
  54. prop.setViewportOrg(origin.getX(), origin.getY());
  55. ctx.updateWindowMapMode();
  56. }
  57. }
  58. @Override
  59. public String toString() {
  60. return pointToString(origin);
  61. }
  62. }
  63. /**
  64. * The META_SETVIEWPORTEXT record sets the horizontal and vertical extents
  65. * of the viewport in the playback device context.
  66. */
  67. public static class WmfSetViewportExt implements HwmfRecord {
  68. protected final Dimension2D extents = new Dimension2DDouble();
  69. @Override
  70. public HwmfRecordType getWmfRecordType() {
  71. return HwmfRecordType.setViewportExt;
  72. }
  73. @Override
  74. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  75. // A signed integer that defines the vertical extent of the viewport in device units.
  76. int height = leis.readShort();
  77. // A signed integer that defines the horizontal extent of the viewport in device units.
  78. int width = leis.readShort();
  79. extents.setSize(width, height);
  80. return 2*LittleEndianConsts.SHORT_SIZE;
  81. }
  82. @Override
  83. public void draw(HwmfGraphics ctx) {
  84. final HwmfDrawProperties prop = ctx.getProperties();
  85. Rectangle2D old = prop.getViewport();
  86. double oldW = (old == null ? 0 : old.getWidth());
  87. double oldH = (old == null ? 0 : old.getHeight());
  88. if (oldW != extents.getWidth() || oldH != extents.getHeight()) {
  89. prop.setViewportExt(extents.getWidth(), extents.getHeight());
  90. ctx.updateWindowMapMode();
  91. }
  92. }
  93. @Override
  94. public String toString() {
  95. return dimToString(extents);
  96. }
  97. }
  98. /**
  99. * The META_OFFSETVIEWPORTORG record moves the viewport origin in the playback device context
  100. * by specified horizontal and vertical offsets.
  101. */
  102. public static class WmfOffsetViewportOrg implements HwmfRecord {
  103. protected final Point2D offset = new Point2D.Double();
  104. @Override
  105. public HwmfRecordType getWmfRecordType() {
  106. return HwmfRecordType.offsetViewportOrg;
  107. }
  108. @Override
  109. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  110. return readPointS(leis, offset);
  111. }
  112. @Override
  113. public void draw(HwmfGraphics ctx) {
  114. final HwmfDrawProperties prop = ctx.getProperties();
  115. Rectangle2D viewport = prop.getViewport();
  116. if (offset.getX() != 0 || offset.getY() != 0) {
  117. double x = (viewport == null) ? 0 : viewport.getX();
  118. double y = (viewport == null) ? 0 : viewport.getY();
  119. prop.setViewportOrg(x + offset.getX(), y + offset.getY());
  120. ctx.updateWindowMapMode();
  121. }
  122. }
  123. @Override
  124. public String toString() {
  125. return pointToString(offset);
  126. }
  127. }
  128. /**
  129. * The META_SETWINDOWORG record defines the output window origin in the playback device context.
  130. */
  131. public static class WmfSetWindowOrg implements HwmfRecord {
  132. protected final Point2D origin = new Point2D.Double();
  133. @Override
  134. public HwmfRecordType getWmfRecordType() {
  135. return HwmfRecordType.setWindowOrg;
  136. }
  137. @Override
  138. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  139. return readPointS(leis, origin);
  140. }
  141. @Override
  142. public void draw(HwmfGraphics ctx) {
  143. final HwmfDrawProperties prop = ctx.getProperties();
  144. final Rectangle2D old = prop.getWindow();
  145. double oldX = (old == null ? 0 : old.getX());
  146. double oldY = (old == null ? 0 : old.getY());
  147. if (oldX != getX() || oldY != getY()) {
  148. prop.setWindowOrg(getX(), getY());
  149. ctx.updateWindowMapMode();
  150. }
  151. }
  152. public double getY() {
  153. return origin.getY();
  154. }
  155. public double getX() {
  156. return origin.getX();
  157. }
  158. @Override
  159. public String toString() {
  160. return pointToString(origin);
  161. }
  162. }
  163. /**
  164. * The META_SETWINDOWEXT record defines the horizontal and vertical extents
  165. * of the output window in the playback device context.
  166. */
  167. public static class WmfSetWindowExt implements HwmfRecord {
  168. protected final Dimension2D size = new Dimension2DDouble();
  169. @Override
  170. public HwmfRecordType getWmfRecordType() {
  171. return HwmfRecordType.setWindowExt;
  172. }
  173. @Override
  174. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  175. // A signed integer that defines the vertical extent of the window in logical units.
  176. int height = leis.readShort();
  177. // A signed integer that defines the horizontal extent of the window in logical units.
  178. int width = leis.readShort();
  179. size.setSize(width, height);
  180. return 2*LittleEndianConsts.SHORT_SIZE;
  181. }
  182. @Override
  183. public void draw(HwmfGraphics ctx) {
  184. final HwmfDrawProperties prop = ctx.getProperties();
  185. Rectangle2D old = prop.getWindow();
  186. double oldW = (old == null ? 0 : old.getWidth());
  187. double oldH = (old == null ? 0 : old.getHeight());
  188. if (oldW != size.getWidth() || oldH != size.getHeight()) {
  189. prop.setWindowExt(size.getWidth(), size.getHeight());
  190. ctx.updateWindowMapMode();
  191. }
  192. }
  193. public Dimension2D getSize() {
  194. return size;
  195. }
  196. @Override
  197. public String toString() {
  198. return dimToString(size);
  199. }
  200. }
  201. /**
  202. * The META_OFFSETWINDOWORG record moves the output window origin in the
  203. * playback device context by specified horizontal and vertical offsets.
  204. */
  205. public static class WmfOffsetWindowOrg implements HwmfRecord {
  206. protected final Point2D offset = new Point2D.Double();
  207. @Override
  208. public HwmfRecordType getWmfRecordType() {
  209. return HwmfRecordType.offsetWindowOrg;
  210. }
  211. @Override
  212. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  213. return readPointS(leis, offset);
  214. }
  215. @Override
  216. public void draw(HwmfGraphics ctx) {
  217. final HwmfDrawProperties prop = ctx.getProperties();
  218. Rectangle2D old = prop.getWindow();
  219. if (offset.getX() != 0 || offset.getY() != 0) {
  220. prop.setWindowOrg(old.getX() + offset.getX(), old.getY() + offset.getY());
  221. ctx.updateWindowMapMode();
  222. }
  223. }
  224. @Override
  225. public String toString() {
  226. return pointToString(offset);
  227. }
  228. }
  229. /**
  230. * The META_OFFSETWINDOWORG record moves the output window origin in the
  231. * playback device context by specified horizontal and vertical offsets.
  232. */
  233. public static class WmfScaleWindowExt implements HwmfRecord {
  234. protected final Dimension2D scale = new Dimension2DDouble();
  235. @Override
  236. public HwmfRecordType getWmfRecordType() {
  237. return HwmfRecordType.scaleWindowExt;
  238. }
  239. @Override
  240. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  241. // A signed integer that defines the amount by which to divide the
  242. // result of multiplying the current y-extent by the value of the yNum member.
  243. double yDenom = leis.readShort();
  244. // A signed integer that defines the amount by which to multiply the
  245. // current y-extent.
  246. double yNum = leis.readShort();
  247. // A signed integer that defines the amount by which to divide the
  248. // result of multiplying the current x-extent by the value of the xNum member.
  249. double xDenom = leis.readShort();
  250. // A signed integer that defines the amount by which to multiply the
  251. // current x-extent.
  252. double xNum = leis.readShort();
  253. scale.setSize(xNum / xDenom, yNum / yDenom);
  254. return 4*LittleEndianConsts.SHORT_SIZE;
  255. }
  256. @Override
  257. public void draw(HwmfGraphics ctx) {
  258. final HwmfDrawProperties prop = ctx.getProperties();
  259. Rectangle2D old = prop.getWindow();
  260. if (scale.getWidth() != 1.0 || scale.getHeight() != 1.0) {
  261. double width = old.getWidth() * scale.getWidth();
  262. double height = old.getHeight() * scale.getHeight();
  263. ctx.getProperties().setWindowExt(width, height);
  264. ctx.updateWindowMapMode();
  265. }
  266. }
  267. @Override
  268. public String toString() {
  269. return "{ scaleX: "+scale.getWidth()+", scaleY: "+scale.getHeight()+" }";
  270. }
  271. }
  272. /**
  273. * The META_SCALEVIEWPORTEXT record scales the horizontal and vertical extents of the viewport
  274. * that is defined in the playback device context by using the ratios formed by the specified
  275. * multiplicands and divisors.
  276. */
  277. public static class WmfScaleViewportExt implements HwmfRecord {
  278. protected final Dimension2D scale = new Dimension2DDouble();
  279. @Override
  280. public HwmfRecordType getWmfRecordType() {
  281. return HwmfRecordType.scaleViewportExt;
  282. }
  283. @Override
  284. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  285. // A signed integer that defines the amount by which to divide the
  286. // result of multiplying the current y-extent by the value of the yNum member.
  287. double yDenom = leis.readShort();
  288. // A signed integer that defines the amount by which to multiply the
  289. // current y-extent.
  290. double yNum = leis.readShort();
  291. // A signed integer that defines the amount by which to divide the
  292. // result of multiplying the current x-extent by the value of the xNum member.
  293. double xDenom = leis.readShort();
  294. // A signed integer that defines the amount by which to multiply the
  295. // current x-extent.
  296. double xNum = leis.readShort();
  297. scale.setSize(xNum / xDenom, yNum / yDenom);
  298. return 4*LittleEndianConsts.SHORT_SIZE;
  299. }
  300. @Override
  301. public void draw(HwmfGraphics ctx) {
  302. final HwmfDrawProperties prop = ctx.getProperties();
  303. final Rectangle2D old = prop.getViewport() == null ? prop.getWindow() : prop.getViewport();
  304. if (scale.getWidth() != 1.0 || scale.getHeight() != 1.0) {
  305. double width = old.getWidth() * scale.getWidth();
  306. double height = old.getHeight() * scale.getHeight();
  307. prop.setViewportExt(width, height);
  308. ctx.updateWindowMapMode();
  309. }
  310. }
  311. @Override
  312. public String toString() {
  313. return "{ scaleX: "+scale.getWidth()+", scaleY: "+scale.getHeight()+" }";
  314. }
  315. }
  316. /**
  317. * The META_OFFSETCLIPRGN record moves the clipping region in the playback device context by the
  318. * specified offsets.
  319. */
  320. public static class WmfOffsetClipRgn implements HwmfRecord, HwmfObjectTableEntry {
  321. protected final Point2D offset = new Point2D.Double();
  322. @Override
  323. public HwmfRecordType getWmfRecordType() {
  324. return HwmfRecordType.offsetClipRgn;
  325. }
  326. @Override
  327. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  328. return readPointS(leis, offset);
  329. }
  330. @Override
  331. public void draw(HwmfGraphics ctx) {
  332. ctx.addObjectTableEntry(this);
  333. }
  334. @Override
  335. public void applyObject(HwmfGraphics ctx) {
  336. }
  337. @Override
  338. public String toString() {
  339. return pointToString(offset);
  340. }
  341. }
  342. /**
  343. * The META_EXCLUDECLIPRECT record sets the clipping region in the playback device context to the
  344. * existing clipping region minus the specified rectangle.
  345. */
  346. public static class WmfExcludeClipRect implements HwmfRecord, HwmfObjectTableEntry {
  347. /** a rectangle in logical units */
  348. protected final Rectangle2D bounds = new Rectangle2D.Double();
  349. @Override
  350. public HwmfRecordType getWmfRecordType() {
  351. return HwmfRecordType.excludeClipRect;
  352. }
  353. @Override
  354. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  355. return readBounds(leis, bounds);
  356. }
  357. @Override
  358. public void draw(HwmfGraphics ctx) {
  359. ctx.addObjectTableEntry(this);
  360. }
  361. @Override
  362. public void applyObject(HwmfGraphics ctx) {
  363. ctx.setClip(normalizeBounds(bounds), HwmfRegionMode.RGN_DIFF, false);
  364. }
  365. @Override
  366. public String toString() {
  367. return boundsToString(bounds);
  368. }
  369. }
  370. /**
  371. * The META_INTERSECTCLIPRECT record sets the clipping region in the playback device context to the
  372. * intersection of the existing clipping region and the specified rectangle.
  373. */
  374. public static class WmfIntersectClipRect implements HwmfRecord, HwmfObjectTableEntry {
  375. /** a rectangle in logical units */
  376. protected final Rectangle2D bounds = new Rectangle2D.Double();
  377. @Override
  378. public HwmfRecordType getWmfRecordType() {
  379. return HwmfRecordType.intersectClipRect;
  380. }
  381. @Override
  382. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  383. return readBounds(leis, bounds);
  384. }
  385. @Override
  386. public void draw(HwmfGraphics ctx) {
  387. ctx.addObjectTableEntry(this);
  388. }
  389. @Override
  390. public void applyObject(HwmfGraphics ctx) {
  391. ctx.setClip(bounds, HwmfRegionMode.RGN_AND, true);
  392. }
  393. @Override
  394. public String toString() {
  395. return boundsToString(bounds);
  396. }
  397. }
  398. /**
  399. * The META_SELECTCLIPREGION record specifies a Region Object to be the current clipping region.
  400. */
  401. public static class WmfSelectClipRegion implements HwmfRecord {
  402. /**
  403. * A 16-bit unsigned integer used to index into the WMF Object Table to get
  404. * the region to be clipped.
  405. */
  406. private int region;
  407. @Override
  408. public HwmfRecordType getWmfRecordType() {
  409. return HwmfRecordType.selectClipRegion;
  410. }
  411. @Override
  412. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  413. region = leis.readShort();
  414. return LittleEndianConsts.SHORT_SIZE;
  415. }
  416. @Override
  417. public void draw(HwmfGraphics ctx) {
  418. }
  419. }
  420. public static class WmfScanObject {
  421. /**
  422. * A 16-bit unsigned integer that specifies the number of horizontal (x-axis)
  423. * coordinates in the ScanLines array. This value MUST be a multiple of 2, since left and right
  424. * endpoints are required to specify each scanline.
  425. */
  426. private int count;
  427. /**
  428. * A 16-bit unsigned integer that defines the vertical (y-axis) coordinate, in logical units, of the top scanline.
  429. */
  430. private int top;
  431. /**
  432. * A 16-bit unsigned integer that defines the vertical (y-axis) coordinate, in logical units, of the bottom scanline.
  433. */
  434. private int bottom;
  435. /**
  436. * A 16-bit unsigned integer that defines the horizontal (x-axis) coordinate,
  437. * in logical units, of the left endpoint of the scanline.
  438. */
  439. private int left_scanline[];
  440. /**
  441. * A 16-bit unsigned integer that defines the horizontal (x-axis) coordinate,
  442. * in logical units, of the right endpoint of the scanline.
  443. */
  444. private int right_scanline[];
  445. /**
  446. * A 16-bit unsigned integer that MUST be the same as the value of the Count
  447. * field; it is present to allow upward travel in the structure.
  448. */
  449. private int count2;
  450. public int init(LittleEndianInputStream leis) {
  451. count = leis.readUShort();
  452. top = leis.readUShort();
  453. bottom = leis.readUShort();
  454. int size = 3*LittleEndianConsts.SHORT_SIZE;
  455. left_scanline = new int[count/2];
  456. right_scanline = new int[count/2];
  457. for (int i=0; i<count/2; i++) {
  458. left_scanline[i] = leis.readUShort();
  459. right_scanline[i] = leis.readUShort();
  460. size += 2*LittleEndianConsts.SHORT_SIZE;
  461. }
  462. count2 = leis.readUShort();
  463. size += LittleEndianConsts.SHORT_SIZE;
  464. return size;
  465. }
  466. }
  467. public static class WmfCreateRegion implements HwmfRecord, HwmfObjectTableEntry {
  468. /**
  469. * A 16-bit signed integer. A value that MUST be ignored.
  470. */
  471. private int nextInChain;
  472. /**
  473. * A 16-bit signed integer that specifies the region identifier. It MUST be 0x0006.
  474. */
  475. private int objectType;
  476. /**
  477. * A 32-bit unsigned integer. A value that MUST be ignored.
  478. */
  479. private int objectCount;
  480. /**
  481. * A 16-bit signed integer that defines the size of the region in bytes plus the size of aScans in bytes.
  482. */
  483. private int regionSize;
  484. /**
  485. * A 16-bit signed integer that defines the number of scanlines composing the region.
  486. */
  487. private int scanCount;
  488. /**
  489. * A 16-bit signed integer that defines the maximum number of points in any one scan in this region.
  490. */
  491. private int maxScan;
  492. private Rectangle2D bounds = new Rectangle2D.Double();
  493. /**
  494. * An array of Scan objects that define the scanlines in the region.
  495. */
  496. private WmfScanObject scanObjects[];
  497. @Override
  498. public HwmfRecordType getWmfRecordType() {
  499. return HwmfRecordType.createRegion;
  500. }
  501. @Override
  502. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  503. nextInChain = leis.readShort();
  504. objectType = leis.readShort();
  505. objectCount = leis.readInt();
  506. regionSize = leis.readShort();
  507. scanCount = leis.readShort();
  508. maxScan = leis.readShort();
  509. // A 16-bit signed integer that defines the x-coordinate, in logical units, of the
  510. // upper-left corner of the rectangle.
  511. double left = leis.readShort();
  512. // A 16-bit signed integer that defines the y-coordinate, in logical units, of the
  513. // upper-left corner of the rectangle.
  514. double top = leis.readShort();
  515. // A 16-bit signed integer that defines the x-coordinate, in logical units, of the
  516. // lower-right corner of the rectangle.
  517. double right = leis.readShort();
  518. // A 16-bit signed integer that defines the y-coordinate, in logical units, of the
  519. // lower-right corner of the rectangle.
  520. double bottom = leis.readShort();
  521. bounds.setRect(left, top, right-left, bottom-top);
  522. int size = 9*LittleEndianConsts.SHORT_SIZE+LittleEndianConsts.INT_SIZE;
  523. scanObjects = new WmfScanObject[scanCount];
  524. for (int i=0; i<scanCount; i++) {
  525. size += (scanObjects[i] = new WmfScanObject()).init(leis);
  526. }
  527. return size;
  528. }
  529. @Override
  530. public void draw(HwmfGraphics ctx) {
  531. ctx.addObjectTableEntry(this);
  532. }
  533. @Override
  534. public void applyObject(HwmfGraphics ctx) {
  535. Rectangle2D lastRect = null;
  536. Area scanLines = new Area();
  537. int count = 0;
  538. for (WmfScanObject so : scanObjects) {
  539. int y = Math.min(so.top, so.bottom);
  540. int h = Math.abs(so.top - so.bottom - 1);
  541. for (int i=0; i<so.count/2; i++) {
  542. int x = Math.min(so.left_scanline[i], so.right_scanline[i]);
  543. int w = Math.abs(so.right_scanline[i] - so.left_scanline[i] - 1);
  544. lastRect = new Rectangle2D.Double(x,y,w,h);
  545. scanLines.add(new Area(lastRect));
  546. count++;
  547. }
  548. }
  549. Shape region = null;
  550. if (count > 0) {
  551. region = (count == 1) ? lastRect : scanLines;
  552. }
  553. ctx.getProperties().setRegion(region);
  554. }
  555. }
  556. }