Return-Path: X-Original-To: apmail-pdfbox-commits-archive@www.apache.org Delivered-To: apmail-pdfbox-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 01DF7102C9 for ; Sun, 1 Jun 2014 12:31:33 +0000 (UTC) Received: (qmail 26628 invoked by uid 500); 1 Jun 2014 12:31:33 -0000 Delivered-To: apmail-pdfbox-commits-archive@pdfbox.apache.org Received: (qmail 26607 invoked by uid 500); 1 Jun 2014 12:31:33 -0000 Mailing-List: contact commits-help@pdfbox.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@pdfbox.apache.org Delivered-To: mailing list commits@pdfbox.apache.org Received: (qmail 26600 invoked by uid 99); 1 Jun 2014 12:31:33 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 01 Jun 2014 12:31:33 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 01 Jun 2014 12:31:31 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 8B4CB238889B; Sun, 1 Jun 2014 12:31:05 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1598975 - /pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java Date: Sun, 01 Jun 2014 12:31:05 -0000 To: commits@pdfbox.apache.org From: tilman@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20140601123105.8B4CB238889B@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: tilman Date: Sun Jun 1 12:31:05 2014 New Revision: 1598975 URL: http://svn.apache.org/r1598975 Log: PDFBOX-2106: png suffix for RLE encoding Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java?rev=1598975&r1=1598974&r2=1598975&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java Sun Jun 1 12:31:05 2014 @@ -1,477 +1,486 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.pdfbox.pdmodel.graphics.image; - -import org.apache.pdfbox.cos.COSArray; -import org.apache.pdfbox.cos.COSBase; -import org.apache.pdfbox.cos.COSName; -import org.apache.pdfbox.cos.COSStream; -import org.apache.pdfbox.filter.DecodeResult; -import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.pdmodel.common.PDMetadata; -import org.apache.pdfbox.pdmodel.common.PDStream; -import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; -import org.apache.pdfbox.pdmodel.graphics.PDXObject; -import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceGray; - -import java.awt.Graphics2D; -import java.awt.Paint; -import java.awt.image.BufferedImage; -import java.awt.image.WritableRaster; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import java.util.Map; - -/** - * An Image XObject. - * - * @author John Hewson - * @author Ben Litchfield - */ -public final class PDImageXObject extends PDXObject implements PDImage -{ - private BufferedImage cachedImage; - private PDColorSpace colorSpace; - private Map colorSpaces; // from current resource dictionary - - /** - * Creates a thumbnail Image XObject from the given COSBase and name. - * @param cosStream the COS stream - * @return an XObject - * @throws IOException if there is an error creating the XObject. - */ - public static PDImageXObject createThumbnail(COSStream cosStream) throws IOException - { - // thumbnails are special, any non-null subtype is treated as being "Image" - PDStream pdStream = new PDStream(cosStream); - return new PDImageXObject(pdStream, null); - } - - /** - * Creates an Image XObject in the given document. - * @param document the current document - * @throws java.io.IOException if there is an error creating the XObject. - */ - public PDImageXObject(PDDocument document) throws IOException - { - this(new PDStream(document), null); - } - - /** - * Creates an Image XObject in the given document using the given filtered stream. - * @param document the current document - * @param filteredStream a filtered stream of image data - * @param cosFilter the filter or a COSArray of filters - * @throws IOException if there is an error creating the XObject. - */ - public PDImageXObject(PDDocument document, InputStream filteredStream, COSBase cosFilter) throws IOException - { - super(new PDStream(document, filteredStream, true), COSName.IMAGE); - getCOSStream().setItem(COSName.FILTER, cosFilter); - colorSpaces = null; - colorSpace = null; - } - - /** - * Creates an Image XObject with the given stream as its contents and current color spaces. - * @param stream the XObject stream to read - * @param colorSpaces the color spaces in the current resources dictionary, null for masks - * @throws java.io.IOException if there is an error creating the XObject. - */ - public PDImageXObject(PDStream stream, Map colorSpaces) throws IOException - { - this(stream, colorSpaces, stream.getStream().getDecodeResult()); - } - - // repairs parameters using decode result - private PDImageXObject(PDStream stream, Map colorSpaces, - DecodeResult decodeResult) - { - super(repair(stream, decodeResult), COSName.IMAGE); - this.colorSpaces = colorSpaces; - this.colorSpace = decodeResult.getJPXColorSpace(); - } - - // repairs parameters using decode result - private static PDStream repair(PDStream stream, DecodeResult decodeResult) - { - stream.getStream().addAll(decodeResult.getParameters()); - return stream; - } - - /** - * Returns the metadata associated with this XObject, or null if there is none. - * @return the metadata associated with this object. - */ - public PDMetadata getMetadata() - { - COSStream cosStream = (COSStream) getCOSStream().getDictionaryObject(COSName.METADATA); - if (cosStream != null) - { - return new PDMetadata(cosStream); - } - return null; - } - - /** - * Sets the metadata associated with this XObject, or null if there is none. - * @param meta the metadata associated with this object - */ - public void setMetadata(PDMetadata meta) - { - getCOSStream().setItem(COSName.METADATA, meta); - } - - /** - * Returns the key of this XObject in the structural parent tree. - * @return this object's key the structural parent tree - */ - public int getStructParent() - { - return getCOSStream().getInt(COSName.STRUCT_PARENT, 0); - } - - /** - * Sets the key of this XObject in the structural parent tree. - * @param key the new key for this XObject - */ - public void setStructParent(int key) - { - getCOSStream().setInt(COSName.STRUCT_PARENT, key); - } - - /** - * {@inheritDoc} - * The returned images are cached for the lifetime of this XObject. - */ - @Override - public BufferedImage getImage() throws IOException - { - if (cachedImage != null) - { - return cachedImage; - } - - // get image as RGB - BufferedImage image = SampledImageReader.getRGBImage(this, getColorKeyMask()); - - // soft mask (overrides explicit mask) - PDImageXObject softMask = getSoftMask(); - if (softMask != null) - { - image = applyMask(image, softMask.getOpaqueImage(), true); - } - else - { - // explicit mask - PDImageXObject mask = getMask(); - if (mask != null) - { - image = applyMask(image, mask.getOpaqueImage(), false); - } - } - - cachedImage = image; - return image; - } - - /** - * {@inheritDoc} - * The returned images are not cached. - */ - @Override - public BufferedImage getStencilImage(Paint paint) throws IOException - { - if (!isStencil()) - { - throw new IllegalStateException("Image is not a stencil"); - } - return SampledImageReader.getStencilImage(this, paint); - } - - /** - * Returns an RGB buffered image containing the opaque image stream without any masks applied. - * If this Image XObject is a mask then the buffered image will contain the raw mask. - * @return the image without any masks applied - * @throws IOException if the image cannot be read - */ - public BufferedImage getOpaqueImage() throws IOException - { - return SampledImageReader.getRGBImage(this, null); - } - - // explicit mask: RGB + Binary -> ARGB - // soft mask: RGB + Gray -> ARGB - private BufferedImage applyMask(BufferedImage image, BufferedImage mask, boolean isSoft) - throws IOException - { - if (mask == null) - { - return image; - } - - int width = image.getWidth(); - int height = image.getHeight(); - - // compose to ARGB - BufferedImage masked = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - - // scale mask to fit image - if (mask.getWidth() != width || mask.getHeight() != height) - { - BufferedImage mask2 = new BufferedImage(width, height, mask.getType()); - Graphics2D g = mask2.createGraphics(); - g.drawImage(mask, 0, 0, width, height, 0, 0, mask.getWidth(), mask.getHeight(), null); - g.dispose(); - mask = mask2; - } - - WritableRaster src = image.getRaster(); - WritableRaster dest = masked.getRaster(); - WritableRaster alpha = mask.getRaster(); - - float[] rgb = new float[3]; - float[] rgba = new float[4]; - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - src.getPixel(x, y, rgb); - - rgba[0] = rgb[0]; - rgba[1] = rgb[1]; - rgba[2] = rgb[2]; - - if (isSoft) - { - rgba[3] = alpha.getPixel(x, y, (float[])null)[0]; - } - else - { - rgba[3] = 255 - alpha.getPixel(x, y, (float[])null)[0]; - } - - dest.setPixel(x, y, rgba); - } - } - - return masked; - } - - /** - * Returns the Mask Image XObject associated with this image, or null if there is none. - * @return Mask Image XObject - */ - public PDImageXObject getMask() throws IOException - { - COSBase mask = getCOSStream().getDictionaryObject(COSName.MASK); - if (mask instanceof COSArray) - { - // color key mask, no explicit mask to return - return null; - } - else - { - COSStream cosStream = (COSStream)getCOSStream().getDictionaryObject(COSName.MASK); - if (cosStream != null) - { - return new PDImageXObject(new PDStream(cosStream), null); // always DeviceGray - } - return null; - } - } - - /** - * Returns the color key mask array associated with this image, or null if there is none. - * @return Mask Image XObject - */ - public COSArray getColorKeyMask() - { - COSBase mask = getCOSStream().getDictionaryObject(COSName.MASK); - if (mask instanceof COSArray) - { - return (COSArray)mask; - } - return null; - } - - /** - * Returns the Soft Mask Image XObject associated with this image, or null if there is none. - * @return the SMask Image XObject, or null. - */ - public PDImageXObject getSoftMask() throws IOException - { - COSStream cosStream = (COSStream)getCOSStream().getDictionaryObject(COSName.SMASK); - if (cosStream != null) - { - return new PDImageXObject(new PDStream(cosStream), null); // always DeviceGray - } - return null; - } - - @Override - public int getBitsPerComponent() - { - if (isStencil()) - { - return 1; - } - else - { - return getCOSStream().getInt(COSName.BITS_PER_COMPONENT, COSName.BPC); - } - } - - @Override - public void setBitsPerComponent(int bpc) - { - getCOSStream().setInt(COSName.BITS_PER_COMPONENT, bpc); - } - - @Override - public PDColorSpace getColorSpace() throws IOException - { - if (colorSpace == null) - { - COSBase cosBase = getCOSStream().getDictionaryObject(COSName.COLORSPACE, COSName.CS); - if (cosBase != null) - { - colorSpace = PDColorSpace.create(cosBase, colorSpaces, null); - } - else if (isStencil()) - { - // stencil mask color space must be gray, it is often missing - return PDDeviceGray.INSTANCE; - } - else - { - // an image without a color space is always broken - throw new IOException("could not determine color space"); - } - } - return colorSpace; - } - - @Override - public PDStream getStream() throws IOException - { - return getPDStream(); - } - - @Override - public void setColorSpace(PDColorSpace cs) - { - getCOSStream().setItem(COSName.COLORSPACE, cs != null ? cs.getCOSObject() : null); - } - - @Override - public int getHeight() - { - return getCOSStream().getInt(COSName.HEIGHT); - } - - @Override - public void setHeight(int h) - { - getCOSStream().setInt(COSName.HEIGHT, h); - } - - @Override - public int getWidth() - { - return getCOSStream().getInt(COSName.WIDTH); - } - - @Override - public void setWidth(int w) - { - getCOSStream().setInt(COSName.WIDTH, w); - } - - @Override - public void setDecode(COSArray decode) - { - getCOSStream().setItem(COSName.DECODE, decode); - } - - @Override - public COSArray getDecode() - { - COSBase decode = getCOSStream().getDictionaryObject(COSName.DECODE); - if (decode != null && decode instanceof COSArray) - { - return (COSArray) decode; - } - return null; - } - - @Override - public boolean isStencil() - { - return getCOSStream().getBoolean(COSName.IMAGE_MASK, false); - } - - @Override - public void setStencil(boolean isStencil) - { - getCOSStream().setBoolean(COSName.IMAGE_MASK, isStencil); - } - - /** - * This will get the suffix for this image type, e.g. jpg/png. - * @return The image suffix or null if not available. - */ - public String getSuffix() - { - List filters = getPDStream().getFilters(); - - if (filters == null) - { - return "png"; - } - else if (filters.contains(COSName.DCT_DECODE)) - { - return "jpg"; - } - else if (filters.contains(COSName.JPX_DECODE)) - { - return "jpx"; - } - else if (filters.contains(COSName.CCITTFAX_DECODE)) - { - return "tiff"; - } - else if (filters.contains(COSName.FLATE_DECODE) - || filters.contains(COSName.LZW_DECODE)) - { - return "png"; - } - else - { - // TODO more... - return null; - } - } - - @Override - public void clear() - { - super.clear(); - cachedImage = null; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.pdfbox.pdmodel.graphics.image; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.filter.DecodeResult; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.PDMetadata; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.pdmodel.graphics.PDXObject; +import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceGray; + +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.image.BufferedImage; +import java.awt.image.WritableRaster; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * An Image XObject. + * + * @author John Hewson + * @author Ben Litchfield + */ +public final class PDImageXObject extends PDXObject implements PDImage +{ + /** + * Log instance. + */ + private static final Log LOG = LogFactory.getLog(PDImageXObject.class); + + private BufferedImage cachedImage; + private PDColorSpace colorSpace; + private Map colorSpaces; // from current resource dictionary + + /** + * Creates a thumbnail Image XObject from the given COSBase and name. + * @param cosStream the COS stream + * @return an XObject + * @throws IOException if there is an error creating the XObject. + */ + public static PDImageXObject createThumbnail(COSStream cosStream) throws IOException + { + // thumbnails are special, any non-null subtype is treated as being "Image" + PDStream pdStream = new PDStream(cosStream); + return new PDImageXObject(pdStream, null); + } + + /** + * Creates an Image XObject in the given document. + * @param document the current document + * @throws java.io.IOException if there is an error creating the XObject. + */ + public PDImageXObject(PDDocument document) throws IOException + { + this(new PDStream(document), null); + } + + /** + * Creates an Image XObject in the given document using the given filtered stream. + * @param document the current document + * @param filteredStream a filtered stream of image data + * @param cosFilter the filter or a COSArray of filters + * @throws IOException if there is an error creating the XObject. + */ + public PDImageXObject(PDDocument document, InputStream filteredStream, COSBase cosFilter) throws IOException + { + super(new PDStream(document, filteredStream, true), COSName.IMAGE); + getCOSStream().setItem(COSName.FILTER, cosFilter); + colorSpaces = null; + colorSpace = null; + } + + /** + * Creates an Image XObject with the given stream as its contents and current color spaces. + * @param stream the XObject stream to read + * @param colorSpaces the color spaces in the current resources dictionary, null for masks + * @throws java.io.IOException if there is an error creating the XObject. + */ + public PDImageXObject(PDStream stream, Map colorSpaces) throws IOException + { + this(stream, colorSpaces, stream.getStream().getDecodeResult()); + } + + // repairs parameters using decode result + private PDImageXObject(PDStream stream, Map colorSpaces, + DecodeResult decodeResult) + { + super(repair(stream, decodeResult), COSName.IMAGE); + this.colorSpaces = colorSpaces; + this.colorSpace = decodeResult.getJPXColorSpace(); + } + + // repairs parameters using decode result + private static PDStream repair(PDStream stream, DecodeResult decodeResult) + { + stream.getStream().addAll(decodeResult.getParameters()); + return stream; + } + + /** + * Returns the metadata associated with this XObject, or null if there is none. + * @return the metadata associated with this object. + */ + public PDMetadata getMetadata() + { + COSStream cosStream = (COSStream) getCOSStream().getDictionaryObject(COSName.METADATA); + if (cosStream != null) + { + return new PDMetadata(cosStream); + } + return null; + } + + /** + * Sets the metadata associated with this XObject, or null if there is none. + * @param meta the metadata associated with this object + */ + public void setMetadata(PDMetadata meta) + { + getCOSStream().setItem(COSName.METADATA, meta); + } + + /** + * Returns the key of this XObject in the structural parent tree. + * @return this object's key the structural parent tree + */ + public int getStructParent() + { + return getCOSStream().getInt(COSName.STRUCT_PARENT, 0); + } + + /** + * Sets the key of this XObject in the structural parent tree. + * @param key the new key for this XObject + */ + public void setStructParent(int key) + { + getCOSStream().setInt(COSName.STRUCT_PARENT, key); + } + + /** + * {@inheritDoc} + * The returned images are cached for the lifetime of this XObject. + */ + @Override + public BufferedImage getImage() throws IOException + { + if (cachedImage != null) + { + return cachedImage; + } + + // get image as RGB + BufferedImage image = SampledImageReader.getRGBImage(this, getColorKeyMask()); + + // soft mask (overrides explicit mask) + PDImageXObject softMask = getSoftMask(); + if (softMask != null) + { + image = applyMask(image, softMask.getOpaqueImage(), true); + } + else + { + // explicit mask + PDImageXObject mask = getMask(); + if (mask != null) + { + image = applyMask(image, mask.getOpaqueImage(), false); + } + } + + cachedImage = image; + return image; + } + + /** + * {@inheritDoc} + * The returned images are not cached. + */ + @Override + public BufferedImage getStencilImage(Paint paint) throws IOException + { + if (!isStencil()) + { + throw new IllegalStateException("Image is not a stencil"); + } + return SampledImageReader.getStencilImage(this, paint); + } + + /** + * Returns an RGB buffered image containing the opaque image stream without any masks applied. + * If this Image XObject is a mask then the buffered image will contain the raw mask. + * @return the image without any masks applied + * @throws IOException if the image cannot be read + */ + public BufferedImage getOpaqueImage() throws IOException + { + return SampledImageReader.getRGBImage(this, null); + } + + // explicit mask: RGB + Binary -> ARGB + // soft mask: RGB + Gray -> ARGB + private BufferedImage applyMask(BufferedImage image, BufferedImage mask, boolean isSoft) + throws IOException + { + if (mask == null) + { + return image; + } + + int width = image.getWidth(); + int height = image.getHeight(); + + // compose to ARGB + BufferedImage masked = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + + // scale mask to fit image + if (mask.getWidth() != width || mask.getHeight() != height) + { + BufferedImage mask2 = new BufferedImage(width, height, mask.getType()); + Graphics2D g = mask2.createGraphics(); + g.drawImage(mask, 0, 0, width, height, 0, 0, mask.getWidth(), mask.getHeight(), null); + g.dispose(); + mask = mask2; + } + + WritableRaster src = image.getRaster(); + WritableRaster dest = masked.getRaster(); + WritableRaster alpha = mask.getRaster(); + + float[] rgb = new float[3]; + float[] rgba = new float[4]; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + src.getPixel(x, y, rgb); + + rgba[0] = rgb[0]; + rgba[1] = rgb[1]; + rgba[2] = rgb[2]; + + if (isSoft) + { + rgba[3] = alpha.getPixel(x, y, (float[])null)[0]; + } + else + { + rgba[3] = 255 - alpha.getPixel(x, y, (float[])null)[0]; + } + + dest.setPixel(x, y, rgba); + } + } + + return masked; + } + + /** + * Returns the Mask Image XObject associated with this image, or null if there is none. + * @return Mask Image XObject + */ + public PDImageXObject getMask() throws IOException + { + COSBase mask = getCOSStream().getDictionaryObject(COSName.MASK); + if (mask instanceof COSArray) + { + // color key mask, no explicit mask to return + return null; + } + else + { + COSStream cosStream = (COSStream)getCOSStream().getDictionaryObject(COSName.MASK); + if (cosStream != null) + { + return new PDImageXObject(new PDStream(cosStream), null); // always DeviceGray + } + return null; + } + } + + /** + * Returns the color key mask array associated with this image, or null if there is none. + * @return Mask Image XObject + */ + public COSArray getColorKeyMask() + { + COSBase mask = getCOSStream().getDictionaryObject(COSName.MASK); + if (mask instanceof COSArray) + { + return (COSArray)mask; + } + return null; + } + + /** + * Returns the Soft Mask Image XObject associated with this image, or null if there is none. + * @return the SMask Image XObject, or null. + */ + public PDImageXObject getSoftMask() throws IOException + { + COSStream cosStream = (COSStream)getCOSStream().getDictionaryObject(COSName.SMASK); + if (cosStream != null) + { + return new PDImageXObject(new PDStream(cosStream), null); // always DeviceGray + } + return null; + } + + @Override + public int getBitsPerComponent() + { + if (isStencil()) + { + return 1; + } + else + { + return getCOSStream().getInt(COSName.BITS_PER_COMPONENT, COSName.BPC); + } + } + + @Override + public void setBitsPerComponent(int bpc) + { + getCOSStream().setInt(COSName.BITS_PER_COMPONENT, bpc); + } + + @Override + public PDColorSpace getColorSpace() throws IOException + { + if (colorSpace == null) + { + COSBase cosBase = getCOSStream().getDictionaryObject(COSName.COLORSPACE, COSName.CS); + if (cosBase != null) + { + colorSpace = PDColorSpace.create(cosBase, colorSpaces, null); + } + else if (isStencil()) + { + // stencil mask color space must be gray, it is often missing + return PDDeviceGray.INSTANCE; + } + else + { + // an image without a color space is always broken + throw new IOException("could not determine color space"); + } + } + return colorSpace; + } + + @Override + public PDStream getStream() throws IOException + { + return getPDStream(); + } + + @Override + public void setColorSpace(PDColorSpace cs) + { + getCOSStream().setItem(COSName.COLORSPACE, cs != null ? cs.getCOSObject() : null); + } + + @Override + public int getHeight() + { + return getCOSStream().getInt(COSName.HEIGHT); + } + + @Override + public void setHeight(int h) + { + getCOSStream().setInt(COSName.HEIGHT, h); + } + + @Override + public int getWidth() + { + return getCOSStream().getInt(COSName.WIDTH); + } + + @Override + public void setWidth(int w) + { + getCOSStream().setInt(COSName.WIDTH, w); + } + + @Override + public void setDecode(COSArray decode) + { + getCOSStream().setItem(COSName.DECODE, decode); + } + + @Override + public COSArray getDecode() + { + COSBase decode = getCOSStream().getDictionaryObject(COSName.DECODE); + if (decode != null && decode instanceof COSArray) + { + return (COSArray) decode; + } + return null; + } + + @Override + public boolean isStencil() + { + return getCOSStream().getBoolean(COSName.IMAGE_MASK, false); + } + + @Override + public void setStencil(boolean isStencil) + { + getCOSStream().setBoolean(COSName.IMAGE_MASK, isStencil); + } + + /** + * This will get the suffix for this image type, e.g. jpg/png. + * @return The image suffix or null if not available. + */ + public String getSuffix() + { + List filters = getPDStream().getFilters(); + + if (filters == null) + { + return "png"; + } + else if (filters.contains(COSName.DCT_DECODE)) + { + return "jpg"; + } + else if (filters.contains(COSName.JPX_DECODE)) + { + return "jpx"; + } + else if (filters.contains(COSName.CCITTFAX_DECODE)) + { + return "tiff"; + } + else if (filters.contains(COSName.FLATE_DECODE) + || filters.contains(COSName.LZW_DECODE) + || filters.contains(COSName.RUN_LENGTH_DECODE)) + { + return "png"; + } + else + { + LOG.warn("getSuffix() returns null, filters: " + filters); + // TODO more... + return null; + } + } + + @Override + public void clear() + { + super.clear(); + cachedImage = null; + } +}