001/* 002 * $Id: BmpImage.java 4784 2011-03-15 08:33:00Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044 045/* 046 * This code was originally released in 2001 by SUN (see class 047 * com.sun.media.imageioimpl.plugins.bmp.BMPImageReader.java) 048 * using the BSD license in a specific wording. In a mail dating from 049 * January 23, 2008, Brian Burkhalter (@sun.com) gave us permission 050 * to use the code under the following version of the BSD license: 051 * 052 * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. 053 * 054 * Redistribution and use in source and binary forms, with or without 055 * modification, are permitted provided that the following conditions 056 * are met: 057 * 058 * - Redistribution of source code must retain the above copyright 059 * notice, this list of conditions and the following disclaimer. 060 * 061 * - Redistribution in binary form must reproduce the above copyright 062 * notice, this list of conditions and the following disclaimer in 063 * the documentation and/or other materials provided with the 064 * distribution. 065 * 066 * Neither the name of Sun Microsystems, Inc. or the names of 067 * contributors may be used to endorse or promote products derived 068 * from this software without specific prior written permission. 069 * 070 * This software is provided "AS IS," without a warranty of any 071 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 072 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 073 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY 074 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 075 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 076 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS 077 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 078 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, 079 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 080 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR 081 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE 082 * POSSIBILITY OF SUCH DAMAGES. 083 * 084 * You acknowledge that this software is not designed or intended for 085 * use in the design, construction, operation or maintenance of any 086 * nuclear facility. 087 */ 088package com.itextpdf.text.pdf.codec; 089 090import java.io.BufferedInputStream; 091import java.io.ByteArrayInputStream; 092import java.io.IOException; 093import java.io.InputStream; 094import java.net.URL; 095import java.util.HashMap; 096 097import com.itextpdf.text.BadElementException; 098import com.itextpdf.text.ExceptionConverter; 099import com.itextpdf.text.Image; 100import com.itextpdf.text.ImgRaw; 101import com.itextpdf.text.Utilities; 102import com.itextpdf.text.error_messages.MessageLocalization; 103import com.itextpdf.text.pdf.PdfArray; 104import com.itextpdf.text.pdf.PdfDictionary; 105import com.itextpdf.text.pdf.PdfName; 106import com.itextpdf.text.pdf.PdfNumber; 107import com.itextpdf.text.pdf.PdfString; 108 109/** Reads a BMP image. All types of BMP can be read. 110 * <p> 111 * It is based in the JAI codec. 112 * 113 * @author Paulo Soares 114 */ 115public class BmpImage { 116 117 // BMP variables 118 private InputStream inputStream; 119 private long bitmapFileSize; 120 private long bitmapOffset; 121 private long compression; 122 private long imageSize; 123 private byte palette[]; 124 private int imageType; 125 private int numBands; 126 private boolean isBottomUp; 127 private int bitsPerPixel; 128 private int redMask, greenMask, blueMask, alphaMask; 129 public HashMap<String, Object> properties = new HashMap<String, Object>(); 130 private long xPelsPerMeter; 131 private long yPelsPerMeter; 132 // BMP Image types 133 private static final int VERSION_2_1_BIT = 0; 134 private static final int VERSION_2_4_BIT = 1; 135 private static final int VERSION_2_8_BIT = 2; 136 private static final int VERSION_2_24_BIT = 3; 137 138 private static final int VERSION_3_1_BIT = 4; 139 private static final int VERSION_3_4_BIT = 5; 140 private static final int VERSION_3_8_BIT = 6; 141 private static final int VERSION_3_24_BIT = 7; 142 143 private static final int VERSION_3_NT_16_BIT = 8; 144 private static final int VERSION_3_NT_32_BIT = 9; 145 146 private static final int VERSION_4_1_BIT = 10; 147 private static final int VERSION_4_4_BIT = 11; 148 private static final int VERSION_4_8_BIT = 12; 149 private static final int VERSION_4_16_BIT = 13; 150 private static final int VERSION_4_24_BIT = 14; 151 private static final int VERSION_4_32_BIT = 15; 152 153 // Color space types 154 private static final int LCS_CALIBRATED_RGB = 0; 155 private static final int LCS_sRGB = 1; 156 private static final int LCS_CMYK = 2; 157 158 // Compression Types 159 private static final int BI_RGB = 0; 160 private static final int BI_RLE8 = 1; 161 private static final int BI_RLE4 = 2; 162 private static final int BI_BITFIELDS = 3; 163 164 int width; 165 int height; 166 167 BmpImage(InputStream is, boolean noHeader, int size) throws IOException { 168 bitmapFileSize = size; 169 bitmapOffset = 0; 170 process(is, noHeader); 171 } 172 173 /** Reads a BMP from an url. 174 * @param url the url 175 * @throws IOException on error 176 * @return the image 177 */ 178 public static Image getImage(URL url) throws IOException { 179 InputStream is = null; 180 try { 181 is = url.openStream(); 182 Image img = getImage(is); 183 img.setUrl(url); 184 return img; 185 } 186 finally { 187 if (is != null) { 188 is.close(); 189 } 190 } 191 } 192 193 /** Reads a BMP from a stream. The stream is not closed. 194 * @param is the stream 195 * @throws IOException on error 196 * @return the image 197 */ 198 public static Image getImage(InputStream is) throws IOException { 199 return getImage(is, false, 0); 200 } 201 202 /** Reads a BMP from a stream. The stream is not closed. 203 * The BMP may not have a header and be considered as a plain DIB. 204 * @param is the stream 205 * @param noHeader true to process a plain DIB 206 * @param size the size of the DIB. Not used for a BMP 207 * @throws IOException on error 208 * @return the image 209 */ 210 public static Image getImage(InputStream is, boolean noHeader, int size) throws IOException { 211 BmpImage bmp = new BmpImage(is, noHeader, size); 212 try { 213 Image img = bmp.getImage(); 214 img.setDpi((int)(bmp.xPelsPerMeter * 0.0254d + 0.5d), (int)(bmp.yPelsPerMeter * 0.0254d + 0.5d)); 215 img.setOriginalType(Image.ORIGINAL_BMP); 216 return img; 217 } 218 catch (BadElementException be) { 219 throw new ExceptionConverter(be); 220 } 221 } 222 223 /** Reads a BMP from a file. 224 * @param file the file 225 * @throws IOException on error 226 * @return the image 227 */ 228 public static Image getImage(String file) throws IOException { 229 return getImage(Utilities.toURL(file)); 230 } 231 232 /** Reads a BMP from a byte array. 233 * @param data the byte array 234 * @throws IOException on error 235 * @return the image 236 */ 237 public static Image getImage(byte data[]) throws IOException { 238 ByteArrayInputStream is = new ByteArrayInputStream(data); 239 Image img = getImage(is); 240 img.setOriginalData(data); 241 return img; 242 } 243 244 245 protected void process(InputStream stream, boolean noHeader) throws IOException { 246 if (noHeader || stream instanceof BufferedInputStream) { 247 inputStream = stream; 248 } else { 249 inputStream = new BufferedInputStream(stream); 250 } 251 if (!noHeader) { 252 // Start File Header 253 if (!(readUnsignedByte(inputStream) == 'B' && 254 readUnsignedByte(inputStream) == 'M')) { 255 throw new RuntimeException(MessageLocalization.getComposedMessage("invalid.magic.value.for.bmp.file")); 256 } 257 258 // Read file size 259 bitmapFileSize = readDWord(inputStream); 260 261 // Read the two reserved fields 262 readWord(inputStream); 263 readWord(inputStream); 264 265 // Offset to the bitmap from the beginning 266 bitmapOffset = readDWord(inputStream); 267 268 // End File Header 269 } 270 // Start BitmapCoreHeader 271 long size = readDWord(inputStream); 272 273 if (size == 12) { 274 width = readWord(inputStream); 275 height = readWord(inputStream); 276 } else { 277 width = readLong(inputStream); 278 height = readLong(inputStream); 279 } 280 281 int planes = readWord(inputStream); 282 bitsPerPixel = readWord(inputStream); 283 284 properties.put("color_planes", Integer.valueOf(planes)); 285 properties.put("bits_per_pixel", Integer.valueOf(bitsPerPixel)); 286 287 // As BMP always has 3 rgb bands, except for Version 5, 288 // which is bgra 289 numBands = 3; 290 if (bitmapOffset == 0) 291 bitmapOffset = size; 292 if (size == 12) { 293 // Windows 2.x and OS/2 1.x 294 properties.put("bmp_version", "BMP v. 2.x"); 295 296 // Classify the image type 297 if (bitsPerPixel == 1) { 298 imageType = VERSION_2_1_BIT; 299 } else if (bitsPerPixel == 4) { 300 imageType = VERSION_2_4_BIT; 301 } else if (bitsPerPixel == 8) { 302 imageType = VERSION_2_8_BIT; 303 } else if (bitsPerPixel == 24) { 304 imageType = VERSION_2_24_BIT; 305 } 306 307 // Read in the palette 308 int numberOfEntries = (int)((bitmapOffset-14-size) / 3); 309 int sizeOfPalette = numberOfEntries*3; 310 if (bitmapOffset == size) { 311 switch (imageType) { 312 case VERSION_2_1_BIT: 313 sizeOfPalette = 2 * 3; 314 break; 315 case VERSION_2_4_BIT: 316 sizeOfPalette = 16 * 3; 317 break; 318 case VERSION_2_8_BIT: 319 sizeOfPalette = 256 * 3; 320 break; 321 case VERSION_2_24_BIT: 322 sizeOfPalette = 0; 323 break; 324 } 325 bitmapOffset = size + sizeOfPalette; 326 } 327 readPalette(sizeOfPalette); 328 } else { 329 330 compression = readDWord(inputStream); 331 imageSize = readDWord(inputStream); 332 xPelsPerMeter = readLong(inputStream); 333 yPelsPerMeter = readLong(inputStream); 334 long colorsUsed = readDWord(inputStream); 335 long colorsImportant = readDWord(inputStream); 336 337 switch((int)compression) { 338 case BI_RGB: 339 properties.put("compression", "BI_RGB"); 340 break; 341 342 case BI_RLE8: 343 properties.put("compression", "BI_RLE8"); 344 break; 345 346 case BI_RLE4: 347 properties.put("compression", "BI_RLE4"); 348 break; 349 350 case BI_BITFIELDS: 351 properties.put("compression", "BI_BITFIELDS"); 352 break; 353 } 354 355 properties.put("x_pixels_per_meter", Long.valueOf(xPelsPerMeter)); 356 properties.put("y_pixels_per_meter", Long.valueOf(yPelsPerMeter)); 357 properties.put("colors_used", Long.valueOf(colorsUsed)); 358 properties.put("colors_important", Long.valueOf(colorsImportant)); 359 360 if (size == 40) { 361 // Windows 3.x and Windows NT 362 switch((int)compression) { 363 364 case BI_RGB: // No compression 365 case BI_RLE8: // 8-bit RLE compression 366 case BI_RLE4: // 4-bit RLE compression 367 368 if (bitsPerPixel == 1) { 369 imageType = VERSION_3_1_BIT; 370 } else if (bitsPerPixel == 4) { 371 imageType = VERSION_3_4_BIT; 372 } else if (bitsPerPixel == 8) { 373 imageType = VERSION_3_8_BIT; 374 } else if (bitsPerPixel == 24) { 375 imageType = VERSION_3_24_BIT; 376 } else if (bitsPerPixel == 16) { 377 imageType = VERSION_3_NT_16_BIT; 378 redMask = 0x7C00; 379 greenMask = 0x3E0; 380 blueMask = 0x1F; 381 properties.put("red_mask", Integer.valueOf(redMask)); 382 properties.put("green_mask", Integer.valueOf(greenMask)); 383 properties.put("blue_mask", Integer.valueOf(blueMask)); 384 } else if (bitsPerPixel == 32) { 385 imageType = VERSION_3_NT_32_BIT; 386 redMask = 0x00FF0000; 387 greenMask = 0x0000FF00; 388 blueMask = 0x000000FF; 389 properties.put("red_mask", Integer.valueOf(redMask)); 390 properties.put("green_mask", Integer.valueOf(greenMask)); 391 properties.put("blue_mask", Integer.valueOf(blueMask)); 392 } 393 394 // Read in the palette 395 int numberOfEntries = (int)((bitmapOffset-14-size) / 4); 396 int sizeOfPalette = numberOfEntries*4; 397 if (bitmapOffset == size) { 398 switch (imageType) { 399 case VERSION_3_1_BIT: 400 sizeOfPalette = (int)(colorsUsed == 0 ? 2 : colorsUsed) * 4; 401 break; 402 case VERSION_3_4_BIT: 403 sizeOfPalette = (int)(colorsUsed == 0 ? 16 : colorsUsed) * 4; 404 break; 405 case VERSION_3_8_BIT: 406 sizeOfPalette = (int)(colorsUsed == 0 ? 256 : colorsUsed) * 4; 407 break; 408 default: 409 sizeOfPalette = 0; 410 break; 411 } 412 bitmapOffset = size + sizeOfPalette; 413 } 414 readPalette(sizeOfPalette); 415 416 properties.put("bmp_version", "BMP v. 3.x"); 417 break; 418 419 case BI_BITFIELDS: 420 421 if (bitsPerPixel == 16) { 422 imageType = VERSION_3_NT_16_BIT; 423 } else if (bitsPerPixel == 32) { 424 imageType = VERSION_3_NT_32_BIT; 425 } 426 427 // BitsField encoding 428 redMask = (int)readDWord(inputStream); 429 greenMask = (int)readDWord(inputStream); 430 blueMask = (int)readDWord(inputStream); 431 432 properties.put("red_mask", Integer.valueOf(redMask)); 433 properties.put("green_mask", Integer.valueOf(greenMask)); 434 properties.put("blue_mask", Integer.valueOf(blueMask)); 435 436 if (colorsUsed != 0) { 437 // there is a palette 438 sizeOfPalette = (int)colorsUsed*4; 439 readPalette(sizeOfPalette); 440 } 441 442 properties.put("bmp_version", "BMP v. 3.x NT"); 443 break; 444 445 default: 446 throw new 447 RuntimeException("Invalid compression specified in BMP file."); 448 } 449 } else if (size == 108) { 450 // Windows 4.x BMP 451 452 properties.put("bmp_version", "BMP v. 4.x"); 453 454 // rgb masks, valid only if comp is BI_BITFIELDS 455 redMask = (int)readDWord(inputStream); 456 greenMask = (int)readDWord(inputStream); 457 blueMask = (int)readDWord(inputStream); 458 // Only supported for 32bpp BI_RGB argb 459 alphaMask = (int)readDWord(inputStream); 460 long csType = readDWord(inputStream); 461 int redX = readLong(inputStream); 462 int redY = readLong(inputStream); 463 int redZ = readLong(inputStream); 464 int greenX = readLong(inputStream); 465 int greenY = readLong(inputStream); 466 int greenZ = readLong(inputStream); 467 int blueX = readLong(inputStream); 468 int blueY = readLong(inputStream); 469 int blueZ = readLong(inputStream); 470 long gammaRed = readDWord(inputStream); 471 long gammaGreen = readDWord(inputStream); 472 long gammaBlue = readDWord(inputStream); 473 474 if (bitsPerPixel == 1) { 475 imageType = VERSION_4_1_BIT; 476 } else if (bitsPerPixel == 4) { 477 imageType = VERSION_4_4_BIT; 478 } else if (bitsPerPixel == 8) { 479 imageType = VERSION_4_8_BIT; 480 } else if (bitsPerPixel == 16) { 481 imageType = VERSION_4_16_BIT; 482 if ((int)compression == BI_RGB) { 483 redMask = 0x7C00; 484 greenMask = 0x3E0; 485 blueMask = 0x1F; 486 } 487 } else if (bitsPerPixel == 24) { 488 imageType = VERSION_4_24_BIT; 489 } else if (bitsPerPixel == 32) { 490 imageType = VERSION_4_32_BIT; 491 if ((int)compression == BI_RGB) { 492 redMask = 0x00FF0000; 493 greenMask = 0x0000FF00; 494 blueMask = 0x000000FF; 495 } 496 } 497 498 properties.put("red_mask", Integer.valueOf(redMask)); 499 properties.put("green_mask", Integer.valueOf(greenMask)); 500 properties.put("blue_mask", Integer.valueOf(blueMask)); 501 properties.put("alpha_mask", Integer.valueOf(alphaMask)); 502 503 // Read in the palette 504 int numberOfEntries = (int)((bitmapOffset-14-size) / 4); 505 int sizeOfPalette = numberOfEntries*4; 506 if (bitmapOffset == size) { 507 switch (imageType) { 508 case VERSION_4_1_BIT: 509 sizeOfPalette = (int)(colorsUsed == 0 ? 2 : colorsUsed) * 4; 510 break; 511 case VERSION_4_4_BIT: 512 sizeOfPalette = (int)(colorsUsed == 0 ? 16 : colorsUsed) * 4; 513 break; 514 case VERSION_4_8_BIT: 515 sizeOfPalette = (int)(colorsUsed == 0 ? 256 : colorsUsed) * 4; 516 break; 517 default: 518 sizeOfPalette = 0; 519 break; 520 } 521 bitmapOffset = size + sizeOfPalette; 522 } 523 readPalette(sizeOfPalette); 524 525 switch((int)csType) { 526 case LCS_CALIBRATED_RGB: 527 // All the new fields are valid only for this case 528 properties.put("color_space", "LCS_CALIBRATED_RGB"); 529 properties.put("redX", Integer.valueOf(redX)); 530 properties.put("redY", Integer.valueOf(redY)); 531 properties.put("redZ", Integer.valueOf(redZ)); 532 properties.put("greenX", Integer.valueOf(greenX)); 533 properties.put("greenY", Integer.valueOf(greenY)); 534 properties.put("greenZ", Integer.valueOf(greenZ)); 535 properties.put("blueX", Integer.valueOf(blueX)); 536 properties.put("blueY", Integer.valueOf(blueY)); 537 properties.put("blueZ", Integer.valueOf(blueZ)); 538 properties.put("gamma_red", Long.valueOf(gammaRed)); 539 properties.put("gamma_green", Long.valueOf(gammaGreen)); 540 properties.put("gamma_blue", Long.valueOf(gammaBlue)); 541 542 // break; 543 throw new 544 RuntimeException("Not implemented yet."); 545 546 case LCS_sRGB: 547 // Default Windows color space 548 properties.put("color_space", "LCS_sRGB"); 549 break; 550 551 case LCS_CMYK: 552 properties.put("color_space", "LCS_CMYK"); 553 // break; 554 throw new 555 RuntimeException("Not implemented yet."); 556 } 557 558 } else { 559 properties.put("bmp_version", "BMP v. 5.x"); 560 throw new 561 RuntimeException("BMP version 5 not implemented yet."); 562 } 563 } 564 565 if (height > 0) { 566 // bottom up image 567 isBottomUp = true; 568 } else { 569 // top down image 570 isBottomUp = false; 571 height = Math.abs(height); 572 } 573 // When number of bitsPerPixel is <= 8, we use IndexColorModel. 574 if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) { 575 576 numBands = 1; 577 578 579 // Create IndexColorModel from the palette. 580 byte r[], g[], b[]; 581 int sizep; 582 if (imageType == VERSION_2_1_BIT || 583 imageType == VERSION_2_4_BIT || 584 imageType == VERSION_2_8_BIT) { 585 586 sizep = palette.length/3; 587 588 if (sizep > 256) { 589 sizep = 256; 590 } 591 592 int off; 593 r = new byte[sizep]; 594 g = new byte[sizep]; 595 b = new byte[sizep]; 596 for (int i=0; i<sizep; i++) { 597 off = 3 * i; 598 b[i] = palette[off]; 599 g[i] = palette[off+1]; 600 r[i] = palette[off+2]; 601 } 602 } else { 603 sizep = palette.length/4; 604 605 if (sizep > 256) { 606 sizep = 256; 607 } 608 609 int off; 610 r = new byte[sizep]; 611 g = new byte[sizep]; 612 b = new byte[sizep]; 613 for (int i=0; i<sizep; i++) { 614 off = 4 * i; 615 b[i] = palette[off]; 616 g[i] = palette[off+1]; 617 r[i] = palette[off+2]; 618 } 619 } 620 621 } else if (bitsPerPixel == 16) { 622 numBands = 3; 623 } else if (bitsPerPixel == 32) { 624 numBands = alphaMask == 0 ? 3 : 4; 625 626 // The number of bands in the SampleModel is determined by 627 // the length of the mask array passed in. 628 } else { 629 numBands = 3; 630 } 631 } 632 633 private byte[] getPalette(int group) { 634 if (palette == null) 635 return null; 636 byte np[] = new byte[palette.length / group * 3]; 637 int e = palette.length / group; 638 for (int k = 0; k < e; ++k) { 639 int src = k * group; 640 int dest = k * 3; 641 np[dest + 2] = palette[src++]; 642 np[dest + 1] = palette[src++]; 643 np[dest] = palette[src]; 644 } 645 return np; 646 } 647 648 private Image getImage() throws IOException, BadElementException { 649 byte bdata[] = null; // buffer for byte data 650 651 // if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE) 652 // bdata = (byte[])((DataBufferByte)tile.getDataBuffer()).getData(); 653 // else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT) 654 // sdata = (short[])((DataBufferUShort)tile.getDataBuffer()).getData(); 655 // else if (sampleModel.getDataType() == DataBuffer.TYPE_INT) 656 // idata = (int[])((DataBufferInt)tile.getDataBuffer()).getData(); 657 658 // There should only be one tile. 659 switch(imageType) { 660 661 case VERSION_2_1_BIT: 662 // no compression 663 return read1Bit(3); 664 665 case VERSION_2_4_BIT: 666 // no compression 667 return read4Bit(3); 668 669 case VERSION_2_8_BIT: 670 // no compression 671 return read8Bit(3); 672 673 case VERSION_2_24_BIT: 674 // no compression 675 bdata = new byte[width * height * 3]; 676 read24Bit(bdata); 677 return new ImgRaw(width, height, 3, 8, bdata); 678 679 case VERSION_3_1_BIT: 680 // 1-bit images cannot be compressed. 681 return read1Bit(4); 682 683 case VERSION_3_4_BIT: 684 switch((int)compression) { 685 case BI_RGB: 686 return read4Bit(4); 687 688 case BI_RLE4: 689 return readRLE4(); 690 691 default: 692 throw new 693 RuntimeException("Invalid compression specified for BMP file."); 694 } 695 696 case VERSION_3_8_BIT: 697 switch((int)compression) { 698 case BI_RGB: 699 return read8Bit(4); 700 701 case BI_RLE8: 702 return readRLE8(); 703 704 default: 705 throw new 706 RuntimeException("Invalid compression specified for BMP file."); 707 } 708 709 case VERSION_3_24_BIT: 710 // 24-bit images are not compressed 711 bdata = new byte[width * height * 3]; 712 read24Bit(bdata); 713 return new ImgRaw(width, height, 3, 8, bdata); 714 715 case VERSION_3_NT_16_BIT: 716 return read1632Bit(false); 717 718 case VERSION_3_NT_32_BIT: 719 return read1632Bit(true); 720 721 case VERSION_4_1_BIT: 722 return read1Bit(4); 723 724 case VERSION_4_4_BIT: 725 switch((int)compression) { 726 727 case BI_RGB: 728 return read4Bit(4); 729 730 case BI_RLE4: 731 return readRLE4(); 732 733 default: 734 throw new 735 RuntimeException("Invalid compression specified for BMP file."); 736 } 737 738 case VERSION_4_8_BIT: 739 switch((int)compression) { 740 741 case BI_RGB: 742 return read8Bit(4); 743 744 case BI_RLE8: 745 return readRLE8(); 746 747 default: 748 throw new 749 RuntimeException("Invalid compression specified for BMP file."); 750 } 751 752 case VERSION_4_16_BIT: 753 return read1632Bit(false); 754 755 case VERSION_4_24_BIT: 756 bdata = new byte[width * height * 3]; 757 read24Bit(bdata); 758 return new ImgRaw(width, height, 3, 8, bdata); 759 760 case VERSION_4_32_BIT: 761 return read1632Bit(true); 762 } 763 return null; 764 } 765 766 private Image indexedModel(byte bdata[], int bpc, int paletteEntries) throws BadElementException { 767 Image img = new ImgRaw(width, height, 1, bpc, bdata); 768 PdfArray colorspace = new PdfArray(); 769 colorspace.add(PdfName.INDEXED); 770 colorspace.add(PdfName.DEVICERGB); 771 byte np[] = getPalette(paletteEntries); 772 int len = np.length; 773 colorspace.add(new PdfNumber(len / 3 - 1)); 774 colorspace.add(new PdfString(np)); 775 PdfDictionary ad = new PdfDictionary(); 776 ad.put(PdfName.COLORSPACE, colorspace); 777 img.setAdditional(ad); 778 return img; 779 } 780 781 private void readPalette(int sizeOfPalette) throws IOException { 782 if (sizeOfPalette == 0) { 783 return; 784 } 785 786 palette = new byte[sizeOfPalette]; 787 int bytesRead = 0; 788 while (bytesRead < sizeOfPalette) { 789 int r = inputStream.read(palette, bytesRead, sizeOfPalette - bytesRead); 790 if (r < 0) { 791 throw new RuntimeException(MessageLocalization.getComposedMessage("incomplete.palette")); 792 } 793 bytesRead += r; 794 } 795 properties.put("palette", palette); 796 } 797 798 // Deal with 1 Bit images using IndexColorModels 799 private Image read1Bit(int paletteEntries) throws IOException, BadElementException { 800 byte bdata[] = new byte[(width + 7) / 8 * height]; 801 int padding = 0; 802 int bytesPerScanline = (int)Math.ceil(width/8.0d); 803 804 int remainder = bytesPerScanline % 4; 805 if (remainder != 0) { 806 padding = 4 - remainder; 807 } 808 809 int imSize = (bytesPerScanline + padding) * height; 810 811 // Read till we have the whole image 812 byte values[] = new byte[imSize]; 813 int bytesRead = 0; 814 while (bytesRead < imSize) { 815 bytesRead += inputStream.read(values, bytesRead, 816 imSize - bytesRead); 817 } 818 819 if (isBottomUp) { 820 821 // Convert the bottom up image to a top down format by copying 822 // one scanline from the bottom to the top at a time. 823 824 for (int i=0; i<height; i++) { 825 System.arraycopy(values, 826 imSize - (i+1)*(bytesPerScanline + padding), 827 bdata, 828 i*bytesPerScanline, bytesPerScanline); 829 } 830 } else { 831 832 for (int i=0; i<height; i++) { 833 System.arraycopy(values, 834 i * (bytesPerScanline + padding), 835 bdata, 836 i * bytesPerScanline, 837 bytesPerScanline); 838 } 839 } 840 return indexedModel(bdata, 1, paletteEntries); 841 } 842 843 // Method to read a 4 bit BMP image data 844 private Image read4Bit(int paletteEntries) throws IOException, BadElementException { 845 byte bdata[] = new byte[(width + 1) / 2 * height]; 846 847 // Padding bytes at the end of each scanline 848 int padding = 0; 849 850 int bytesPerScanline = (int)Math.ceil(width/2.0d); 851 int remainder = bytesPerScanline % 4; 852 if (remainder != 0) { 853 padding = 4 - remainder; 854 } 855 856 int imSize = (bytesPerScanline + padding) * height; 857 858 // Read till we have the whole image 859 byte values[] = new byte[imSize]; 860 int bytesRead = 0; 861 while (bytesRead < imSize) { 862 bytesRead += inputStream.read(values, bytesRead, 863 imSize - bytesRead); 864 } 865 866 if (isBottomUp) { 867 868 // Convert the bottom up image to a top down format by copying 869 // one scanline from the bottom to the top at a time. 870 for (int i=0; i<height; i++) { 871 System.arraycopy(values, 872 imSize - (i+1)*(bytesPerScanline + padding), 873 bdata, 874 i*bytesPerScanline, 875 bytesPerScanline); 876 } 877 } else { 878 for (int i=0; i<height; i++) { 879 System.arraycopy(values, 880 i * (bytesPerScanline + padding), 881 bdata, 882 i * bytesPerScanline, 883 bytesPerScanline); 884 } 885 } 886 return indexedModel(bdata, 4, paletteEntries); 887 } 888 889 // Method to read 8 bit BMP image data 890 private Image read8Bit(int paletteEntries) throws IOException, BadElementException { 891 byte bdata[] = new byte[width * height]; 892 // Padding bytes at the end of each scanline 893 int padding = 0; 894 895 // width * bitsPerPixel should be divisible by 32 896 int bitsPerScanline = width * 8; 897 if ( bitsPerScanline%32 != 0) { 898 padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline; 899 padding = (int)Math.ceil(padding/8.0); 900 } 901 902 int imSize = (width + padding) * height; 903 904 // Read till we have the whole image 905 byte values[] = new byte[imSize]; 906 int bytesRead = 0; 907 while (bytesRead < imSize) { 908 bytesRead += inputStream.read(values, bytesRead, imSize - bytesRead); 909 } 910 911 if (isBottomUp) { 912 913 // Convert the bottom up image to a top down format by copying 914 // one scanline from the bottom to the top at a time. 915 for (int i=0; i<height; i++) { 916 System.arraycopy(values, 917 imSize - (i+1) * (width + padding), 918 bdata, 919 i * width, 920 width); 921 } 922 } else { 923 for (int i=0; i<height; i++) { 924 System.arraycopy(values, 925 i * (width + padding), 926 bdata, 927 i * width, 928 width); 929 } 930 } 931 return indexedModel(bdata, 8, paletteEntries); 932 } 933 934 // Method to read 24 bit BMP image data 935 private void read24Bit(byte[] bdata) { 936 // Padding bytes at the end of each scanline 937 int padding = 0; 938 939 // width * bitsPerPixel should be divisible by 32 940 int bitsPerScanline = width * 24; 941 if ( bitsPerScanline%32 != 0) { 942 padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline; 943 padding = (int)Math.ceil(padding/8.0); 944 } 945 946 947 int imSize = (width * 3 + 3) / 4 * 4 * height; 948 // Read till we have the whole image 949 byte values[] = new byte[imSize]; 950 try { 951 int bytesRead = 0; 952 while (bytesRead < imSize) { 953 int r = inputStream.read(values, bytesRead, 954 imSize - bytesRead); 955 if (r < 0) 956 break; 957 bytesRead += r; 958 } 959 } catch (IOException ioe) { 960 throw new ExceptionConverter(ioe); 961 } 962 963 int l=0, count; 964 965 if (isBottomUp) { 966 int max = width*height*3-1; 967 968 count = -padding; 969 for (int i=0; i<height; i++) { 970 l = max - (i+1)*width*3 + 1; 971 count += padding; 972 for (int j=0; j<width; j++) { 973 bdata[l + 2] = values[count++]; 974 bdata[l + 1] = values[count++]; 975 bdata[l] = values[count++]; 976 l += 3; 977 } 978 } 979 } else { 980 count = -padding; 981 for (int i=0; i<height; i++) { 982 count += padding; 983 for (int j=0; j<width; j++) { 984 bdata[l + 2] = values[count++]; 985 bdata[l + 1] = values[count++]; 986 bdata[l] = values[count++]; 987 l += 3; 988 } 989 } 990 } 991 } 992 993 private int findMask(int mask) { 994 int k = 0; 995 for (; k < 32; ++k) { 996 if ((mask & 1) == 1) 997 break; 998 mask >>>= 1; 999 } 1000 return mask; 1001 } 1002 1003 private int findShift(int mask) { 1004 int k = 0; 1005 for (; k < 32; ++k) { 1006 if ((mask & 1) == 1) 1007 break; 1008 mask >>>= 1; 1009 } 1010 return k; 1011 } 1012 1013 private Image read1632Bit(boolean is32) throws IOException, BadElementException { 1014 1015 int red_mask = findMask(redMask); 1016 int red_shift = findShift(redMask); 1017 int red_factor = red_mask + 1; 1018 int green_mask = findMask(greenMask); 1019 int green_shift = findShift(greenMask); 1020 int green_factor = green_mask + 1; 1021 int blue_mask = findMask(blueMask); 1022 int blue_shift = findShift(blueMask); 1023 int blue_factor = blue_mask + 1; 1024 byte bdata[] = new byte[width * height * 3]; 1025 // Padding bytes at the end of each scanline 1026 int padding = 0; 1027 1028 if (!is32) { 1029 // width * bitsPerPixel should be divisible by 32 1030 int bitsPerScanline = width * 16; 1031 if ( bitsPerScanline%32 != 0) { 1032 padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline; 1033 padding = (int)Math.ceil(padding/8.0); 1034 } 1035 } 1036 1037 int imSize = (int)imageSize; 1038 if (imSize == 0) { 1039 imSize = (int)(bitmapFileSize - bitmapOffset); 1040 } 1041 1042 int l=0; 1043 int v; 1044 if (isBottomUp) { 1045 for (int i=height - 1; i >= 0; --i) { 1046 l = width * 3 * i; 1047 for (int j=0; j<width; j++) { 1048 if (is32) 1049 v = (int)readDWord(inputStream); 1050 else 1051 v = readWord(inputStream); 1052 bdata[l++] = (byte)((v >>> red_shift & red_mask) * 256 / red_factor); 1053 bdata[l++] = (byte)((v >>> green_shift & green_mask) * 256 / green_factor); 1054 bdata[l++] = (byte)((v >>> blue_shift & blue_mask) * 256 / blue_factor); 1055 } 1056 for (int m=0; m<padding; m++) { 1057 inputStream.read(); 1058 } 1059 } 1060 } else { 1061 for (int i=0; i<height; i++) { 1062 for (int j=0; j<width; j++) { 1063 if (is32) 1064 v = (int)readDWord(inputStream); 1065 else 1066 v = readWord(inputStream); 1067 bdata[l++] = (byte)((v >>> red_shift & red_mask) * 256 / red_factor); 1068 bdata[l++] = (byte)((v >>> green_shift & green_mask) * 256 / green_factor); 1069 bdata[l++] = (byte)((v >>> blue_shift & blue_mask) * 256 / blue_factor); 1070 } 1071 for (int m=0; m<padding; m++) { 1072 inputStream.read(); 1073 } 1074 } 1075 } 1076 return new ImgRaw(width, height, 3, 8, bdata); 1077 } 1078 1079 private Image readRLE8() throws IOException, BadElementException { 1080 1081 // If imageSize field is not provided, calculate it. 1082 int imSize = (int)imageSize; 1083 if (imSize == 0) { 1084 imSize = (int)(bitmapFileSize - bitmapOffset); 1085 } 1086 1087 // Read till we have the whole image 1088 byte values[] = new byte[imSize]; 1089 int bytesRead = 0; 1090 while (bytesRead < imSize) { 1091 bytesRead += inputStream.read(values, bytesRead, 1092 imSize - bytesRead); 1093 } 1094 1095 // Since data is compressed, decompress it 1096 byte val[] = decodeRLE(true, values); 1097 1098 // Uncompressed data does not have any padding 1099 imSize = width * height; 1100 1101 if (isBottomUp) { 1102 1103 // Convert the bottom up image to a top down format by copying 1104 // one scanline from the bottom to the top at a time. 1105 // int bytesPerScanline = (int)Math.ceil((double)width/8.0); 1106 byte temp[] = new byte[val.length]; 1107 int bytesPerScanline = width; 1108 for (int i=0; i<height; i++) { 1109 System.arraycopy(val, 1110 imSize - (i+1)*bytesPerScanline, 1111 temp, 1112 i*bytesPerScanline, bytesPerScanline); 1113 } 1114 val = temp; 1115 } 1116 return indexedModel(val, 8, 4); 1117 } 1118 1119 private Image readRLE4() throws IOException, BadElementException { 1120 1121 // If imageSize field is not specified, calculate it. 1122 int imSize = (int)imageSize; 1123 if (imSize == 0) { 1124 imSize = (int)(bitmapFileSize - bitmapOffset); 1125 } 1126 1127 // Read till we have the whole image 1128 byte values[] = new byte[imSize]; 1129 int bytesRead = 0; 1130 while (bytesRead < imSize) { 1131 bytesRead += inputStream.read(values, bytesRead, 1132 imSize - bytesRead); 1133 } 1134 1135 // Decompress the RLE4 compressed data. 1136 byte val[] = decodeRLE(false, values); 1137 1138 // Invert it as it is bottom up format. 1139 if (isBottomUp) { 1140 1141 byte inverted[] = val; 1142 val = new byte[width * height]; 1143 int l = 0, index, lineEnd; 1144 1145 for (int i = height-1; i >= 0; i--) { 1146 index = i * width; 1147 lineEnd = l + width; 1148 while(l != lineEnd) { 1149 val[l++] = inverted[index++]; 1150 } 1151 } 1152 } 1153 int stride = (width + 1) / 2; 1154 byte bdata[] = new byte[stride * height]; 1155 int ptr = 0; 1156 int sh = 0; 1157 for (int h = 0; h < height; ++h) { 1158 for (int w = 0; w < width; ++w) { 1159 if ((w & 1) == 0) 1160 bdata[sh + w / 2] = (byte)(val[ptr++] << 4); 1161 else 1162 bdata[sh + w / 2] |= (byte)(val[ptr++] & 0x0f); 1163 } 1164 sh += stride; 1165 } 1166 return indexedModel(bdata, 4, 4); 1167 } 1168 1169 private byte[] decodeRLE(boolean is8, byte values[]) { 1170 byte val[] = new byte[width * height]; 1171 try { 1172 int ptr = 0; 1173 int x = 0; 1174 int q = 0; 1175 for (int y = 0; y < height && ptr < values.length;) { 1176 int count = values[ptr++] & 0xff; 1177 if (count != 0) { 1178 // encoded mode 1179 int bt = values[ptr++] & 0xff; 1180 if (is8) { 1181 for (int i = count; i != 0; --i) { 1182 val[q++] = (byte)bt; 1183 } 1184 } 1185 else { 1186 for (int i = 0; i < count; ++i) { 1187 val[q++] = (byte)((i & 1) == 1 ? bt & 0x0f : bt >>> 4 & 0x0f); 1188 } 1189 } 1190 x += count; 1191 } 1192 else { 1193 // escape mode 1194 count = values[ptr++] & 0xff; 1195 if (count == 1) 1196 break; 1197 switch (count) { 1198 case 0: 1199 x = 0; 1200 ++y; 1201 q = y * width; 1202 break; 1203 case 2: 1204 // delta mode 1205 x += values[ptr++] & 0xff; 1206 y += values[ptr++] & 0xff; 1207 q = y * width + x; 1208 break; 1209 default: 1210 // absolute mode 1211 if (is8) { 1212 for (int i = count; i != 0; --i) 1213 val[q++] = (byte)(values[ptr++] & 0xff); 1214 } 1215 else { 1216 int bt = 0; 1217 for (int i = 0; i < count; ++i) { 1218 if ((i & 1) == 0) 1219 bt = values[ptr++] & 0xff; 1220 val[q++] = (byte)((i & 1) == 1 ? bt & 0x0f : bt >>> 4 & 0x0f); 1221 } 1222 } 1223 x += count; 1224 // read pad byte 1225 if (is8) { 1226 if ((count & 1) == 1) 1227 ++ptr; 1228 } 1229 else { 1230 if ((count & 3) == 1 || (count & 3) == 2) 1231 ++ptr; 1232 } 1233 break; 1234 } 1235 } 1236 } 1237 } 1238 catch (RuntimeException e) { 1239 //empty on purpose 1240 } 1241 1242 return val; 1243 } 1244 1245 // Windows defined data type reading methods - everything is little endian 1246 1247 // Unsigned 8 bits 1248 private int readUnsignedByte(InputStream stream) throws IOException { 1249 return stream.read() & 0xff; 1250 } 1251 1252 // Unsigned 2 bytes 1253 private int readUnsignedShort(InputStream stream) throws IOException { 1254 int b1 = readUnsignedByte(stream); 1255 int b2 = readUnsignedByte(stream); 1256 return (b2 << 8 | b1) & 0xffff; 1257 } 1258 1259 // Signed 16 bits 1260 private int readShort(InputStream stream) throws IOException { 1261 int b1 = readUnsignedByte(stream); 1262 int b2 = readUnsignedByte(stream); 1263 return b2 << 8 | b1; 1264 } 1265 1266 // Unsigned 16 bits 1267 private int readWord(InputStream stream) throws IOException { 1268 return readUnsignedShort(stream); 1269 } 1270 1271 // Unsigned 4 bytes 1272 private long readUnsignedInt(InputStream stream) throws IOException { 1273 int b1 = readUnsignedByte(stream); 1274 int b2 = readUnsignedByte(stream); 1275 int b3 = readUnsignedByte(stream); 1276 int b4 = readUnsignedByte(stream); 1277 long l = b4 << 24 | b3 << 16 | b2 << 8 | b1; 1278 return l & 0xffffffff; 1279 } 1280 1281 // Signed 4 bytes 1282 private int readInt(InputStream stream) throws IOException { 1283 int b1 = readUnsignedByte(stream); 1284 int b2 = readUnsignedByte(stream); 1285 int b3 = readUnsignedByte(stream); 1286 int b4 = readUnsignedByte(stream); 1287 return b4 << 24 | b3 << 16 | b2 << 8 | b1; 1288 } 1289 1290 // Unsigned 4 bytes 1291 private long readDWord(InputStream stream) throws IOException { 1292 return readUnsignedInt(stream); 1293 } 1294 1295 // 32 bit signed value 1296 private int readLong(InputStream stream) throws IOException { 1297 return readInt(stream); 1298 } 1299}