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.

HemfFill.java 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  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.hemf.record.emf;
  16. import static org.apache.poi.hemf.record.emf.HemfDraw.readPointL;
  17. import static org.apache.poi.hemf.record.emf.HemfDraw.readRectL;
  18. import static org.apache.poi.hemf.record.emf.HemfRecordIterator.HEADER_SIZE;
  19. import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
  20. import static org.apache.poi.hwmf.record.HwmfDraw.pointToString;
  21. import java.awt.Shape;
  22. import java.awt.geom.AffineTransform;
  23. import java.awt.geom.Area;
  24. import java.awt.geom.Point2D;
  25. import java.awt.geom.Rectangle2D;
  26. import java.io.ByteArrayInputStream;
  27. import java.io.ByteArrayOutputStream;
  28. import java.io.IOException;
  29. import java.util.ArrayList;
  30. import java.util.List;
  31. import org.apache.poi.hemf.draw.HemfDrawProperties;
  32. import org.apache.poi.hemf.draw.HemfGraphics;
  33. import org.apache.poi.hwmf.draw.HwmfGraphics;
  34. import org.apache.poi.hwmf.record.HwmfBitmapDib;
  35. import org.apache.poi.hwmf.record.HwmfColorRef;
  36. import org.apache.poi.hwmf.record.HwmfDraw;
  37. import org.apache.poi.hwmf.record.HwmfFill;
  38. import org.apache.poi.hwmf.record.HwmfFill.ColorUsage;
  39. import org.apache.poi.hwmf.record.HwmfRegionMode;
  40. import org.apache.poi.hwmf.record.HwmfTernaryRasterOp;
  41. import org.apache.poi.util.IOUtils;
  42. import org.apache.poi.util.LittleEndian;
  43. import org.apache.poi.util.LittleEndianConsts;
  44. import org.apache.poi.util.LittleEndianInputStream;
  45. public class HemfFill {
  46. private static final int MAX_RECORD_LENGTH = 10_000_000;
  47. /**
  48. * The EMR_SETPOLYFILLMODE record defines polygon fill mode.
  49. */
  50. public static class EmfSetPolyfillMode extends HwmfFill.WmfSetPolyfillMode implements HemfRecord {
  51. @Override
  52. public HemfRecordType getEmfRecordType() {
  53. return HemfRecordType.setPolyfillMode;
  54. }
  55. @Override
  56. public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
  57. // A 32-bit unsigned integer that specifies the polygon fill mode and
  58. // MUST be in the PolygonFillMode enumeration.
  59. polyFillMode = HwmfPolyfillMode.valueOf((int)leis.readUInt());
  60. return LittleEndianConsts.INT_SIZE;
  61. }
  62. }
  63. public static class EmfExtFloodFill extends HwmfFill.WmfExtFloodFill implements HemfRecord {
  64. @Override
  65. public HemfRecordType getEmfRecordType() {
  66. return HemfRecordType.extFloodFill;
  67. }
  68. @Override
  69. public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
  70. long size = readPointL(leis, start);
  71. size += colorRef.init(leis);
  72. // A 32-bit unsigned integer that specifies how to use the Color value to determine the area for
  73. // the flood fill operation. The value MUST be in the FloodFill enumeration
  74. mode = (int)leis.readUInt();
  75. return size + LittleEndianConsts.INT_SIZE;
  76. }
  77. }
  78. /**
  79. * The EMR_STRETCHBLT record specifies a block transfer of pixels from a source bitmap to a destination rectangle,
  80. * optionally in combination with a brush pattern, according to a specified raster operation, stretching or
  81. * compressing the output to fit the dimensions of the destination, if necessary.
  82. */
  83. public static class EmfStretchBlt extends HwmfFill.WmfStretchDib implements HemfRecord {
  84. protected final Rectangle2D bounds = new Rectangle2D.Double();
  85. /** An XForm object that specifies a world-space to page-space transform to apply to the source bitmap. */
  86. protected final AffineTransform xFormSrc = new AffineTransform();
  87. /** A WMF ColorRef object that specifies the background color of the source bitmap. */
  88. protected final HwmfColorRef bkColorSrc = new HwmfColorRef();
  89. @Override
  90. public HemfRecordType getEmfRecordType() {
  91. return HemfRecordType.stretchBlt;
  92. }
  93. @Override
  94. public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
  95. int startIdx = leis.getReadIndex();
  96. long size = readRectL(leis, bounds);
  97. size += readBounds2(leis, this.dstBounds);
  98. // A 32-bit unsigned integer that specifies the raster operation code. This code defines how the
  99. // color data of the source rectangle is to be combined with the color data of the destination
  100. // rectangle and optionally a brush pattern, to achieve the final color.
  101. int rasterOpIndex = (int)leis.readUInt();
  102. rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex >>> 16);
  103. size += LittleEndianConsts.INT_SIZE;
  104. final Point2D srcPnt = new Point2D.Double();
  105. size += readPointL(leis, srcPnt);
  106. size += readXForm(leis, xFormSrc);
  107. size += bkColorSrc.init(leis);
  108. colorUsage = ColorUsage.valueOf((int)leis.readUInt());
  109. // A 32-bit unsigned integer that specifies the offset, in bytes, from the
  110. // start of this record to the source bitmap header in the BitmapBuffer field.
  111. final int offBmiSrc = (int)leis.readUInt();
  112. // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap header.
  113. final int cbBmiSrc = (int)leis.readUInt();
  114. size += 3*LittleEndianConsts.INT_SIZE;
  115. if (size >= recordSize) {
  116. return size;
  117. }
  118. // A 32-bit unsigned integer that specifies the offset, in bytes, from the
  119. // start of this record to the source bitmap bits in the BitmapBuffer field.
  120. final int offBitsSrc = (int)leis.readUInt();
  121. // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap bits.
  122. final int cbBitsSrc = (int)leis.readUInt();
  123. size += 2*LittleEndianConsts.INT_SIZE;
  124. if (size >= recordSize) {
  125. return size;
  126. }
  127. if (srcEqualsDstDimension()) {
  128. srcBounds.setRect(srcPnt.getX(), srcPnt.getY(), dstBounds.getWidth(), dstBounds.getHeight());
  129. } else {
  130. int srcWidth = leis.readInt();
  131. int srcHeight = leis.readInt();
  132. size += 2 * LittleEndianConsts.INT_SIZE;
  133. srcBounds.setRect(srcPnt.getX(), srcPnt.getY(), srcWidth, srcHeight);
  134. }
  135. size += readBitmap(leis, bitmap, startIdx, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc);
  136. return size;
  137. }
  138. protected boolean srcEqualsDstDimension() {
  139. return false;
  140. }
  141. @Override
  142. public void draw(HemfGraphics ctx) {
  143. HemfDrawProperties prop = ctx.getProperties();
  144. prop.setBackgroundColor(this.bkColorSrc);
  145. super.draw(ctx);
  146. }
  147. @Override
  148. public String toString() {
  149. return
  150. "{ bounds: "+boundsToString(bounds)+
  151. ", xFormSrc: { scaleX: "+xFormSrc.getScaleX()+", shearX: "+xFormSrc.getShearX()+", transX: "+xFormSrc.getTranslateX()+", scaleY: "+xFormSrc.getScaleY()+", shearY: "+xFormSrc.getShearY()+", transY: "+xFormSrc.getTranslateY()+" }"+
  152. ", bkColorSrc: "+bkColorSrc+
  153. ","+super.toString().substring(1);
  154. }
  155. }
  156. /**
  157. * The EMR_STRETCHDIBITS record specifies a block transfer of pixels from a source bitmap to a
  158. * destination rectangle, optionally in combination with a brush pattern, according to a specified raster
  159. * operation, stretching or compressing the output to fit the dimensions of the destination, if necessary.
  160. */
  161. public static class EmfStretchDiBits extends HwmfFill.WmfStretchDib implements HemfRecord {
  162. protected final Rectangle2D bounds = new Rectangle2D.Double();
  163. @Override
  164. public HemfRecordType getEmfRecordType() {
  165. return HemfRecordType.stretchDiBits;
  166. }
  167. @Override
  168. public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
  169. final int startIdx = leis.getReadIndex();
  170. long size = readRectL(leis, bounds);
  171. // A 32-bit signed integer that specifies the logical x-coordinate of the upper-left
  172. // corner of the destination rectangle.
  173. int xDest = leis.readInt();
  174. int yDest = leis.readInt();
  175. size += 2*LittleEndianConsts.INT_SIZE;
  176. size += readBounds2(leis, srcBounds);
  177. // A 32-bit unsigned integer that specifies the offset, in bytes from the start
  178. // of this record to the source bitmap header.
  179. int offBmiSrc = (int)leis.readUInt();
  180. // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap header.
  181. int cbBmiSrc = (int)leis.readUInt();
  182. // A 32-bit unsigned integer that specifies the offset, in bytes, from the
  183. // start of this record to the source bitmap bits.
  184. int offBitsSrc = (int)leis.readUInt();
  185. // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap bits.
  186. int cbBitsSrc = (int)leis.readUInt();
  187. // A 32-bit unsigned integer that specifies how to interpret values in the color table
  188. // in the source bitmap header. This value MUST be in the DIBColors enumeration
  189. colorUsage = ColorUsage.valueOf(leis.readInt());
  190. // A 32-bit unsigned integer that specifies a raster operation code.
  191. // These codes define how the color data of the source rectangle is to be combined with the color data
  192. // of the destination rectangle and optionally a brush pattern, to achieve the final color.
  193. // The value MUST be in the WMF Ternary Raster Operation enumeration
  194. int rasterOpIndex = (int)leis.readUInt();
  195. rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex >>> 16);
  196. // A 32-bit signed integer that specifies the logical width of the destination rectangle.
  197. int cxDest = leis.readInt();
  198. // A 32-bit signed integer that specifies the logical height of the destination rectangle.
  199. int cyDest = leis.readInt();
  200. dstBounds.setRect(xDest, yDest, cxDest, cyDest);
  201. size += 8*LittleEndianConsts.INT_SIZE;
  202. size += readBitmap(leis, bitmap, startIdx, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc);
  203. return size;
  204. }
  205. }
  206. /**
  207. * The EMR_BITBLT record specifies a block transfer of pixels from a source bitmap to a destination rectangle,
  208. * optionally in combination with a brush pattern, according to a specified raster operation.
  209. */
  210. public static class EmfBitBlt extends EmfStretchBlt {
  211. @Override
  212. public HemfRecordType getEmfRecordType() {
  213. return HemfRecordType.bitBlt;
  214. }
  215. @Override
  216. protected boolean srcEqualsDstDimension() {
  217. return false;
  218. }
  219. }
  220. /** The EMR_FRAMERGN record draws a border around the specified region using the specified brush. */
  221. public static class EmfFrameRgn extends HwmfDraw.WmfFrameRegion implements HemfRecord {
  222. private final Rectangle2D bounds = new Rectangle2D.Double();
  223. private final List<Rectangle2D> rgnRects = new ArrayList<>();
  224. @Override
  225. public HemfRecordType getEmfRecordType() {
  226. return HemfRecordType.frameRgn;
  227. }
  228. @Override
  229. public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
  230. long size = readRectL(leis, bounds);
  231. // A 32-bit unsigned integer that specifies the size of region data, in bytes.
  232. long rgnDataSize = leis.readUInt();
  233. // A 32-bit unsigned integer that specifies the brush EMF Object Table index.
  234. brushIndex = (int)leis.readUInt();
  235. // A 32-bit signed integer that specifies the width of the vertical brush stroke, in logical units.
  236. width = leis.readInt();
  237. // A 32-bit signed integer that specifies the height of the horizontal brush stroke, in logical units.
  238. height = leis.readInt();
  239. size += 4*LittleEndianConsts.INT_SIZE;
  240. size += readRgnData(leis, rgnRects);
  241. return size;
  242. }
  243. @Override
  244. public void draw(HwmfGraphics ctx) {
  245. ctx.applyObjectTableEntry(brushIndex);
  246. ctx.fill(getShape());
  247. }
  248. protected Shape getShape() {
  249. return getRgnShape(rgnRects);
  250. }
  251. }
  252. /** The EMR_INVERTRGN record inverts the colors in the specified region. */
  253. public static class EmfInvertRgn implements HemfRecord {
  254. protected final Rectangle2D bounds = new Rectangle2D.Double();
  255. protected final List<Rectangle2D> rgnRects = new ArrayList<>();
  256. @Override
  257. public HemfRecordType getEmfRecordType() {
  258. return HemfRecordType.invertRgn;
  259. }
  260. @Override
  261. public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
  262. long size = readRectL(leis, bounds);
  263. // A 32-bit unsigned integer that specifies the size of region data, in bytes.
  264. long rgnDataSize = leis.readUInt();
  265. size += LittleEndianConsts.INT_SIZE;
  266. size += readRgnData(leis, rgnRects);
  267. return size;
  268. }
  269. protected Shape getShape() {
  270. return getRgnShape(rgnRects);
  271. }
  272. }
  273. /**
  274. * The EMR_PAINTRGN record paints the specified region by using the brush currently selected into the
  275. * playback device context.
  276. */
  277. public static class EmfPaintRgn extends EmfInvertRgn {
  278. @Override
  279. public HemfRecordType getEmfRecordType() {
  280. return HemfRecordType.paintRgn;
  281. }
  282. }
  283. /** The EMR_FILLRGN record fills the specified region by using the specified brush. */
  284. public static class EmfFillRgn extends HwmfFill.WmfFillRegion implements HemfRecord {
  285. protected final Rectangle2D bounds = new Rectangle2D.Double();
  286. protected final List<Rectangle2D> rgnRects = new ArrayList<>();
  287. @Override
  288. public HemfRecordType getEmfRecordType() {
  289. return HemfRecordType.fillRgn;
  290. }
  291. @Override
  292. public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
  293. long size = readRectL(leis, bounds);
  294. // A 32-bit unsigned integer that specifies the size of region data, in bytes.
  295. long rgnDataSize = leis.readUInt();
  296. brushIndex = (int)leis.readUInt();
  297. size += 2*LittleEndianConsts.INT_SIZE;
  298. size += readRgnData(leis, rgnRects);
  299. return size;
  300. }
  301. protected Shape getShape() {
  302. return getRgnShape(rgnRects);
  303. }
  304. }
  305. public static class EmfExtSelectClipRgn implements HemfRecord {
  306. protected HwmfRegionMode regionMode;
  307. protected final List<Rectangle2D> rgnRects = new ArrayList<>();
  308. @Override
  309. public HemfRecordType getEmfRecordType() {
  310. return HemfRecordType.extSelectClipRgn;
  311. }
  312. @Override
  313. public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
  314. // A 32-bit unsigned integer that specifies the size of region data in bytes
  315. long rgnDataSize = leis.readUInt();
  316. // A 32-bit unsigned integer that specifies the way to use the region.
  317. regionMode = HwmfRegionMode.valueOf((int)leis.readUInt());
  318. long size = 2* LittleEndianConsts.INT_SIZE;
  319. // If RegionMode is RGN_COPY, this data can be omitted and the clip region
  320. // SHOULD be set to the default (NULL) clip region.
  321. if (regionMode != HwmfRegionMode.RGN_COPY) {
  322. size += readRgnData(leis, rgnRects);
  323. }
  324. return size;
  325. }
  326. protected Shape getShape() {
  327. return getRgnShape(rgnRects);
  328. }
  329. @Override
  330. public void draw(HemfGraphics ctx) {
  331. HemfDrawProperties prop = ctx.getProperties();
  332. ctx.setClip(getShape(), regionMode, true);
  333. }
  334. @Override
  335. public String toString() {
  336. StringBuilder sb = new StringBuilder();
  337. sb.append("{ regionMode: '"+regionMode+"'");
  338. sb.append(", regions: [");
  339. boolean isFirst = true;
  340. for (Rectangle2D r : rgnRects) {
  341. if (!isFirst) {
  342. sb.append(",");
  343. }
  344. isFirst = false;
  345. sb.append(boundsToString(r));
  346. }
  347. sb.append("]}");
  348. return sb.toString();
  349. }
  350. }
  351. public static class EmfAlphaBlend implements HemfRecord {
  352. /** the destination bounding rectangle in device units */
  353. protected final Rectangle2D bounds = new Rectangle2D.Double();
  354. /** the destination rectangle */
  355. protected final Rectangle2D destRect = new Rectangle2D.Double();
  356. /** the source rectangle */
  357. protected final Rectangle2D srcRect = new Rectangle2D.Double();
  358. /**
  359. * The blend operation code. The only source and destination blend operation that has been defined
  360. * is 0x00, which specifies that the source bitmap MUST be combined with the destination bitmap based
  361. * on the alpha transparency values of the source pixels.
  362. */
  363. protected byte blendOperation;
  364. /** This value MUST be 0x00 and MUST be ignored. */
  365. protected byte blendFlags;
  366. /**
  367. * An 8-bit unsigned integer that specifies alpha transparency, which determines the blend of the source
  368. * and destination bitmaps. This value MUST be used on the entire source bitmap. The minimum alpha
  369. * transparency value, zero, corresponds to completely transparent; the maximum value, 0xFF, corresponds
  370. * to completely opaque. In effect, a value of 0xFF specifies that the per-pixel alpha values determine
  371. * the blend of the source and destination bitmaps.
  372. */
  373. protected int srcConstantAlpha;
  374. /**
  375. * A byte that specifies how source and destination pixels are interpreted with respect to alpha transparency.
  376. *
  377. * 0x00:
  378. * The pixels in the source bitmap do not specify alpha transparency.
  379. * In this case, the SrcConstantAlpha value determines the blend of the source and destination bitmaps.
  380. * Note that in the following equations SrcConstantAlpha is divided by 255,
  381. * which produces a value in the range 0 to 1.
  382. *
  383. * 0x01: "AC_SRC_ALPHA"
  384. * Indicates that the source bitmap is 32 bits-per-pixel and specifies an alpha transparency value
  385. * for each pixel.
  386. */
  387. protected byte alphaFormat;
  388. /** a world-space to page-space transform to apply to the source bitmap. */
  389. protected final AffineTransform xFormSrc = new AffineTransform();
  390. /** the background color of the source bitmap. */
  391. protected final HwmfColorRef bkColorSrc = new HwmfColorRef();
  392. /**
  393. * A 32-bit unsigned integer that specifies how to interpret values in the
  394. * color table in the source bitmap header.
  395. */
  396. protected ColorUsage usageSrc;
  397. protected final HwmfBitmapDib bitmap = new HwmfBitmapDib();
  398. @Override
  399. public HemfRecordType getEmfRecordType() {
  400. return HemfRecordType.alphaBlend;
  401. }
  402. @Override
  403. public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
  404. final int startIdx = leis.getReadIndex();
  405. long size = readRectL(leis, bounds);
  406. size += readBounds2(leis, destRect);
  407. blendOperation = leis.readByte();
  408. assert (blendOperation == 0);
  409. blendFlags = leis.readByte();
  410. assert (blendOperation == 0);
  411. srcConstantAlpha = leis.readUByte();
  412. alphaFormat = leis.readByte();
  413. // A 32-bit signed integer that specifies the logical x-coordinate of the upper-left
  414. // corner of the source rectangle.
  415. final int xSrc = leis.readInt();
  416. // A 32-bit signed integer that specifies the logical y-coordinate of the upper-left
  417. // corner of the source rectangle.
  418. final int ySrc = leis.readInt();
  419. size += 3*LittleEndianConsts.INT_SIZE;
  420. size += readXForm(leis, xFormSrc);
  421. size += bkColorSrc.init(leis);
  422. usageSrc = ColorUsage.valueOf((int)leis.readUInt());
  423. // A 32-bit unsigned integer that specifies the offset, in bytes, from the
  424. // start of this record to the source bitmap header in the BitmapBuffer field.
  425. final int offBmiSrc = (int)leis.readUInt();
  426. // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap header.
  427. final int cbBmiSrc = (int)leis.readUInt();
  428. // A 32-bit unsigned integer that specifies the offset, in bytes, from the
  429. // start of this record to the source bitmap bits in the BitmapBuffer field.
  430. final int offBitsSrc = (int)leis.readUInt();
  431. // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap bits.
  432. final int cbBitsSrc = (int)leis.readUInt();
  433. // A 32-bit signed integer that specifies the logical width of the source rectangle.
  434. // This value MUST be greater than zero.
  435. final int cxSrc = leis.readInt();
  436. // A 32-bit signed integer that specifies the logical height of the source rectangle.
  437. // This value MUST be greater than zero.
  438. final int cySrc = leis.readInt();
  439. srcRect.setRect(xSrc, ySrc, cxSrc, cySrc);
  440. size += 7 * LittleEndianConsts.INT_SIZE;
  441. size += readBitmap(leis, bitmap, startIdx, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc);
  442. return size;
  443. }
  444. }
  445. /**
  446. * The EMR_SETDIBITSTODEVICE record specifies a block transfer of pixels from specified scanlines of
  447. * a source bitmap to a destination rectangle.
  448. */
  449. public static class EmfSetDiBitsToDevice implements HemfRecord {
  450. protected final Rectangle2D bounds = new Rectangle2D.Double();
  451. protected final Point2D dest = new Point2D.Double();
  452. protected final Rectangle2D src = new Rectangle2D.Double();
  453. protected ColorUsage usageSrc;
  454. protected final HwmfBitmapDib bitmap = new HwmfBitmapDib();
  455. @Override
  456. public HemfRecordType getEmfRecordType() {
  457. return HemfRecordType.setDiBitsToDevice;
  458. }
  459. @Override
  460. public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
  461. int startIdx = leis.getReadIndex();
  462. // A WMF RectL object that defines the destination bounding rectangle in device units.
  463. long size = readRectL(leis, bounds);
  464. // the logical x/y-coordinate of the upper-left corner of the destination rectangle.
  465. size += readPointL(leis, dest);
  466. // the source rectangle
  467. size += readBounds2(leis, src);
  468. // A 32-bit unsigned integer that specifies the offset, in bytes, from the
  469. // start of this record to the source bitmap header in the BitmapBuffer field.
  470. final int offBmiSrc = (int)leis.readUInt();
  471. // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap header.
  472. final int cbBmiSrc = (int)leis.readUInt();
  473. // A 32-bit unsigned integer that specifies the offset, in bytes, from the
  474. // start of this record to the source bitmap bits in the BitmapBuffer field.
  475. final int offBitsSrc = (int)leis.readUInt();
  476. // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap bits.
  477. final int cbBitsSrc = (int)leis.readUInt();
  478. // A 32-bit unsigned integer that specifies how to interpret values in the color table
  479. // in the source bitmap header. This value MUST be in the DIBColors enumeration
  480. usageSrc = ColorUsage.valueOf((int)leis.readUInt());
  481. // A 32-bit unsigned integer that specifies the first scan line in the array.
  482. final int iStartScan = (int)leis.readUInt();
  483. // A 32-bit unsigned integer that specifies the number of scan lines.
  484. final int cScans = (int)leis.readUInt();
  485. size += 7*LittleEndianConsts.INT_SIZE;
  486. size += readBitmap(leis, bitmap, startIdx, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc);
  487. return size;
  488. }
  489. @Override
  490. public String toString() {
  491. return
  492. "{ bounds: " + boundsToString(bounds) +
  493. ", dest: " + pointToString(dest) +
  494. ", src: " + boundsToString(src) +
  495. ", usageSrc: '" + usageSrc + "'" +
  496. ", bitmap: " + bitmap +
  497. "}";
  498. }
  499. }
  500. static long readBitmap(final LittleEndianInputStream leis, final HwmfBitmapDib bitmap,
  501. final int startIdx, final int offBmi, final int cbBmi, final int offBits, int cbBits)
  502. throws IOException {
  503. if (offBmi == 0) {
  504. return 0;
  505. }
  506. final int offCurr = leis.getReadIndex()-(startIdx-HEADER_SIZE);
  507. final int undefinedSpace1 = offBmi-offCurr;
  508. if (undefinedSpace1 < 0) {
  509. return 0;
  510. }
  511. final int undefinedSpace2 = offBits-offCurr-cbBmi-undefinedSpace1;
  512. assert(undefinedSpace2 >= 0);
  513. leis.skipFully(undefinedSpace1);
  514. if (cbBmi == 0 || cbBits == 0) {
  515. return undefinedSpace1;
  516. }
  517. final int dibSize = cbBmi+cbBits;
  518. if (undefinedSpace2 == 0) {
  519. return undefinedSpace1 + bitmap.init(leis, dibSize);
  520. }
  521. final ByteArrayOutputStream bos = new ByteArrayOutputStream(cbBmi+cbBits);
  522. final long cbBmiSrcAct = IOUtils.copy(leis, bos, cbBmi);
  523. assert (cbBmiSrcAct == cbBmi);
  524. leis.skipFully(undefinedSpace2);
  525. final long cbBitsSrcAct = IOUtils.copy(leis, bos, cbBits);
  526. assert (cbBitsSrcAct == cbBits);
  527. final LittleEndianInputStream leisDib = new LittleEndianInputStream(new ByteArrayInputStream(bos.toByteArray()));
  528. final int dibSizeAct = bitmap.init(leisDib, dibSize);
  529. assert (dibSizeAct <= dibSize);
  530. return undefinedSpace1 + cbBmi + undefinedSpace2 + cbBits;
  531. }
  532. static long readRgnData(final LittleEndianInputStream leis, final List<Rectangle2D> rgnRects) {
  533. // *** RegionDataHeader ***
  534. // A 32-bit unsigned integer that specifies the size of this object in bytes. This MUST be 0x00000020.
  535. long rgnHdrSize = leis.readUInt();
  536. assert(rgnHdrSize == 0x20);
  537. // A 32-bit unsigned integer that specifies the region type. This SHOULD be RDH_RECTANGLES (0x00000001)
  538. long rgnHdrType = leis.readUInt();
  539. assert(rgnHdrType == 1);
  540. // A 32-bit unsigned integer that specifies the number of rectangles in this region.
  541. long rgnCntRect = leis.readUInt();
  542. // A 32-bit unsigned integer that specifies the size of the buffer of rectangles in bytes.
  543. long rgnCntBytes = leis.readUInt();
  544. long size = 4*LittleEndianConsts.INT_SIZE;
  545. // A 128-bit WMF RectL object, which specifies the bounds of the region.
  546. Rectangle2D rgnBounds = new Rectangle2D.Double();
  547. size += readRectL(leis, rgnBounds);
  548. for (int i=0; i<rgnCntRect; i++) {
  549. Rectangle2D rgnRct = new Rectangle2D.Double();
  550. size += readRectL(leis, rgnRct);
  551. rgnRects.add(rgnRct);
  552. }
  553. return size;
  554. }
  555. static int readBounds2(LittleEndianInputStream leis, Rectangle2D bounds) {
  556. /**
  557. * The 32-bit signed integers that defines the corners of the bounding rectangle.
  558. */
  559. int x = leis.readInt();
  560. int y = leis.readInt();
  561. int w = leis.readInt();
  562. int h = leis.readInt();
  563. bounds.setRect(x, y, w, h);
  564. return 4 * LittleEndianConsts.INT_SIZE;
  565. }
  566. static int readXForm(LittleEndianInputStream leis, AffineTransform xform) {
  567. // mapping <java AffineTransform> = <xform>
  568. // m00 (scaleX) = eM11 (Horizontal scaling component)
  569. double m00 = leis.readFloat();
  570. // m01 (shearX) = eM12 (Horizontal proportionality constant)
  571. double m01 = leis.readFloat();
  572. // m10 (shearY) = eM21 (Vertical proportionality constant)
  573. double m10 = leis.readFloat();
  574. // m11 (scaleY) = eM22 (Vertical scaling component)
  575. double m11 = leis.readFloat();
  576. // m02 (translateX) = eDx (The horizontal translation component, in logical units.)
  577. double m02 = leis.readFloat();
  578. // m12 (translateY) = eDy (The vertical translation component, in logical units.)
  579. double m12 = leis.readFloat();
  580. xform.setTransform(m00, m10, m01, m11, m02, m12);
  581. return 6 * LittleEndian.INT_SIZE;
  582. }
  583. protected static Shape getRgnShape(List<Rectangle2D> rgnRects) {
  584. if (rgnRects.size() == 1) {
  585. return rgnRects.get(0);
  586. }
  587. final Area frame = new Area();
  588. rgnRects.forEach((rct) -> frame.add(new Area(rct)));
  589. return frame;
  590. }
  591. }