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.

HwmfBitmapDib.java 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  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.AlphaComposite;
  17. import java.awt.BasicStroke;
  18. import java.awt.Color;
  19. import java.awt.Graphics2D;
  20. import java.awt.LinearGradientPaint;
  21. import java.awt.MultipleGradientPaint;
  22. import java.awt.RenderingHints;
  23. import java.awt.image.BufferedImage;
  24. import java.io.ByteArrayInputStream;
  25. import java.io.IOException;
  26. import java.io.InputStream;
  27. import javax.imageio.ImageIO;
  28. import org.apache.poi.hwmf.usermodel.HwmfPicture;
  29. import org.apache.poi.util.IOUtils;
  30. import org.apache.poi.util.LittleEndian;
  31. import org.apache.poi.util.LittleEndianConsts;
  32. import org.apache.poi.util.LittleEndianInputStream;
  33. import org.apache.poi.util.POILogFactory;
  34. import org.apache.poi.util.POILogger;
  35. import org.apache.poi.util.RecordFormatException;
  36. /**
  37. * The DeviceIndependentBitmap Object defines an image in device-independent bitmap (DIB) format.
  38. */
  39. public class HwmfBitmapDib {
  40. private static final POILogger logger = POILogFactory.getLogger(HwmfBitmapDib.class);
  41. private static final int BMP_HEADER_SIZE = 14;
  42. private static final int MAX_RECORD_LENGTH = HwmfPicture.MAX_RECORD_LENGTH;
  43. public enum BitCount {
  44. /**
  45. * The image SHOULD be in either JPEG or PNG format. <6> Neither of these formats includes
  46. * a color table, so this value specifies that no color table is present. See [JFIF] and [RFC2083]
  47. * for more information concerning JPEG and PNG compression formats.
  48. */
  49. BI_BITCOUNT_0(0x0000),
  50. /**
  51. * Each pixel in the bitmap is represented by a single bit. If the bit is clear, the pixel is displayed
  52. * with the color of the first entry in the color table; if the bit is set, the pixel has the color of the
  53. * second entry in the table.
  54. */
  55. BI_BITCOUNT_1(0x0001),
  56. /**
  57. * Each pixel in the bitmap is represented by a 4-bit index into the color table, and each byte
  58. * contains 2 pixels.
  59. */
  60. BI_BITCOUNT_2(0x0004),
  61. /**
  62. * Each pixel in the bitmap is represented by an 8-bit index into the color table, and each byte
  63. * contains 1 pixel.
  64. */
  65. BI_BITCOUNT_3(0x0008),
  66. /**
  67. * Each pixel in the bitmap is represented by a 16-bit value.
  68. * <br>
  69. * If the Compression field of the BitmapInfoHeader Object is BI_RGB, the Colors field of DIB
  70. * is NULL. Each WORD in the bitmap array represents a single pixel. The relative intensities of
  71. * red, green, and blue are represented with 5 bits for each color component. The value for blue
  72. * is in the least significant 5 bits, followed by 5 bits each for green and red. The most significant
  73. * bit is not used. The color table is used for optimizing colors on palette-based devices, and
  74. * contains the number of entries specified by the ColorUsed field of the BitmapInfoHeader
  75. * Object.
  76. * <br>
  77. * If the Compression field of the BitmapInfoHeader Object is BI_BITFIELDS, the Colors field
  78. * contains three DWORD color masks that specify the red, green, and blue components,
  79. * respectively, of each pixel. Each WORD in the bitmap array represents a single pixel.
  80. * <br>
  81. * When the Compression field is set to BI_BITFIELDS, bits set in each DWORD mask MUST be
  82. * contiguous and SHOULD NOT overlap the bits of another mask.
  83. */
  84. BI_BITCOUNT_4(0x0010),
  85. /**
  86. * The bitmap has a maximum of 2^24 colors, and the Colors field of DIB is
  87. * NULL. Each 3-byte triplet in the bitmap array represents the relative intensities of blue, green,
  88. * and red, respectively, for a pixel. The Colors color table is used for optimizing colors used on
  89. * palette-based devices, and MUST contain the number of entries specified by the ColorUsed
  90. * field of the BitmapInfoHeader Object.
  91. */
  92. BI_BITCOUNT_5(0x0018),
  93. /**
  94. * The bitmap has a maximum of 2^24 colors.
  95. * <br>
  96. * If the Compression field of the BitmapInfoHeader Object is set to BI_RGB, the Colors field
  97. * of DIB is set to NULL. Each DWORD in the bitmap array represents the relative intensities of
  98. * blue, green, and red, respectively, for a pixel. The high byte in each DWORD is not used. The
  99. * Colors color table is used for optimizing colors used on palette-based devices, and MUST
  100. * contain the number of entries specified by the ColorUsed field of the BitmapInfoHeader
  101. * Object.
  102. * <br>
  103. * If the Compression field of the BitmapInfoHeader Object is set to BI_BITFIELDS, the Colors
  104. * field contains three DWORD color masks that specify the red, green, and blue components,
  105. * respectively, of each pixel. Each DWORD in the bitmap array represents a single pixel.
  106. * <br>
  107. * When the Compression field is set to BI_BITFIELDS, bits set in each DWORD mask must be
  108. * contiguous and should not overlap the bits of another mask. All the bits in the pixel do not
  109. * need to be used.
  110. */
  111. BI_BITCOUNT_6(0x0020);
  112. int flag;
  113. BitCount(int flag) {
  114. this.flag = flag;
  115. }
  116. static BitCount valueOf(int flag) {
  117. for (BitCount bc : values()) {
  118. if (bc.flag == flag) return bc;
  119. }
  120. return null;
  121. }
  122. }
  123. public enum Compression {
  124. /**
  125. * The bitmap is in uncompressed red green blue (RGB) format that is not compressed
  126. * and does not use color masks.
  127. */
  128. BI_RGB(0x0000),
  129. /**
  130. * An RGB format that uses run-length encoding (RLE) compression for bitmaps
  131. * with 8 bits per pixel. The compression uses a 2-byte format consisting of a count byte
  132. * followed by a byte containing a color index.
  133. */
  134. BI_RLE8(0x0001),
  135. /**
  136. * An RGB format that uses RLE compression for bitmaps with 4 bits per pixel. The
  137. * compression uses a 2-byte format consisting of a count byte followed by two word-length
  138. * color indexes.
  139. */
  140. BI_RLE4(0x0002),
  141. /**
  142. * The bitmap is not compressed and the color table consists of three DWORD
  143. * color masks that specify the red, green, and blue components, respectively, of each pixel.
  144. * This is valid when used with 16 and 32-bits per pixel bitmaps.
  145. */
  146. BI_BITFIELDS(0x0003),
  147. /**
  148. * The image is a JPEG image, as specified in [JFIF]. This value SHOULD only be used in
  149. * certain bitmap operations, such as JPEG pass-through. The application MUST query for the
  150. * pass-through support, since not all devices support JPEG pass-through. Using non-RGB
  151. * bitmaps MAY limit the portability of the metafile to other devices. For instance, display device
  152. * contexts generally do not support this pass-through.
  153. */
  154. BI_JPEG(0x0004),
  155. /**
  156. * The image is a PNG image, as specified in [RFC2083]. This value SHOULD only be
  157. * used certain bitmap operations, such as JPEG/PNG pass-through. The application MUST query
  158. * for the pass-through support, because not all devices support JPEG/PNG pass-through. Using
  159. * non-RGB bitmaps MAY limit the portability of the metafile to other devices. For instance,
  160. * display device contexts generally do not support this pass-through.
  161. */
  162. BI_PNG(0x0005),
  163. /**
  164. * The image is an uncompressed CMYK format.
  165. */
  166. BI_CMYK(0x000B),
  167. /**
  168. * A CMYK format that uses RLE compression for bitmaps with 8 bits per pixel.
  169. * The compression uses a 2-byte format consisting of a count byte followed by a byte containing
  170. * a color index.
  171. */
  172. BI_CMYKRLE8(0x000C),
  173. /**
  174. * A CMYK format that uses RLE compression for bitmaps with 4 bits per pixel.
  175. * The compression uses a 2-byte format consisting of a count byte followed by two word-length
  176. * color indexes.
  177. */
  178. BI_CMYKRLE4(0x000D);
  179. int flag;
  180. Compression(int flag) {
  181. this.flag = flag;
  182. }
  183. static Compression valueOf(int flag) {
  184. for (Compression c : values()) {
  185. if (c.flag == flag) return c;
  186. }
  187. return null;
  188. }
  189. }
  190. private int headerSize;
  191. private int headerWidth;
  192. private int headerHeight;
  193. private int headerPlanes;
  194. private BitCount headerBitCount;
  195. private Compression headerCompression;
  196. private long headerImageSize = -1;
  197. @SuppressWarnings("unused")
  198. private int headerXPelsPerMeter = -1;
  199. @SuppressWarnings("unused")
  200. private int headerYPelsPerMeter = -1;
  201. private long headerColorUsed = -1;
  202. @SuppressWarnings("unused")
  203. private long headerColorImportant = -1;
  204. private Color colorTable[];
  205. @SuppressWarnings("unused")
  206. private int colorMaskR,colorMaskG,colorMaskB;
  207. // size of header and color table, for start of image data calculation
  208. private int introSize;
  209. private byte imageData[];
  210. public int init(LittleEndianInputStream leis, int recordSize) throws IOException {
  211. leis.mark(10000);
  212. // need to read the header to calculate start of bitmap data correct
  213. introSize = readHeader(leis);
  214. assert(introSize == headerSize);
  215. introSize += readColors(leis);
  216. assert(introSize < 10000);
  217. leis.reset();
  218. // The size and format of this data is determined by information in the DIBHeaderInfo field. If
  219. // it is a BitmapCoreHeader, the size in bytes MUST be calculated as follows:
  220. int bodySize = ((((headerWidth * headerPlanes * headerBitCount.flag + 31) & ~31) / 8) * Math.abs(headerHeight));
  221. // This formula SHOULD also be used to calculate the size of aData when DIBHeaderInfo is a
  222. // BitmapInfoHeader Object, using values from that object, but only if its Compression value is
  223. // BI_RGB, BI_BITFIELDS, or BI_CMYK.
  224. // Otherwise, the size of aData MUST be the BitmapInfoHeader Object value ImageSize.
  225. assert( headerSize != 0x0C || bodySize == headerImageSize);
  226. if (headerSize == 0x0C ||
  227. headerCompression == Compression.BI_RGB ||
  228. headerCompression == Compression.BI_BITFIELDS ||
  229. headerCompression == Compression.BI_CMYK) {
  230. int fileSize = (int)Math.min(introSize+bodySize,recordSize);
  231. imageData = IOUtils.safelyAllocate(fileSize, MAX_RECORD_LENGTH);
  232. leis.readFully(imageData, 0, introSize);
  233. leis.skipFully(recordSize-fileSize);
  234. // emfs are sometimes truncated, read as much as possible
  235. int readBytes = leis.read(imageData, introSize, fileSize-introSize);
  236. return introSize+(recordSize-fileSize)+readBytes;
  237. } else {
  238. imageData = IOUtils.safelyAllocate(recordSize, MAX_RECORD_LENGTH);
  239. leis.readFully(imageData);
  240. return recordSize;
  241. }
  242. }
  243. protected int readHeader(LittleEndianInputStream leis) throws IOException {
  244. int size = 0;
  245. /**
  246. * DIBHeaderInfo (variable): Either a BitmapCoreHeader Object or a
  247. * BitmapInfoHeader Object that specifies information about the image.
  248. *
  249. * The first 32 bits of this field is the HeaderSize value.
  250. * If it is 0x0000000C, then this is a BitmapCoreHeader; otherwise, this is a BitmapInfoHeader.
  251. */
  252. headerSize = leis.readInt();
  253. size += LittleEndianConsts.INT_SIZE;
  254. if (headerSize == 0x0C) {
  255. // BitmapCoreHeader
  256. // A 16-bit unsigned integer that defines the width of the DIB, in pixels.
  257. headerWidth = leis.readUShort();
  258. // A 16-bit unsigned integer that defines the height of the DIB, in pixels.
  259. headerHeight = leis.readUShort();
  260. // A 16-bit unsigned integer that defines the number of planes for the target
  261. // device. This value MUST be 0x0001.
  262. headerPlanes = leis.readUShort();
  263. // A 16-bit unsigned integer that defines the format of each pixel, and the
  264. // maximum number of colors in the DIB.
  265. headerBitCount = BitCount.valueOf(leis.readUShort());
  266. size += 4*LittleEndianConsts.SHORT_SIZE;
  267. } else {
  268. // fix header size, sometimes this is invalid
  269. headerSize = 40;
  270. // BitmapInfoHeader
  271. // A 32-bit signed integer that defines the width of the DIB, in pixels.
  272. // This value MUST be positive.
  273. // This field SHOULD specify the width of the decompressed image file,
  274. // if the Compression value specifies JPEG or PNG format.
  275. headerWidth = leis.readInt();
  276. // A 32-bit signed integer that defines the height of the DIB, in pixels.
  277. // This value MUST NOT be zero.
  278. // - If this value is positive, the DIB is a bottom-up bitmap,
  279. // and its origin is the lower-left corner.
  280. // This field SHOULD specify the height of the decompressed image file,
  281. // if the Compression value specifies JPEG or PNG format.
  282. // - If this value is negative, the DIB is a top-down bitmap,
  283. // and its origin is the upper-left corner. Top-down bitmaps do not support compression.
  284. headerHeight = leis.readInt();
  285. // A 16-bit unsigned integer that defines the number of planes for the target
  286. // device. This value MUST be 0x0001.
  287. headerPlanes = leis.readUShort();
  288. // A 16-bit unsigned integer that defines the format of each pixel, and the
  289. // maximum number of colors in the DIB.
  290. headerBitCount = BitCount.valueOf(leis.readUShort());
  291. // A 32-bit unsigned integer that defines the compression mode of the DIB.
  292. // This value MUST NOT specify a compressed format if the DIB is a top-down bitmap,
  293. // as indicated by the Height value.
  294. headerCompression = Compression.valueOf((int)leis.readUInt());
  295. // A 32-bit unsigned integer that defines the size, in bytes, of the image.
  296. // If the Compression value is BI_RGB, this value SHOULD be zero and MUST be ignored.
  297. // If the Compression value is BI_JPEG or BI_PNG, this value MUST specify the size of the JPEG
  298. // or PNG image buffer, respectively.
  299. headerImageSize = leis.readUInt();
  300. // A 32-bit signed integer that defines the horizontal resolution,
  301. // in pixels-per-meter, of the target device for the DIB.
  302. headerXPelsPerMeter = leis.readInt();
  303. // A 32-bit signed integer that defines the vertical resolution,
  304. headerYPelsPerMeter = leis.readInt();
  305. // A 32-bit unsigned integer that specifies the number of indexes in the
  306. // color table used by the DIB
  307. // in pixelsper-meter, of the target device for the DIB.
  308. headerColorUsed = leis.readUInt();
  309. // A 32-bit unsigned integer that defines the number of color indexes that are
  310. // required for displaying the DIB. If this value is zero, all color indexes are required.
  311. headerColorImportant = leis.readUInt();
  312. size += 8*LittleEndianConsts.INT_SIZE+2*LittleEndianConsts.SHORT_SIZE;
  313. }
  314. return size;
  315. }
  316. protected int readColors(LittleEndianInputStream leis) throws IOException {
  317. switch (headerBitCount) {
  318. default:
  319. case BI_BITCOUNT_0:
  320. // no table
  321. return 0;
  322. case BI_BITCOUNT_1:
  323. // 2 colors
  324. return readRGBQuad(leis, (int)(headerColorUsed == 0 ? 2 : Math.min(headerColorUsed,2)));
  325. case BI_BITCOUNT_2:
  326. // 16 colors
  327. return readRGBQuad(leis, (int)(headerColorUsed == 0 ? 16 : Math.min(headerColorUsed,16)));
  328. case BI_BITCOUNT_3:
  329. // 256 colors
  330. return readRGBQuad(leis, (int)(headerColorUsed == 0 ? 256 : Math.min(headerColorUsed,256)));
  331. case BI_BITCOUNT_4:
  332. switch (headerCompression) {
  333. case BI_RGB:
  334. colorMaskB = 0x1F;
  335. colorMaskG = 0x1F<<5;
  336. colorMaskR = 0x1F<<10;
  337. return 0;
  338. case BI_BITFIELDS:
  339. colorMaskB = leis.readInt();
  340. colorMaskG = leis.readInt();
  341. colorMaskR = leis.readInt();
  342. return 3*LittleEndianConsts.INT_SIZE;
  343. default:
  344. throw new IOException("Invalid compression option ("+headerCompression+") for bitcount ("+headerBitCount+").");
  345. }
  346. case BI_BITCOUNT_5:
  347. case BI_BITCOUNT_6:
  348. switch (headerCompression) {
  349. case BI_RGB:
  350. colorMaskR=0xFF;
  351. colorMaskG=0xFF;
  352. colorMaskB=0xFF;
  353. return 0;
  354. case BI_BITFIELDS:
  355. colorMaskB = leis.readInt();
  356. colorMaskG = leis.readInt();
  357. colorMaskR = leis.readInt();
  358. return 3*LittleEndianConsts.INT_SIZE;
  359. default:
  360. throw new IOException("Invalid compression option ("+headerCompression+") for bitcount ("+headerBitCount+").");
  361. }
  362. }
  363. }
  364. protected int readRGBQuad(LittleEndianInputStream leis, int count) throws IOException {
  365. int size = 0;
  366. colorTable = new Color[count];
  367. for (int i=0; i<count; i++) {
  368. int blue = leis.readUByte();
  369. int green = leis.readUByte();
  370. int red = leis.readUByte();
  371. @SuppressWarnings("unused")
  372. int reserved = leis.readUByte();
  373. colorTable[i] = new Color(red, green, blue);
  374. size += 4 * LittleEndianConsts.BYTE_SIZE;
  375. }
  376. return size;
  377. }
  378. public boolean isValid() {
  379. // the recordsize ended before the image data
  380. if (imageData == null) {
  381. return false;
  382. }
  383. // ignore all black mono-brushes
  384. if (this.headerBitCount == BitCount.BI_BITCOUNT_1) {
  385. if (colorTable == null) {
  386. return false;
  387. }
  388. for (Color c : colorTable) {
  389. if (!Color.BLACK.equals(c)) {
  390. return true;
  391. }
  392. }
  393. return false;
  394. }
  395. return true;
  396. }
  397. public InputStream getBMPStream() {
  398. return new ByteArrayInputStream(getBMPData());
  399. }
  400. public byte[] getBMPData() {
  401. if (imageData == null) {
  402. throw new RecordFormatException("bitmap not initialized ... need to call init() before");
  403. }
  404. // sometimes there are missing bytes after the imageData which will be 0-filled
  405. int imageSize = (int)Math.max(imageData.length, introSize+headerImageSize);
  406. // create the image data and leave the parsing to the ImageIO api
  407. byte buf[] = IOUtils.safelyAllocate(BMP_HEADER_SIZE+imageSize, MAX_RECORD_LENGTH);
  408. // https://en.wikipedia.org/wiki/BMP_file_format # Bitmap file header
  409. buf[0] = (byte)'B';
  410. buf[1] = (byte)'M';
  411. // the full size of the bmp
  412. LittleEndian.putInt(buf, 2, BMP_HEADER_SIZE+imageSize);
  413. // the next 4 bytes are unused
  414. LittleEndian.putInt(buf, 6, 0);
  415. // start of image = BMP header length + dib header length + color tables length
  416. LittleEndian.putInt(buf, 10, BMP_HEADER_SIZE + introSize);
  417. // fill the "known" image data
  418. System.arraycopy(imageData, 0, buf, BMP_HEADER_SIZE, imageData.length);
  419. return buf;
  420. }
  421. public BufferedImage getImage() {
  422. try {
  423. return ImageIO.read(getBMPStream());
  424. } catch (IOException|RuntimeException e) {
  425. logger.log(POILogger.ERROR, "invalid bitmap data - returning placeholder image");
  426. return getPlaceholder();
  427. }
  428. }
  429. @Override
  430. public String toString() {
  431. return
  432. "{ headerSize: " + headerSize +
  433. ", width: " + headerWidth +
  434. ", height: " + headerHeight +
  435. ", planes: " + headerPlanes +
  436. ", bitCount: '" + headerBitCount + "'" +
  437. ", compression: '" + headerCompression + "'" +
  438. ", imageSize: " + headerImageSize +
  439. ", xPelsPerMeter: " + headerXPelsPerMeter +
  440. ", yPelsPerMeter: " + headerYPelsPerMeter +
  441. ", colorUsed: " + headerColorUsed +
  442. ", colorImportant: " + headerColorImportant +
  443. ", imageSize: " + (imageData == null ? 0 : imageData.length) +
  444. "}";
  445. }
  446. protected BufferedImage getPlaceholder() {
  447. BufferedImage bi = new BufferedImage(headerWidth, headerHeight, BufferedImage.TYPE_INT_ARGB);
  448. Graphics2D g = bi.createGraphics();
  449. g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  450. g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
  451. g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
  452. g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
  453. g.setComposite(AlphaComposite.Clear);
  454. g.fillRect(0, 0, headerWidth, headerHeight);
  455. final int arcs = Math.min(headerWidth, headerHeight) / 7;
  456. Color bg = Color.LIGHT_GRAY;
  457. Color fg = Color.GRAY;
  458. LinearGradientPaint lgp = new LinearGradientPaint(0f, 0f, 5, 5,
  459. new float[] {0,.1f,.1001f}, new Color[] {fg,fg,bg}, MultipleGradientPaint.CycleMethod.REFLECT);
  460. g.setComposite(AlphaComposite.SrcOver.derive(0.4f));
  461. g.setPaint(lgp);
  462. g.fillRoundRect(0, 0, headerWidth-1, headerHeight-1, arcs, arcs);
  463. g.setColor(Color.DARK_GRAY);
  464. g.setComposite(AlphaComposite.Src);
  465. g.setStroke(new BasicStroke(2));
  466. g.drawRoundRect(0, 0, headerWidth-1, headerHeight-1, arcs, arcs);
  467. g.dispose();
  468. return bi;
  469. }
  470. }