001/* 002 * $Id: GifImage.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 */ 044package com.itextpdf.text.pdf.codec; 045 046import java.io.BufferedInputStream; 047import java.io.ByteArrayInputStream; 048import java.io.DataInputStream; 049import java.io.IOException; 050import java.io.InputStream; 051import java.net.URL; 052import java.util.ArrayList; 053 054import com.itextpdf.text.ExceptionConverter; 055import com.itextpdf.text.Image; 056import com.itextpdf.text.ImgRaw; 057import com.itextpdf.text.Utilities; 058import com.itextpdf.text.error_messages.MessageLocalization; 059import com.itextpdf.text.pdf.PdfArray; 060import com.itextpdf.text.pdf.PdfDictionary; 061import com.itextpdf.text.pdf.PdfName; 062import com.itextpdf.text.pdf.PdfNumber; 063import com.itextpdf.text.pdf.PdfString; 064 065/** Reads gif images of all types. All the images in a gif are read in the constructors 066 * and can be retrieved with other methods. 067 * @author Paulo Soares 068 */ 069public class GifImage { 070 071 protected DataInputStream in; 072 protected int width; // full image width 073 protected int height; // full image height 074 protected boolean gctFlag; // global color table used 075 076 protected int bgIndex; // background color index 077 protected int bgColor; // background color 078 protected int pixelAspect; // pixel aspect ratio 079 080 protected boolean lctFlag; // local color table flag 081 protected boolean interlace; // interlace flag 082 protected int lctSize; // local color table size 083 084 protected int ix, iy, iw, ih; // current image rectangle 085 086 protected byte[] block = new byte[256]; // current data block 087 protected int blockSize = 0; // block size 088 089 // last graphic control extension info 090 protected int dispose = 0; // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev 091 protected boolean transparency = false; // use transparent color 092 protected int delay = 0; // delay in milliseconds 093 protected int transIndex; // transparent color index 094 095 protected static final int MaxStackSize = 4096; // max decoder pixel stack size 096 097 // LZW decoder working arrays 098 protected short[] prefix; 099 protected byte[] suffix; 100 protected byte[] pixelStack; 101 protected byte[] pixels; 102 103 protected byte m_out[]; 104 protected int m_bpc; 105 protected int m_gbpc; 106 protected byte m_global_table[]; 107 protected byte m_local_table[]; 108 protected byte m_curr_table[]; 109 protected int m_line_stride; 110 protected byte fromData[]; 111 protected URL fromUrl; 112 113 114 protected ArrayList<GifFrame> frames = new ArrayList<GifFrame>(); // frames read from current file 115 116 /** Reads gif images from an URL. 117 * @param url the URL 118 * @throws IOException on error 119 */ 120 public GifImage(URL url) throws IOException { 121 fromUrl = url; 122 InputStream is = null; 123 try { 124 is = url.openStream(); 125 process(is); 126 } 127 finally { 128 if (is != null) { 129 is.close(); 130 } 131 } 132 } 133 134 /** Reads gif images from a file. 135 * @param file the file 136 * @throws IOException on error 137 */ 138 public GifImage(String file) throws IOException { 139 this(Utilities.toURL(file)); 140 } 141 142 /** Reads gif images from a byte array. 143 * @param data the byte array 144 * @throws IOException on error 145 */ 146 public GifImage(byte data[]) throws IOException { 147 fromData = data; 148 InputStream is = null; 149 try { 150 is = new ByteArrayInputStream(data); 151 process(is); 152 } 153 finally { 154 if (is != null) { 155 is.close(); 156 } 157 } 158 } 159 160 /** Reads gif images from a stream. The stream is not closed. 161 * @param is the stream 162 * @throws IOException on error 163 */ 164 public GifImage(InputStream is) throws IOException { 165 process(is); 166 } 167 168 /** Gets the number of frames the gif has. 169 * @return the number of frames the gif has 170 */ 171 public int getFrameCount() { 172 return frames.size(); 173 } 174 175 /** Gets the image from a frame. The first frame is 1. 176 * @param frame the frame to get the image from 177 * @return the image 178 */ 179 public Image getImage(int frame) { 180 GifFrame gf = frames.get(frame - 1); 181 return gf.image; 182 } 183 184 /** Gets the [x,y] position of the frame in reference to the 185 * logical screen. 186 * @param frame the frame 187 * @return the [x,y] position of the frame 188 */ 189 public int[] getFramePosition(int frame) { 190 GifFrame gf = frames.get(frame - 1); 191 return new int[]{gf.ix, gf.iy}; 192 193 } 194 195 /** Gets the logical screen. The images may be smaller and placed 196 * in some position in this screen to playback some animation. 197 * No image will be be bigger that this. 198 * @return the logical screen dimensions as [x,y] 199 */ 200 public int[] getLogicalScreen() { 201 return new int[]{width, height}; 202 } 203 204 void process(InputStream is) throws IOException { 205 in = new DataInputStream(new BufferedInputStream(is)); 206 readHeader(); 207 readContents(); 208 if (frames.isEmpty()) 209 throw new IOException(MessageLocalization.getComposedMessage("the.file.does.not.contain.any.valid.image")); 210 } 211 212 /** 213 * Reads GIF file header information. 214 */ 215 protected void readHeader() throws IOException { 216 StringBuilder id = new StringBuilder(""); 217 for (int i = 0; i < 6; i++) 218 id.append((char)in.read()); 219 if (!id.toString().startsWith("GIF8")) { 220 throw new IOException(MessageLocalization.getComposedMessage("gif.signature.nor.found")); 221 } 222 223 readLSD(); 224 if (gctFlag) { 225 m_global_table = readColorTable(m_gbpc); 226 } 227 } 228 229 /** 230 * Reads Logical Screen Descriptor 231 */ 232 protected void readLSD() throws IOException { 233 234 // logical screen size 235 width = readShort(); 236 height = readShort(); 237 238 // packed fields 239 int packed = in.read(); 240 gctFlag = (packed & 0x80) != 0; // 1 : global color table flag 241 m_gbpc = (packed & 7) + 1; 242 bgIndex = in.read(); // background color index 243 pixelAspect = in.read(); // pixel aspect ratio 244 } 245 246 /** 247 * Reads next 16-bit value, LSB first 248 */ 249 protected int readShort() throws IOException { 250 // read 16-bit value, LSB first 251 return in.read() | in.read() << 8; 252 } 253 254 /** 255 * Reads next variable length block from input. 256 * 257 * @return number of bytes stored in "buffer" 258 */ 259 protected int readBlock() throws IOException { 260 blockSize = in.read(); 261 if (blockSize <= 0) 262 return blockSize = 0; 263 264 blockSize = in.read(block, 0, blockSize); 265 266 return blockSize; 267 } 268 269 protected byte[] readColorTable(int bpc) throws IOException { 270 int ncolors = 1 << bpc; 271 int nbytes = 3*ncolors; 272 bpc = newBpc(bpc); 273 byte table[] = new byte[(1 << bpc) * 3]; 274 in.readFully(table, 0, nbytes); 275 return table; 276 } 277 278 279 static protected int newBpc(int bpc) { 280 switch (bpc) { 281 case 1: 282 case 2: 283 case 4: 284 break; 285 case 3: 286 return 4; 287 default: 288 return 8; 289 } 290 return bpc; 291 } 292 293 protected void readContents() throws IOException { 294 // read GIF file content blocks 295 boolean done = false; 296 while (!done) { 297 int code = in.read(); 298 switch (code) { 299 300 case 0x2C: // image separator 301 readImage(); 302 break; 303 304 case 0x21: // extension 305 code = in.read(); 306 switch (code) { 307 308 case 0xf9: // graphics control extension 309 readGraphicControlExt(); 310 break; 311 312 case 0xff: // application extension 313 readBlock(); 314 skip(); // don't care 315 break; 316 317 default: // uninteresting extension 318 skip(); 319 } 320 break; 321 322 default: 323 done = true; 324 break; 325 } 326 } 327 } 328 329 /** 330 * Reads next frame image 331 */ 332 protected void readImage() throws IOException { 333 ix = readShort(); // (sub)image position & size 334 iy = readShort(); 335 iw = readShort(); 336 ih = readShort(); 337 338 int packed = in.read(); 339 lctFlag = (packed & 0x80) != 0; // 1 - local color table flag 340 interlace = (packed & 0x40) != 0; // 2 - interlace flag 341 // 3 - sort flag 342 // 4-5 - reserved 343 lctSize = 2 << (packed & 7); // 6-8 - local color table size 344 m_bpc = newBpc(m_gbpc); 345 if (lctFlag) { 346 m_curr_table = readColorTable((packed & 7) + 1); // read table 347 m_bpc = newBpc((packed & 7) + 1); 348 } 349 else { 350 m_curr_table = m_global_table; 351 } 352 if (transparency && transIndex >= m_curr_table.length / 3) 353 transparency = false; 354 if (transparency && m_bpc == 1) { // Acrobat 5.05 doesn't like this combination 355 byte tp[] = new byte[12]; 356 System.arraycopy(m_curr_table, 0, tp, 0, 6); 357 m_curr_table = tp; 358 m_bpc = 2; 359 } 360 boolean skipZero = decodeImageData(); // decode pixel data 361 if (!skipZero) 362 skip(); 363 364 Image img = null; 365 try { 366 img = new ImgRaw(iw, ih, 1, m_bpc, m_out); 367 PdfArray colorspace = new PdfArray(); 368 colorspace.add(PdfName.INDEXED); 369 colorspace.add(PdfName.DEVICERGB); 370 int len = m_curr_table.length; 371 colorspace.add(new PdfNumber(len / 3 - 1)); 372 colorspace.add(new PdfString(m_curr_table)); 373 PdfDictionary ad = new PdfDictionary(); 374 ad.put(PdfName.COLORSPACE, colorspace); 375 img.setAdditional(ad); 376 if (transparency) { 377 img.setTransparency(new int[]{transIndex, transIndex}); 378 } 379 } 380 catch (Exception e) { 381 throw new ExceptionConverter(e); 382 } 383 img.setOriginalType(Image.ORIGINAL_GIF); 384 img.setOriginalData(fromData); 385 img.setUrl(fromUrl); 386 GifFrame gf = new GifFrame(); 387 gf.image = img; 388 gf.ix = ix; 389 gf.iy = iy; 390 frames.add(gf); // add image to frame list 391 392 //resetFrame(); 393 394 } 395 396 protected boolean decodeImageData() throws IOException { 397 int NullCode = -1; 398 int npix = iw * ih; 399 int available, clear, code_mask, code_size, end_of_information, in_code, old_code, 400 bits, code, count, i, datum, data_size, first, top, bi; 401 boolean skipZero = false; 402 403 if (prefix == null) 404 prefix = new short[MaxStackSize]; 405 if (suffix == null) 406 suffix = new byte[MaxStackSize]; 407 if (pixelStack == null) 408 pixelStack = new byte[MaxStackSize+1]; 409 410 m_line_stride = (iw * m_bpc + 7) / 8; 411 m_out = new byte[m_line_stride * ih]; 412 int pass = 1; 413 int inc = interlace ? 8 : 1; 414 int line = 0; 415 int xpos = 0; 416 417 // Initialize GIF data stream decoder. 418 419 data_size = in.read(); 420 clear = 1 << data_size; 421 end_of_information = clear + 1; 422 available = clear + 2; 423 old_code = NullCode; 424 code_size = data_size + 1; 425 code_mask = (1 << code_size) - 1; 426 for (code = 0; code < clear; code++) { 427 prefix[code] = 0; 428 suffix[code] = (byte) code; 429 } 430 431 // Decode GIF pixel stream. 432 433 datum = bits = count = first = top = bi = 0; 434 435 for (i = 0; i < npix; ) { 436 if (top == 0) { 437 if (bits < code_size) { 438 // Load bytes until there are enough bits for a code. 439 if (count == 0) { 440 // Read a new data block. 441 count = readBlock(); 442 if (count <= 0) { 443 skipZero = true; 444 break; 445 } 446 bi = 0; 447 } 448 datum += (block[bi] & 0xff) << bits; 449 bits += 8; 450 bi++; 451 count--; 452 continue; 453 } 454 455 // Get the next code. 456 457 code = datum & code_mask; 458 datum >>= code_size; 459 bits -= code_size; 460 461 // Interpret the code 462 463 if (code > available || code == end_of_information) 464 break; 465 if (code == clear) { 466 // Reset decoder. 467 code_size = data_size + 1; 468 code_mask = (1 << code_size) - 1; 469 available = clear + 2; 470 old_code = NullCode; 471 continue; 472 } 473 if (old_code == NullCode) { 474 pixelStack[top++] = suffix[code]; 475 old_code = code; 476 first = code; 477 continue; 478 } 479 in_code = code; 480 if (code == available) { 481 pixelStack[top++] = (byte) first; 482 code = old_code; 483 } 484 while (code > clear) { 485 pixelStack[top++] = suffix[code]; 486 code = prefix[code]; 487 } 488 first = suffix[code] & 0xff; 489 490 // Add a new string to the string table, 491 492 if (available >= MaxStackSize) 493 break; 494 pixelStack[top++] = (byte) first; 495 prefix[available] = (short) old_code; 496 suffix[available] = (byte) first; 497 available++; 498 if ((available & code_mask) == 0 && available < MaxStackSize) { 499 code_size++; 500 code_mask += available; 501 } 502 old_code = in_code; 503 } 504 505 // Pop a pixel off the pixel stack. 506 507 top--; 508 i++; 509 510 setPixel(xpos, line, pixelStack[top]); 511 ++xpos; 512 if (xpos >= iw) { 513 xpos = 0; 514 line += inc; 515 if (line >= ih) { 516 if (interlace) { 517 do { 518 pass++; 519 switch (pass) { 520 case 2: 521 line = 4; 522 break; 523 case 3: 524 line = 2; 525 inc = 4; 526 break; 527 case 4: 528 line = 1; 529 inc = 2; 530 break; 531 default: // this shouldn't happen 532 line = ih - 1; 533 inc = 0; 534 } 535 } while (line >= ih); 536 } 537 else { 538 line = ih - 1; // this shouldn't happen 539 inc = 0; 540 } 541 } 542 } 543 } 544 return skipZero; 545 } 546 547 548 protected void setPixel(int x, int y, int v) { 549 if (m_bpc == 8) { 550 int pos = x + iw * y; 551 m_out[pos] = (byte)v; 552 } 553 else { 554 int pos = m_line_stride * y + x / (8 / m_bpc); 555 int vout = v << 8 - m_bpc * (x % (8 / m_bpc))- m_bpc; 556 m_out[pos] |= vout; 557 } 558 } 559 560 /** 561 * Resets frame state for reading next image. 562 */ 563 protected void resetFrame() { 564 // it does nothing in the pdf context 565 //boolean transparency = false; 566 //int delay = 0; 567 } 568 569 /** 570 * Reads Graphics Control Extension values 571 */ 572 protected void readGraphicControlExt() throws IOException { 573 in.read(); // block size 574 int packed = in.read(); // packed fields 575 dispose = (packed & 0x1c) >> 2; // disposal method 576 if (dispose == 0) 577 dispose = 1; // elect to keep old image if discretionary 578 transparency = (packed & 1) != 0; 579 delay = readShort() * 10; // delay in milliseconds 580 transIndex = in.read(); // transparent color index 581 in.read(); // block terminator 582 } 583 584 /** 585 * Skips variable length blocks up to and including 586 * next zero length block. 587 */ 588 protected void skip() throws IOException { 589 do { 590 readBlock(); 591 } while (blockSize > 0); 592 } 593 594 static class GifFrame { 595 Image image; 596 int ix; 597 int iy; 598 } 599}