Save This Page
Home » iText-2.1.7 » com.lowagie » text » pdf » codec » [javadoc | source]
    1   /*
    2    * Copyright 2003-2008 by Paulo Soares.
    3    *
    4    * The contents of this file are subject to the Mozilla Public License Version 1.1
    5    * (the "License"); you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at http://www.mozilla.org/MPL/
    7    *
    8    * Software distributed under the License is distributed on an "AS IS" basis,
    9    * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
   10    * for the specific language governing rights and limitations under the License.
   11    *
   12    * The Original Code is 'iText, a free JAVA-PDF library'.
   13    *
   14    * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
   15    * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
   16    * All Rights Reserved.
   17    * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
   18    * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
   19    *
   20    * Contributor(s): all the names of the contributors are added in the source code
   21    * where applicable.
   22    *
   23    * Alternatively, the contents of this file may be used under the terms of the
   24    * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
   25    * provisions of LGPL are applicable instead of those above.  If you wish to
   26    * allow use of your version of this file only under the terms of the LGPL
   27    * License and not to allow others to use your version of this file under
   28    * the MPL, indicate your decision by deleting the provisions above and
   29    * replace them with the notice and other provisions required by the LGPL.
   30    * If you do not delete the provisions above, a recipient may use your version
   31    * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
   32    *
   33    * This library is free software; you can redistribute it and/or modify it
   34    * under the terms of the MPL as stated above or under the terms of the GNU
   35    * Library General Public License as published by the Free Software Foundation;
   36    * either version 2 of the License, or any later version.
   37    *
   38    * This library is distributed in the hope that it will be useful, but WITHOUT
   39    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   40    * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
   41    * details.
   42    *
   43    * If you didn't download this code from the following link, you should check if
   44    * you aren't using an obsolete version:
   45    * http://www.lowagie.com/iText/
   46    *
   47    * This code is based on a series of source files originally released
   48    * by SUN in the context of the JAI project. The original code was released 
   49    * under the BSD license in a specific wording. In a mail dating from
   50    * January 23, 2008, Brian Burkhalter (@sun.com) gave us permission
   51    * to use the code under the following version of the BSD license:
   52    *
   53    * Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.
   54    * 
   55    * Redistribution and use in source and binary forms, with or without
   56    * modification, are permitted provided that the following conditions
   57    * are met: 
   58    * 
   59    * - Redistribution of source code must retain the above copyright 
   60    *   notice, this  list of conditions and the following disclaimer.
   61    * 
   62    * - Redistribution in binary form must reproduce the above copyright
   63    *   notice, this list of conditions and the following disclaimer in 
   64    *   the documentation and/or other materials provided with the
   65    *   distribution.
   66    * 
   67    * Neither the name of Sun Microsystems, Inc. or the names of 
   68    * contributors may be used to endorse or promote products derived 
   69    * from this software without specific prior written permission.
   70    * 
   71    * This software is provided "AS IS," without a warranty of any 
   72    * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
   73    * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
   74    * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
   75    * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
   76    * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
   77    * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
   78    * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
   79    * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
   80    * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
   81    * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
   82    * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
   83    * POSSIBILITY OF SUCH DAMAGES. 
   84    * 
   85    * You acknowledge that this software is not designed or intended for 
   86    * use in the design, construction, operation or maintenance of any 
   87    * nuclear facility.
   88    */
   89   
   90   package com.lowagie.text.pdf.codec;
   91   
   92   import java.awt.color.ICC_Profile;
   93   import java.io.ByteArrayInputStream;
   94   import java.io.ByteArrayOutputStream;
   95   import java.io.DataInputStream;
   96   import java.io.IOException;
   97   import java.io.InputStream;
   98   import java.net.URL;
   99   import java.util.zip.Inflater;
  100   import java.util.zip.InflaterInputStream;
  101   
  102   import com.lowagie.text.ExceptionConverter;
  103   import com.lowagie.text.Image;
  104   import com.lowagie.text.ImgRaw;
  105   import com.lowagie.text.Utilities;
  106   import com.lowagie.text.pdf.ByteBuffer;
  107   import com.lowagie.text.pdf.PdfArray;
  108   import com.lowagie.text.pdf.PdfDictionary;
  109   import com.lowagie.text.pdf.PdfLiteral;
  110   import com.lowagie.text.pdf.PdfName;
  111   import com.lowagie.text.pdf.PdfNumber;
  112   import com.lowagie.text.pdf.PdfObject;
  113   import com.lowagie.text.pdf.PdfReader;
  114   import com.lowagie.text.pdf.PdfString;
  115   
  116   /** Reads a PNG image. All types of PNG can be read.
  117    * <p>
  118    * It is based in part in the JAI codec.
  119    *
  120    * @author  Paulo Soares (psoares@consiste.pt)
  121    */
  122   public class PngImage {
  123   /** Some PNG specific values. */
  124       public static final int[] PNGID = {137, 80, 78, 71, 13, 10, 26, 10};
  125       
  126   /** A PNG marker. */
  127       public static final String IHDR = "IHDR";
  128       
  129   /** A PNG marker. */
  130       public static final String PLTE = "PLTE";
  131       
  132   /** A PNG marker. */
  133       public static final String IDAT = "IDAT";
  134       
  135   /** A PNG marker. */
  136       public static final String IEND = "IEND";
  137       
  138   /** A PNG marker. */
  139       public static final String tRNS = "tRNS";
  140       
  141   /** A PNG marker. */
  142       public static final String pHYs = "pHYs";
  143       
  144   /** A PNG marker. */
  145       public static final String gAMA = "gAMA";
  146       
  147   /** A PNG marker. */
  148       public static final String cHRM = "cHRM";
  149       
  150   /** A PNG marker. */
  151       public static final String sRGB = "sRGB";
  152       
  153   /** A PNG marker. */
  154       public static final String iCCP = "iCCP";
  155       
  156       private static final int TRANSFERSIZE = 4096;
  157       private static final int PNG_FILTER_NONE = 0;
  158       private static final int PNG_FILTER_SUB = 1;
  159       private static final int PNG_FILTER_UP = 2;
  160       private static final int PNG_FILTER_AVERAGE = 3;
  161       private static final int PNG_FILTER_PAETH = 4;
  162       private static final PdfName intents[] = {PdfName.PERCEPTUAL,
  163           PdfName.RELATIVECOLORIMETRIC,PdfName.SATURATION,PdfName.ABSOLUTECOLORIMETRIC};
  164       
  165       InputStream is;
  166       DataInputStream dataStream;
  167       int width;
  168       int height;
  169       int bitDepth;
  170       int colorType;
  171       int compressionMethod;
  172       int filterMethod;
  173       int interlaceMethod;
  174       PdfDictionary additional = new PdfDictionary();
  175       byte image[];
  176       byte smask[];
  177       byte trans[];
  178       NewByteArrayOutputStream idat = new NewByteArrayOutputStream();
  179       int dpiX;
  180       int dpiY;
  181       float XYRatio;
  182       boolean genBWMask;
  183       boolean palShades;
  184       int transRedGray = -1;
  185       int transGreen = -1;
  186       int transBlue = -1;
  187       int inputBands;
  188       int bytesPerPixel; // number of bytes per input pixel
  189       byte colorTable[];
  190       float gamma = 1f;
  191       boolean hasCHRM = false;
  192       float xW, yW, xR, yR, xG, yG, xB, yB;
  193       PdfName intent;
  194       ICC_Profile icc_profile;
  195   
  196       
  197       
  198       /** Creates a new instance of PngImage */
  199       PngImage(InputStream is) {
  200           this.is = is;
  201       }
  202       
  203       /** Reads a PNG from an url.
  204        * @param url the url
  205        * @throws IOException on error
  206        * @return the image
  207        */    
  208       public static Image getImage(URL url) throws IOException {
  209           InputStream is = null;
  210           try {
  211               is = url.openStream();
  212               Image img = getImage(is);
  213               img.setUrl(url);
  214               return img;
  215           }
  216           finally {
  217               if (is != null) {
  218                   is.close();
  219               }
  220           }
  221       }
  222       
  223       /** Reads a PNG from a stream.
  224        * @param is the stream
  225        * @throws IOException on error
  226        * @return the image
  227        */    
  228       public static Image getImage(InputStream is) throws IOException {
  229           PngImage png = new PngImage(is);
  230           return png.getImage();
  231       }
  232       
  233       /** Reads a PNG from a file.
  234        * @param file the file
  235        * @throws IOException on error
  236        * @return the image
  237        */    
  238       public static Image getImage(String file) throws IOException {
  239           return getImage(Utilities.toURL(file));
  240       }
  241       
  242       /** Reads a PNG from a byte array.
  243        * @param data the byte array
  244        * @throws IOException on error
  245        * @return the image
  246        */    
  247       public static Image getImage(byte data[]) throws IOException {
  248           ByteArrayInputStream is = new ByteArrayInputStream(data);
  249           Image img = getImage(is);
  250           img.setOriginalData(data);
  251           return img;
  252       }
  253       
  254       boolean checkMarker(String s) {
  255           if (s.length() != 4)
  256               return false;
  257           for (int k = 0; k < 4; ++k) {
  258               char c = s.charAt(k);
  259               if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z'))
  260                   return false;
  261           }
  262           return true;
  263       }
  264       
  265       void readPng() throws IOException {
  266           for (int i = 0; i < PNGID.length; i++) {
  267               if (PNGID[i] != is.read())	{
  268                   throw new IOException("File is not a valid PNG.");
  269               }
  270           }
  271           byte buffer[] = new byte[TRANSFERSIZE];
  272           while (true) {
  273               int len = getInt(is);
  274               String marker = getString(is);
  275               if (len < 0 || !checkMarker(marker))
  276                   throw new IOException("Corrupted PNG file.");
  277               if (IDAT.equals(marker)) {
  278                   int size;
  279                   while (len != 0) {
  280                       size = is.read(buffer, 0, Math.min(len, TRANSFERSIZE));
  281                       if (size < 0)
  282                           return;
  283                       idat.write(buffer, 0, size);
  284                       len -= size;
  285                   }
  286               }
  287               else if (tRNS.equals(marker)) {
  288                   switch (colorType) {
  289                       case 0:
  290                           if (len >= 2) {
  291                               len -= 2;
  292                               int gray = getWord(is);
  293                               if (bitDepth == 16)
  294                                   transRedGray = gray;
  295                               else
  296                                   additional.put(PdfName.MASK, new PdfLiteral("["+gray+" "+gray+"]"));
  297                           }
  298                           break;
  299                       case 2:
  300                           if (len >= 6) {
  301                               len -= 6;
  302                               int red = getWord(is);
  303                               int green = getWord(is);
  304                               int blue = getWord(is);
  305                               if (bitDepth == 16) {
  306                                   transRedGray = red;
  307                                   transGreen = green;
  308                                   transBlue = blue;
  309                               }
  310                               else
  311                                   additional.put(PdfName.MASK, new PdfLiteral("["+red+" "+red+" "+green+" "+green+" "+blue+" "+blue+"]"));
  312                           }
  313                           break;
  314                       case 3:
  315                           if (len > 0) {
  316                               trans = new byte[len];
  317                               for (int k = 0; k < len; ++k)
  318                                   trans[k] = (byte)is.read();
  319                               len = 0;
  320                           }
  321                           break;
  322                   }
  323                   Utilities.skip(is, len);
  324               }
  325               else if (IHDR.equals(marker)) {
  326                   width = getInt(is);
  327                   height = getInt(is);
  328                   
  329                   bitDepth = is.read();
  330                   colorType = is.read();
  331                   compressionMethod = is.read();
  332                   filterMethod = is.read();
  333                   interlaceMethod = is.read();
  334               }
  335               else if (PLTE.equals(marker)) {
  336                   if (colorType == 3) {
  337                       PdfArray colorspace = new PdfArray();
  338                       colorspace.add(PdfName.INDEXED);
  339                       colorspace.add(getColorspace());
  340                       colorspace.add(new PdfNumber(len / 3 - 1));
  341                       ByteBuffer colortable = new ByteBuffer();
  342                       while ((len--) > 0) {
  343                           colortable.append_i(is.read());
  344                       }
  345                       colorspace.add(new PdfString(colorTable = colortable.toByteArray()));
  346                       additional.put(PdfName.COLORSPACE, colorspace);
  347                   }
  348                   else {
  349                       Utilities.skip(is, len);
  350                   }
  351               }
  352               else if (pHYs.equals(marker)) {
  353                   int dx = getInt(is);
  354                   int dy = getInt(is);
  355                   int unit = is.read();
  356                   if (unit == 1) {
  357                       dpiX = (int)(dx * 0.0254f + 0.5f);
  358                       dpiY = (int)(dy * 0.0254f + 0.5f);
  359                   }
  360                   else {
  361                       if (dy != 0)
  362                           XYRatio = (float)dx / (float)dy;
  363                   }
  364               }
  365               else if (cHRM.equals(marker)) {
  366                   xW = getInt(is) / 100000f;
  367                   yW = getInt(is) / 100000f;
  368                   xR = getInt(is) / 100000f;
  369                   yR = getInt(is) / 100000f;
  370                   xG = getInt(is) / 100000f;
  371                   yG = getInt(is) / 100000f;
  372                   xB = getInt(is) / 100000f;
  373                   yB = getInt(is) / 100000f;
  374                   hasCHRM = !(Math.abs(xW)<0.0001f||Math.abs(yW)<0.0001f||Math.abs(xR)<0.0001f||Math.abs(yR)<0.0001f||Math.abs(xG)<0.0001f||Math.abs(yG)<0.0001f||Math.abs(xB)<0.0001f||Math.abs(yB)<0.0001f);
  375               }
  376               else if (sRGB.equals(marker)) {
  377                   int ri = is.read();
  378                   intent = intents[ri];
  379                   gamma = 2.2f;
  380                   xW = 0.3127f;
  381                   yW = 0.329f;
  382                   xR = 0.64f;
  383                   yR = 0.33f;
  384                   xG = 0.3f;
  385                   yG = 0.6f;
  386                   xB = 0.15f;
  387                   yB = 0.06f;
  388                   hasCHRM = true;
  389               }
  390               else if (gAMA.equals(marker)) {
  391                   int gm = getInt(is);
  392                   if (gm != 0) {
  393                       gamma = 100000f / gm;
  394                       if (!hasCHRM) {
  395                           xW = 0.3127f;
  396                           yW = 0.329f;
  397                           xR = 0.64f;
  398                           yR = 0.33f;
  399                           xG = 0.3f;
  400                           yG = 0.6f;
  401                           xB = 0.15f;
  402                           yB = 0.06f;
  403                           hasCHRM = true;
  404                       }
  405                   }
  406               }
  407               else if (iCCP.equals(marker)) {
  408                   do {
  409                       --len;
  410                   } while (is.read() != 0);
  411                   is.read();
  412                   --len;
  413                   byte icccom[] = new byte[len];
  414                   int p = 0;
  415                   while (len > 0) {
  416                       int r = is.read(icccom, p, len);
  417                       if (r < 0)
  418                           throw new IOException("Premature end of file.");
  419                       p += r;
  420                       len -= r;
  421                   }
  422                   byte iccp[] = PdfReader.FlateDecode(icccom, true);
  423                   icccom = null;
  424                   try {
  425                       icc_profile = ICC_Profile.getInstance(iccp);
  426                   }
  427                   catch (RuntimeException e) {
  428                       icc_profile = null;
  429                   }
  430               }
  431               else if (IEND.equals(marker)) {
  432                   break;
  433               }
  434               else {
  435                   Utilities.skip(is, len);
  436               }
  437               Utilities.skip(is, 4);
  438           }
  439       }
  440       
  441       PdfObject getColorspace() {
  442           if (icc_profile != null) {
  443               if ((colorType & 2) == 0)
  444                   return PdfName.DEVICEGRAY;
  445               else
  446                   return PdfName.DEVICERGB;
  447           }
  448           if (gamma == 1f && !hasCHRM) {
  449               if ((colorType & 2) == 0)
  450                   return PdfName.DEVICEGRAY;
  451               else
  452                   return PdfName.DEVICERGB;
  453           }
  454           else {
  455               PdfArray array = new PdfArray();
  456               PdfDictionary dic = new PdfDictionary();
  457               if ((colorType & 2) == 0) {
  458                   if (gamma == 1f)
  459                       return PdfName.DEVICEGRAY;
  460                   array.add(PdfName.CALGRAY);
  461                   dic.put(PdfName.GAMMA, new PdfNumber(gamma));
  462                   dic.put(PdfName.WHITEPOINT, new PdfLiteral("[1 1 1]"));
  463                   array.add(dic);
  464               }
  465               else {
  466                   PdfObject wp = new PdfLiteral("[1 1 1]");
  467                   array.add(PdfName.CALRGB);
  468                   if (gamma != 1f) {
  469                       PdfArray gm = new PdfArray();
  470                       PdfNumber n = new PdfNumber(gamma);
  471                       gm.add(n);
  472                       gm.add(n);
  473                       gm.add(n);
  474                       dic.put(PdfName.GAMMA, gm);
  475                   }
  476                   if (hasCHRM) {
  477                       float z = yW*((xG-xB)*yR-(xR-xB)*yG+(xR-xG)*yB);
  478                       float YA = yR*((xG-xB)*yW-(xW-xB)*yG+(xW-xG)*yB)/z;
  479                       float XA = YA*xR/yR;
  480                       float ZA = YA*((1-xR)/yR-1);
  481                       float YB = -yG*((xR-xB)*yW-(xW-xB)*yR+(xW-xR)*yB)/z;
  482                       float XB = YB*xG/yG;
  483                       float ZB = YB*((1-xG)/yG-1);
  484                       float YC = yB*((xR-xG)*yW-(xW-xG)*yW+(xW-xR)*yG)/z;
  485                       float XC = YC*xB/yB;
  486                       float ZC = YC*((1-xB)/yB-1);
  487                       float XW = XA+XB+XC;
  488                       float YW = 1;//YA+YB+YC;
  489                       float ZW = ZA+ZB+ZC;
  490                       PdfArray wpa = new PdfArray();
  491                       wpa.add(new PdfNumber(XW));
  492                       wpa.add(new PdfNumber(YW));
  493                       wpa.add(new PdfNumber(ZW));
  494                       wp = wpa;
  495                       PdfArray matrix = new PdfArray();
  496                       matrix.add(new PdfNumber(XA));
  497                       matrix.add(new PdfNumber(YA));
  498                       matrix.add(new PdfNumber(ZA));
  499                       matrix.add(new PdfNumber(XB));
  500                       matrix.add(new PdfNumber(YB));
  501                       matrix.add(new PdfNumber(ZB));
  502                       matrix.add(new PdfNumber(XC));
  503                       matrix.add(new PdfNumber(YC));
  504                       matrix.add(new PdfNumber(ZC));
  505                       dic.put(PdfName.MATRIX, matrix);
  506                   }
  507                   dic.put(PdfName.WHITEPOINT, wp);
  508                   array.add(dic);
  509               }
  510               return array;
  511           }
  512       }
  513       
  514       Image getImage() throws IOException {
  515           readPng();
  516           try {
  517               int pal0 = 0;
  518               int palIdx = 0;
  519               palShades = false;
  520               if (trans != null) {
  521                   for (int k = 0; k < trans.length; ++k) {
  522                       int n = trans[k] & 0xff;
  523                       if (n == 0) {
  524                           ++pal0;
  525                           palIdx = k;
  526                       }
  527                       if (n != 0 && n != 255) {
  528                           palShades = true;
  529                           break;
  530                       }
  531                   }
  532               }
  533               if ((colorType & 4) != 0)
  534                   palShades = true;
  535               genBWMask = (!palShades && (pal0 > 1 || transRedGray >= 0));
  536               if (!palShades && !genBWMask && pal0 == 1) {
  537                   additional.put(PdfName.MASK, new PdfLiteral("["+palIdx+" "+palIdx+"]"));
  538               }
  539               boolean needDecode = (interlaceMethod == 1) || (bitDepth == 16) || ((colorType & 4) != 0) || palShades || genBWMask;
  540               switch (colorType) {
  541                   case 0:
  542                       inputBands = 1;
  543                       break;
  544                   case 2:
  545                       inputBands = 3;
  546                       break;
  547                   case 3:
  548                       inputBands = 1;
  549                       break;
  550                   case 4:
  551                       inputBands = 2;
  552                       break;
  553                   case 6:
  554                       inputBands = 4;
  555                       break;
  556               }
  557               if (needDecode)
  558                   decodeIdat();
  559               int components = inputBands;
  560               if ((colorType & 4) != 0)
  561                   --components;
  562               int bpc = bitDepth;
  563               if (bpc == 16)
  564                   bpc = 8;
  565               Image img;
  566               if (image != null) {
  567                   if (colorType == 3)
  568                       img = new ImgRaw(width, height, components, bpc, image);
  569                   else
  570                       img = Image.getInstance(width, height, components, bpc, image);
  571               }
  572               else {
  573                   img = new ImgRaw(width, height, components, bpc, idat.toByteArray());
  574                   img.setDeflated(true);
  575                   PdfDictionary decodeparms = new PdfDictionary();
  576                   decodeparms.put(PdfName.BITSPERCOMPONENT, new PdfNumber(bitDepth));
  577                   decodeparms.put(PdfName.PREDICTOR, new PdfNumber(15));
  578                   decodeparms.put(PdfName.COLUMNS, new PdfNumber(width));
  579                   decodeparms.put(PdfName.COLORS, new PdfNumber((colorType == 3 || (colorType & 2) == 0) ? 1 : 3));
  580                   additional.put(PdfName.DECODEPARMS, decodeparms);
  581               }
  582               if (additional.get(PdfName.COLORSPACE) == null)
  583                   additional.put(PdfName.COLORSPACE, getColorspace());
  584               if (intent != null)
  585                   additional.put(PdfName.INTENT, intent);
  586               if (additional.size() > 0)
  587                   img.setAdditional(additional);
  588               if (icc_profile != null)
  589                   img.tagICC(icc_profile);
  590               if (palShades) {
  591                   Image im2 = Image.getInstance(width, height, 1, 8, smask);
  592                   im2.makeMask();
  593                   img.setImageMask(im2);
  594               }
  595               if (genBWMask) {
  596                   Image im2 = Image.getInstance(width, height, 1, 1, smask);
  597                   im2.makeMask();
  598                   img.setImageMask(im2);
  599               }
  600               img.setDpi(dpiX, dpiY);
  601               img.setXYRatio(XYRatio);
  602               img.setOriginalType(Image.ORIGINAL_PNG);
  603               return img;
  604           }
  605           catch (Exception e) {
  606               throw new ExceptionConverter(e);
  607           }
  608       }
  609       
  610       void decodeIdat() {
  611           int nbitDepth = bitDepth;
  612           if (nbitDepth == 16)
  613               nbitDepth = 8;
  614           int size = -1;
  615           bytesPerPixel = (bitDepth == 16) ? 2 : 1;
  616           switch (colorType) {
  617               case 0:
  618                   size = (nbitDepth * width + 7) / 8 * height;
  619                   break;
  620               case 2:
  621                   size = width * 3 * height;
  622                   bytesPerPixel *= 3;
  623                   break;
  624               case 3:
  625                   if (interlaceMethod == 1)
  626                       size = (nbitDepth * width + 7) / 8 * height;
  627                   bytesPerPixel = 1;
  628                   break;
  629               case 4:
  630                   size = width * height;
  631                   bytesPerPixel *= 2;
  632                   break;
  633               case 6:
  634                   size = width * 3 * height;
  635                   bytesPerPixel *= 4;
  636                   break;
  637           }
  638           if (size >= 0)
  639               image = new byte[size];
  640           if (palShades)
  641               smask = new byte[width * height];
  642           else if (genBWMask)
  643               smask = new byte[(width + 7) / 8 * height];
  644           ByteArrayInputStream bai = new ByteArrayInputStream(idat.getBuf(), 0, idat.size());
  645           InputStream infStream = new InflaterInputStream(bai, new Inflater());
  646           dataStream = new DataInputStream(infStream);
  647           
  648           if (interlaceMethod != 1) {
  649               decodePass(0, 0, 1, 1, width, height);
  650           }
  651           else {
  652               decodePass(0, 0, 8, 8, (width + 7)/8, (height + 7)/8);
  653               decodePass(4, 0, 8, 8, (width + 3)/8, (height + 7)/8);
  654               decodePass(0, 4, 4, 8, (width + 3)/4, (height + 3)/8);
  655               decodePass(2, 0, 4, 4, (width + 1)/4, (height + 3)/4);
  656               decodePass(0, 2, 2, 4, (width + 1)/2, (height + 1)/4);
  657               decodePass(1, 0, 2, 2, width/2, (height + 1)/2);
  658               decodePass(0, 1, 1, 2, width, height/2);
  659           }
  660           
  661       }
  662       
  663       void decodePass( int xOffset, int yOffset,
  664       int xStep, int yStep,
  665       int passWidth, int passHeight) {
  666           if ((passWidth == 0) || (passHeight == 0)) {
  667               return;
  668           }
  669           
  670           int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8;
  671           byte[] curr = new byte[bytesPerRow];
  672           byte[] prior = new byte[bytesPerRow];
  673           
  674           // Decode the (sub)image row-by-row
  675           int srcY, dstY;
  676           for (srcY = 0, dstY = yOffset;
  677           srcY < passHeight;
  678           srcY++, dstY += yStep) {
  679               // Read the filter type byte and a row of data
  680               int filter = 0;
  681               try {
  682                   filter = dataStream.read();
  683                   dataStream.readFully(curr, 0, bytesPerRow);
  684               } catch (Exception e) {
  685                   // empty on purpose
  686               }
  687               
  688               switch (filter) {
  689                   case PNG_FILTER_NONE:
  690                       break;
  691                   case PNG_FILTER_SUB:
  692                       decodeSubFilter(curr, bytesPerRow, bytesPerPixel);
  693                       break;
  694                   case PNG_FILTER_UP:
  695                       decodeUpFilter(curr, prior, bytesPerRow);
  696                       break;
  697                   case PNG_FILTER_AVERAGE:
  698                       decodeAverageFilter(curr, prior, bytesPerRow, bytesPerPixel);
  699                       break;
  700                   case PNG_FILTER_PAETH:
  701                       decodePaethFilter(curr, prior, bytesPerRow, bytesPerPixel);
  702                       break;
  703                   default:
  704                       // Error -- uknown filter type
  705                       throw new RuntimeException("PNG filter unknown.");
  706               }
  707               
  708               processPixels(curr, xOffset, xStep, dstY, passWidth);
  709               
  710               // Swap curr and prior
  711               byte[] tmp = prior;
  712               prior = curr;
  713               curr = tmp;
  714           }
  715       }
  716       
  717       void processPixels(byte curr[], int xOffset, int step, int y, int width) {
  718           int srcX, dstX;
  719   
  720           int out[] = getPixel(curr);
  721           int sizes = 0;
  722           switch (colorType) {
  723               case 0:
  724               case 3:
  725               case 4:
  726                   sizes = 1;
  727                   break;
  728               case 2:
  729               case 6:
  730                   sizes = 3;
  731                   break;
  732           }
  733           if (image != null) {
  734               dstX = xOffset;
  735               int yStride = (sizes*this.width*(bitDepth == 16 ? 8 : bitDepth)+ 7)/8;
  736               for (srcX = 0; srcX < width; srcX++) {
  737                   setPixel(image, out, inputBands * srcX, sizes, dstX, y, bitDepth, yStride);
  738                   dstX += step;
  739               }
  740           }
  741           if (palShades) {
  742               if ((colorType & 4) != 0) {
  743                   if (bitDepth == 16) {
  744                       for (int k = 0; k < width; ++k)
  745                           out[k * inputBands + sizes] >>>= 8;
  746                   }
  747                   int yStride = this.width;
  748                   dstX = xOffset;
  749                   for (srcX = 0; srcX < width; srcX++) {
  750                       setPixel(smask, out, inputBands * srcX + sizes, 1, dstX, y, 8, yStride);
  751                       dstX += step;
  752                   }
  753               }
  754               else { //colorType 3
  755                   int yStride = this.width;
  756                   int v[] = new int[1];
  757                   dstX = xOffset;
  758                   for (srcX = 0; srcX < width; srcX++) {
  759                       int idx = out[srcX];
  760                       if (idx < trans.length)
  761                           v[0] = trans[idx];
  762                       else
  763                       	v[0] = 255; // Patrick Valsecchi
  764                       setPixel(smask, v, 0, 1, dstX, y, 8, yStride);
  765                       dstX += step;
  766                   }
  767               }
  768           }
  769           else if (genBWMask) {
  770               switch (colorType) {
  771                   case 3: {
  772                       int yStride = (this.width + 7) / 8;
  773                       int v[] = new int[1];
  774                       dstX = xOffset;
  775                       for (srcX = 0; srcX < width; srcX++) {
  776                           int idx = out[srcX];
  777                           v[0] = ((idx < trans.length && trans[idx] == 0) ? 1 : 0);
  778                           setPixel(smask, v, 0, 1, dstX, y, 1, yStride);
  779                           dstX += step;
  780                       }
  781                       break;
  782                   }
  783                   case 0: {
  784                       int yStride = (this.width + 7) / 8;
  785                       int v[] = new int[1];
  786                       dstX = xOffset;
  787                       for (srcX = 0; srcX < width; srcX++) {
  788                           int g = out[srcX];
  789                           v[0] = (g == transRedGray ? 1 : 0);
  790                           setPixel(smask, v, 0, 1, dstX, y, 1, yStride);
  791                           dstX += step;
  792                       }
  793                       break;
  794                   }
  795                   case 2: {
  796                       int yStride = (this.width + 7) / 8;
  797                       int v[] = new int[1];
  798                       dstX = xOffset;
  799                       for (srcX = 0; srcX < width; srcX++) {
  800                           int markRed = inputBands * srcX;
  801                           v[0] = (out[markRed] == transRedGray && out[markRed + 1] == transGreen 
  802                               && out[markRed + 2] == transBlue ? 1 : 0);
  803                           setPixel(smask, v, 0, 1, dstX, y, 1, yStride);
  804                           dstX += step;
  805                       }
  806                       break;
  807                   }
  808               }
  809           }
  810       }
  811       
  812       static int getPixel(byte image[], int x, int y, int bitDepth, int bytesPerRow) {
  813           if (bitDepth == 8) {
  814               int pos = bytesPerRow * y + x;
  815               return image[pos] & 0xff;
  816           }
  817           else {
  818               int pos = bytesPerRow * y + x / (8 / bitDepth);
  819               int v = image[pos] >> (8 - bitDepth * (x % (8 / bitDepth))- bitDepth);
  820               return v & ((1 << bitDepth) - 1);
  821           }
  822       }
  823       
  824       static void setPixel(byte image[], int data[], int offset, int size, int x, int y, int bitDepth, int bytesPerRow) {
  825           if (bitDepth == 8) {
  826               int pos = bytesPerRow * y + size * x;
  827               for (int k = 0; k < size; ++k)
  828                   image[pos + k] = (byte)data[k + offset];
  829           }
  830           else if (bitDepth == 16) {
  831               int pos = bytesPerRow * y + size * x;
  832               for (int k = 0; k < size; ++k)
  833                   image[pos + k] = (byte)(data[k + offset] >>> 8);
  834           }
  835           else {
  836               int pos = bytesPerRow * y + x / (8 / bitDepth);
  837               int v = data[offset] << (8 - bitDepth * (x % (8 / bitDepth))- bitDepth);
  838               image[pos] |= v;
  839           }
  840       }
  841       
  842       int[] getPixel(byte curr[]) {
  843           switch (bitDepth) {
  844               case 8: {
  845                   int out[] = new int[curr.length];
  846                   for (int k = 0; k < out.length; ++k)
  847                       out[k] = curr[k] & 0xff;
  848                   return out;
  849               }
  850               case 16: {
  851                   int out[] = new int[curr.length / 2];
  852                   for (int k = 0; k < out.length; ++k)
  853                       out[k] = ((curr[k * 2] & 0xff) << 8) + (curr[k * 2 + 1] & 0xff);
  854                   return out;
  855               }
  856               default: {
  857                   int out[] = new int[curr.length * 8 / bitDepth];
  858                   int idx = 0;
  859                   int passes = 8 / bitDepth;
  860                   int mask = (1 << bitDepth) - 1;
  861                   for (int k = 0; k < curr.length; ++k) {
  862                       for (int j = passes - 1; j >= 0; --j) {
  863                           out[idx++] = (curr[k] >>> (bitDepth * j)) & mask; 
  864                       }
  865                   }
  866                   return out;
  867               }
  868           }
  869       }
  870       
  871       private static void decodeSubFilter(byte[] curr, int count, int bpp) {
  872           for (int i = bpp; i < count; i++) {
  873               int val;
  874               
  875               val = curr[i] & 0xff;
  876               val += curr[i - bpp] & 0xff;
  877               
  878               curr[i] = (byte)val;
  879           }
  880       }
  881       
  882       private static void decodeUpFilter(byte[] curr, byte[] prev,
  883       int count) {
  884           for (int i = 0; i < count; i++) {
  885               int raw = curr[i] & 0xff;
  886               int prior = prev[i] & 0xff;
  887               
  888               curr[i] = (byte)(raw + prior);
  889           }
  890       }
  891       
  892       private static void decodeAverageFilter(byte[] curr, byte[] prev,
  893       int count, int bpp) {
  894           int raw, priorPixel, priorRow;
  895           
  896           for (int i = 0; i < bpp; i++) {
  897               raw = curr[i] & 0xff;
  898               priorRow = prev[i] & 0xff;
  899               
  900               curr[i] = (byte)(raw + priorRow/2);
  901           }
  902           
  903           for (int i = bpp; i < count; i++) {
  904               raw = curr[i] & 0xff;
  905               priorPixel = curr[i - bpp] & 0xff;
  906               priorRow = prev[i] & 0xff;
  907               
  908               curr[i] = (byte)(raw + (priorPixel + priorRow)/2);
  909           }
  910       }
  911       
  912       private static int paethPredictor(int a, int b, int c) {
  913           int p = a + b - c;
  914           int pa = Math.abs(p - a);
  915           int pb = Math.abs(p - b);
  916           int pc = Math.abs(p - c);
  917           
  918           if ((pa <= pb) && (pa <= pc)) {
  919               return a;
  920           } else if (pb <= pc) {
  921               return b;
  922           } else {
  923               return c;
  924           }
  925       }
  926       
  927       private static void decodePaethFilter(byte[] curr, byte[] prev,
  928       int count, int bpp) {
  929           int raw, priorPixel, priorRow, priorRowPixel;
  930           
  931           for (int i = 0; i < bpp; i++) {
  932               raw = curr[i] & 0xff;
  933               priorRow = prev[i] & 0xff;
  934               
  935               curr[i] = (byte)(raw + priorRow);
  936           }
  937           
  938           for (int i = bpp; i < count; i++) {
  939               raw = curr[i] & 0xff;
  940               priorPixel = curr[i - bpp] & 0xff;
  941               priorRow = prev[i] & 0xff;
  942               priorRowPixel = prev[i - bpp] & 0xff;
  943               
  944               curr[i] = (byte)(raw + paethPredictor(priorPixel,
  945               priorRow,
  946               priorRowPixel));
  947           }
  948       }
  949       
  950       static class NewByteArrayOutputStream extends ByteArrayOutputStream {
  951           public byte[] getBuf() {
  952               return buf;
  953           }
  954       }
  955   
  956   /**
  957    * Gets an <CODE>int</CODE> from an <CODE>InputStream</CODE>.
  958    *
  959    * @param		is      an <CODE>InputStream</CODE>
  960    * @return		the value of an <CODE>int</CODE>
  961    */
  962       
  963       public static final int getInt(InputStream is) throws IOException {
  964           return (is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read();
  965       }
  966       
  967   /**
  968    * Gets a <CODE>word</CODE> from an <CODE>InputStream</CODE>.
  969    *
  970    * @param		is      an <CODE>InputStream</CODE>
  971    * @return		the value of an <CODE>int</CODE>
  972    */
  973       
  974       public static final int getWord(InputStream is) throws IOException {
  975           return (is.read() << 8) + is.read();
  976       }
  977       
  978   /**
  979    * Gets a <CODE>String</CODE> from an <CODE>InputStream</CODE>.
  980    *
  981    * @param		is      an <CODE>InputStream</CODE>
  982    * @return		the value of an <CODE>int</CODE>
  983    */
  984       
  985       public static final String getString(InputStream is) throws IOException {
  986           StringBuffer buf = new StringBuffer();
  987           for (int i = 0; i < 4; i++) {
  988               buf.append((char)is.read());
  989           }
  990           return buf.toString();
  991       }
  992   
  993   }

Save This Page
Home » iText-2.1.7 » com.lowagie » text » pdf » codec » [javadoc | source]