Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.idrsolutions.com
* Help section for developers at http://www.idrsolutions.com/support/
*
* (C) Copyright 1997-2017 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---------------
* ImageDecoder.java
* ---------------
*/
package org.jpedal.parser.image;
import com.idrsolutions.pdf.color.shading.BitReader;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.TexturePaint;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import org.jpedal.color.*;
import org.jpedal.exception.PdfException;
import org.jpedal.external.ErrorTracker;
import org.jpedal.external.ImageDataHandler;
import org.jpedal.external.ImageHandler;
import org.jpedal.images.ImageTransformer;
import org.jpedal.images.ImageTransformerDouble;
import org.jpedal.images.SamplingFactory;
import org.jpedal.io.ColorSpaceConvertor;
import org.jpedal.io.ObjectStore;
import org.jpedal.io.PdfObjectReader;
import org.jpedal.objects.PdfImageData;
import org.jpedal.objects.PdfPageData;
import org.jpedal.objects.raw.PdfArrayIterator;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.parser.BaseDecoder;
import org.jpedal.parser.ParserOptions;
import org.jpedal.parser.PdfObjectCache;
import org.jpedal.parser.ValueTypes;
import org.jpedal.parser.image.data.ImageData;
import org.jpedal.parser.image.downsample.DownSampler;
import org.jpedal.parser.image.mask.MaskDataDecoder;
import org.jpedal.parser.image.mask.MaskDecoder;
import org.jpedal.parser.image.mask.SMaskDecoder;
import org.jpedal.parser.image.utils.ConvertMaskToImage;
import org.jpedal.render.SwingDisplay;
import org.jpedal.utils.LogWriter;
public class ImageDecoder extends BaseDecoder {
private boolean cacheLargeImages;
//Allow print to use transparency in printing instead of removing it
public static boolean allowPrintTransparency;
protected ParserOptions parserOptions;
final PdfImageData pdfImages;
private boolean getSamplingOnly;
//flag to show if image transparent*/
boolean isMask = true;
String imagesInFile;
PdfObjectCache cache;
final ImageHandler customImageHandler;
final PdfPageData pageData;
final ObjectStore objectStoreStreamRef;
//name of current image in pdf
String currentImage = "";
final ErrorTracker errorTracker;
final PdfObjectReader currentPdfFile;
//images on page
public final int imageCount;
public ImageDecoder(final int imageCount, final PdfObjectReader currentPdfFile, final ErrorTracker errorTracker, final ImageHandler customImageHandler, final ObjectStore objectStoreStreamRef, final PdfImageData pdfImages, final PdfPageData pageData, final String imagesInFile) {
this.imageCount = imageCount;
this.currentPdfFile = currentPdfFile;
this.errorTracker = errorTracker;
this.customImageHandler = customImageHandler;
this.objectStoreStreamRef = objectStoreStreamRef;
this.pdfImages = pdfImages;
this.pageData = pageData;
this.imagesInFile = imagesInFile;
}
private GenericColorSpace setupXObjectColorspace(final PdfObject XObject, final ImageData imageData) {
final int width = imageData.getWidth();
final int height = imageData.getHeight();
final int depth = imageData.getDepth();
//handle colour information
GenericColorSpace decodeColorData = new DeviceRGBColorSpace();
final PdfArrayIterator ColorSpace = XObject.getMixedArray(PdfDictionary.ColorSpace);
if (ColorSpace.getTokenCount() > 0) { //if not set will be zero
decodeColorData = ColorspaceFactory.getColorSpaceInstance(currentPdfFile, ColorSpace);
}
decodeColorData.setPrinting(parserOptions.isPrinting());
//track colorspace use
cache.put(PdfObjectCache.ColorspacesUsed, decodeColorData.getID(), "x");
//fix for odd itext file (/PDFdata/baseline_screens/debug3/Leistung.pdf)
final byte[] indexData = decodeColorData.getIndexedMap();
if (depth == 8) {
final byte[] objectData = imageData.getObjectData();
if (indexData != null && decodeColorData.getID() == ColorSpaces.DeviceRGB && width * height == objectData.length) {
final PdfObject newMask = XObject.getDictionary(PdfDictionary.Mask);
final int[] maskArray = XObject.getIntArray(PdfDictionary.Mask);
if (newMask != null || maskArray != null) {
//this specific case has all zeros
if (maskArray != null && maskArray.length == 2 && maskArray[0] == 255 && maskArray[0] == maskArray[1] && decodeColorData.getIndexedMap() != null && decodeColorData.getIndexedMap().length == 768) {
//see if index looks corrupt (ie all zeros) We exit as soon as we have disproved
boolean isCorrupt = true;
for (int jj = 0; jj < 768; jj++) {
if (indexData[jj] != 0) {
isCorrupt = false;
jj = 768;
}
}
if (isCorrupt) {
decodeColorData = new DeviceGrayColorSpace();
}
}
}
}
}
return decodeColorData;
}
public BufferedImage processImageXObject(final PdfObject XObject, String image_name, byte[] objectData, final String details) throws PdfException {
BufferedImage image = null;
//add filename to make it unique
image_name = parserOptions.getFileName() + '-' + image_name;
// System.out.println("XObject="+XObject+" "+XObject.getObjectRefAsString());
PdfObject newSMask = XObject.getDictionary(PdfDictionary.SMask);
final PdfObject newMask = XObject.getDictionary(PdfDictionary.Mask);
final int[] maskArray = XObject.getIntArray(PdfDictionary.Mask);
ImageData imageData = new ImageData(XObject, objectData);
imageData.getFilter(XObject);
GenericColorSpace decodeColorData = setupXObjectColorspace(XObject, imageData);
imageData.setCompCount(decodeColorData.getColorSpace().getNumComponents());
/*
* New code to apply SMask and Mask to data
* (note old isMask)
*/
final byte[] convertedData = XObject.getConvertedData();
if (convertedData != null) { //reuse converted mask data
objectData = convertedData;
decodeColorData = new DeviceRGBColorSpace();
//imageData.setObjectData(objectData);
imageData = null;
} else if (newSMask != null || newMask != null || maskArray != null) {
if (newSMask != null && XObject.getInt(PdfDictionary.Width) == 1 && XObject.getInt(PdfDictionary.Height) == 1 && XObject.getInt(PdfDictionary.BitsPerComponent) == 8) { //swap out the image with inverted SMask if empty
//silly case we handle in code below // /baseline_screens/11dec/grayscale.pdf
} else {
//WE NEED TO CONVERT JPG to raw DATA in IMAGE
if (imageData.isDCT()) {
objectData = JPEGDecoder.getBytesFromJPEG(objectData, decodeColorData, XObject);
imageData.setObjectData(objectData);
XObject.setMixedArray(PdfDictionary.Filter, null);
XObject.setDecodedStream(objectData);
} else if (imageData.isJPX()) {
objectData = JPeg2000ImageDecoder.getBytesFromJPEG2000(objectData);
if (decodeColorData.getID() == ColorSpaces.DeviceN) {
objectData = ((DeviceNColorSpace) decodeColorData).getRGBBytes(objectData, imageData.getWidth(), imageData.getHeight());
}
imageData.setObjectData(objectData);
XObject.setMixedArray(PdfDictionary.Filter, null);
XObject.setDecodedStream(objectData);
decodeColorData = new DeviceRGBColorSpace();
}
if (newSMask != null) {
///WE NEED TO CONVERT JPG to raw DATA in smask as well
final ImageData smaskImageData = new ImageData(newSMask, null);
smaskImageData.getFilter(newSMask);
final GenericColorSpace maskColorSpace = setupXObjectColorspace(newSMask, smaskImageData);
byte[] maskData = currentPdfFile.readStream(newSMask, true, true, false, false, false, newSMask.getCacheName(currentPdfFile.getObjectReader()));
if (1 == 1) {
objectData = SMaskDecoder.applyJPX_JBIG_Smask(imageData, smaskImageData, maskData, XObject, newSMask, decodeColorData, maskColorSpace);
} else { // old method
maskData = MaskDataDecoder.getSMaskData(maskData, smaskImageData, newSMask, setupXObjectColorspace(newSMask, smaskImageData));
objectData = SMaskDecoder.applySMask(maskData, imageData, decodeColorData, newSMask, XObject);
}
XObject.setConvertedData(objectData);
} else { //mask
byte[] index = decodeColorData.getIndexedMap();
if (index != null) {
index = decodeColorData.convertIndexToRGB(index);
if (maskArray != null) {
return getIndexedMaskImage(index, imageData, maskArray);
}
objectData = ColorSpaceConvertor.convertIndexToRGBByte(index, imageData.getWidth(), imageData.getHeight(), imageData.getCompCount(), imageData.getDepth(), objectData, false, false);
decodeColorData = new DeviceRGBColorSpace();
imageData.setObjectData(objectData);
decodeColorData.setIndex(null, 0);
// imageData.setCompCount(3);
// imageData.setDepth(8);
}
///WE NEED TO CONVERT JPG to raw DATA in mask as well
final ImageData maskImageData;
if (newMask == null) {
maskImageData = new ImageData(XObject, objectData);
} else {
maskImageData = new ImageData(newMask, objectData);
}
if (maskArray != null) {
return MaskDataDecoder.applyMaskArray(imageData, maskArray);
}
byte[] maskData = currentPdfFile.readStream(newMask, true, true, false, false, false, newMask.getCacheName(currentPdfFile.getObjectReader()));
maskData = MaskDataDecoder.getSMaskData(maskData, maskImageData, newMask, setupXObjectColorspace(newMask, maskImageData));
objectData = MaskDecoder.applyMask(imageData, decodeColorData, newMask, XObject, maskData);
XObject.setConvertedData(objectData);
decodeColorData = new DeviceRGBColorSpace();
XObject.setDictionary(PdfDictionary.Mask, null);
XObject.setIntArray(PdfDictionary.Mask, null);
}
// String dest="/Users/markee/Desktop/deviceRGB/"+org.jpedal.DevFlags.currentFile.substring(org.jpedal.DevFlags.currentFile.lastIndexOf("/"));
// ObjectStore.copy(org.jpedal.DevFlags.currentFile, dest);
//also set SMask to null (will set image to DeviceRGB below
//XObject.setDictionary(PdfDictionary.SMask, null);
imageData = null;
}
}
//reset if changed in Mask/SMask code
if (imageData == null) {
imageData = new ImageData(XObject, objectData);
decodeColorData = new DeviceRGBColorSpace(true); //sets to 4 comp ARGB
imageData.setCompCount(4);
newSMask = null;
}
isMask = XObject.getBoolean(PdfDictionary.ImageMask);
LogWriter.writeLog("Processing XObject: " + image_name + ' ' + XObject.getObjectRefAsString() + " width=" + imageData.getWidth() + " Height=" + imageData.getHeight() +
" Depth=" + imageData.getDepth() + " colorspace=" + decodeColorData);
//allow user to process image
if (customImageHandler != null && !(customImageHandler instanceof ImageDataHandler)) {
image = customImageHandler.processImageData(gs, XObject); //user gets raw JPEG data
}
//deal with special case of 1x1 pixel backed onto large inverted Smask which would be very slow in Generic code
//see (11dec/grayscale.pdf)
if (newSMask != null && XObject.getInt(PdfDictionary.Width) == 1 && XObject.getInt(PdfDictionary.Height) == 1 && XObject.getInt(PdfDictionary.BitsPerComponent) == 8) { //swap out the image with inverted SMask if empty
image = ConvertMaskToImage.convert(newSMask, currentPdfFile);
} else if (customImageHandler == null || (image == null && !customImageHandler.alwaysIgnoreGenericHandler())) {
image = processImage(decodeColorData, imageData, isMask, XObject);
}
//add details to string so we can pass back
if (ImageCommands.trackImages && image != null && details != null) {
setImageInfo(imageData, details, decodeColorData, image);
}
return image;
}
private static BufferedImage getIndexedMaskImage(final byte[] index, final ImageData imageData, final int[] maskArray) {
final int d = imageData.getDepth();
int p = 0;
int c = 0;
final boolean[] invisible = new boolean[1 << d];
for (int i = 0; i < maskArray.length; i += 2) {
final int start = maskArray[i];
final int end = maskArray[i + 1];
if (start == end) {
invisible[start] = true;
} else {
for (int j = start; j < end; j++) {
invisible[j] = true;
}
}
}
final int[] indexColors = new int[index.length / 3];
for (int i = 0; i < indexColors.length; i++) {
indexColors[i] = (255 << 24) | ((index[c++] & 0xff) << 16) | ((index[c++] & 0xff) << 8) | (index[c++] & 0xff);
}
final BitReader reader = new BitReader(imageData.getObjectData(), d < 8);
final BufferedImage img = new BufferedImage(imageData.getWidth(), imageData.getHeight(), BufferedImage.TYPE_INT_ARGB);
final int[] output = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();
final int imageDim = imageData.getWidth() * imageData.getHeight();
final int w = imageData.getWidth();
int wc = 0;
for (int i = 0; i < imageDim; i++) {
final int v = reader.getPositive(d);
if (!invisible[v]) {
output[p++] = indexColors[v];
} else {
p++;
}
wc++;
if (wc == w) {
final int balance = 8 - (reader.getPointer() % 8);
wc = 0;
if (balance != 8) {
reader.getPositive(balance);
}
}
}
return img;
}
private void setImageInfo(final ImageData imageData, final String details, final GenericColorSpace decodeColorData, final BufferedImage image) {
final int width = imageData.getWidth();
final int height = imageData.getHeight();
//work out effective dpi
float dpi = gs.CTM[0][0];
if (dpi == 0) {
dpi = gs.CTM[0][1];
}
if (dpi < 0) {
dpi = -dpi;
}
dpi = (int) (width / dpi * 100);
//add details to string
final StringBuilder imageInfo = new StringBuilder(details);
imageInfo.append(" w=");
imageInfo.append(width);
imageInfo.append(" h=");
imageInfo.append(height);
imageInfo.append(' ');
imageInfo.append((int) dpi);
imageInfo.append(' ');
imageInfo.append(ColorSpaces.IDtoString(decodeColorData.getID()));
imageInfo.append(" (");
imageInfo.append(image.getWidth());
imageInfo.append(' ');
imageInfo.append(image.getHeight());
imageInfo.append(" type=");
imageInfo.append(image.getType());
imageInfo.append(')');
if (imagesInFile.isEmpty()) {
imagesInFile = imageInfo.toString();
} else {
imageInfo.append('\n');
imageInfo.append(imagesInFile);
imagesInFile = imageInfo.toString();
}
}
public void setSamplingOnly(final boolean getSamplingOnly) {
this.getSamplingOnly = getSamplingOnly;
}
public String getImagesInFile() {
return this.imagesInFile;
}
@Override
public void setParams(final ParserOptions parserOptions) {
this.parserOptions = parserOptions;
//Set flag to allow tunring on/off transparency optimisations in printing
String value = System.getProperty("org.jpedal.printTransparency");
if (value != null) {
ImageDecoder.allowPrintTransparency = parserOptions.isPrinting() && value.equalsIgnoreCase("true");
}
//Set flag to allow tunring on/off transparency optimisations in printing
value = System.getProperty("org.jpedal.viewerLargeImageCaching");
if (value != null) {
cacheLargeImages = value.equalsIgnoreCase("true");
}
}
/**
* save the current image, clipping and
* resizing clip. This gives us a clipped hires copy.
*/
public void generateClippedImage(BufferedImage image) {
final int pageRotation = pageData.getRotation(parserOptions.getPageNumber());
//object to scale and clip. Creating instance does the scaling
final ImageTransformerDouble image_transformation = new ImageTransformerDouble(gs, image, parserOptions.createScaledVersion(), 1, pageRotation);
//extract images either scaled/clipped or scaled then clipped
if (image_transformation != null) {
image_transformation.doubleScaleTransformShear();
//get intermediate image and save
image = image_transformation.getImage();
}
//convert mask into proper image if saving clipped images
if (isMask) {
image = convertMaskToImage(image, gs.nonstrokeColorSpace.getColor().getRGB());
}
//@suda - image saved here is being saved out as garbage. Do we need to alter colorspace or issue in our image decoder.
//releate comment in JDeliHelper
if (objectStoreStreamRef.saveStoredImageAsBytes("CLIP_" + currentImage, image, false)) {
errorTracker.addPageFailureMessage("Problem saving " + image);
}
//complete the image and workout co-ordinates
image_transformation.completeImage();
//get final image to allow for way we draw 'upside down'
image = image_transformation.getImage();
//allow for null image returned (ie if too small)
if (image != null) {
//store final image on disk & in memory
if (parserOptions.imagesNeeded()) {
//get initial values
final float x = image_transformation.getImageX();
final float y = image_transformation.getImageY();
final float w = image_transformation.getImageW();
final float h = image_transformation.getImageH();
pdfImages.setImageInfo(currentImage, parserOptions.getPageNumber(), x, y, w, h);
}
//
// //save the scaled/clipped version of image if allowed
// if(parserOptions.isFinalImagesExtracted()){
//
// image_transformation.doubleScaleTransformScale();
//
// objectStoreStreamRef.saveStoredImage(
// currentImage,
// ImageCommands.addBackgroundToMask(image, isMask),
// false,
// "png");
//
// }
}
}
private static BufferedImage convertMaskToImage(final BufferedImage outputImage, final int foreground) {
final int[] maskCol = {((foreground >> 16) & 0xFF), ((foreground >> 8) & 0xFF), ((foreground) & 0xFF), 255};
final BufferedImage img = new BufferedImage(outputImage.getWidth(), outputImage.getHeight(), outputImage.getType());
final Raster src = outputImage.getRaster();
final WritableRaster dest = img.getRaster();
final int[] values = new int[4];
final int w = outputImage.getWidth(), h = outputImage.getHeight();
for (int yy = 0; yy < h; yy++) {
for (int xx = 0; xx < w; xx++) {
//get raw color data
src.getPixel(xx, yy, values);
//System.out.println(values[0]+" "+values[1]+" "+values[2]+" "+values[3]+" ");
//if not transparent, fill with color
if (values[3] > 2) {
dest.setPixel(xx, yy, maskCol);
}
}
}
return img;
}
/**
* save the current image in raw and scaled/clipped version
*
* @param image
*/
public void generateTransformedImageSingle(BufferedImage image) {
//
if (parserOptions.isRawImagesExtracted()) {
objectStoreStreamRef.saveStoredImageAsBytes('R' + currentImage, image, false);
}
// get clipped image and co-ords
final Area clipping_shape = gs.getClippingShape();
//object to scale and clip. Creating instance does the scaling
final ImageTransformer image_transformation = new ImageTransformer(gs, image);
//get initial values
float x = image_transformation.getImageX();
float y = image_transformation.getImageY();
float w = image_transformation.getImageW();
float h = image_transformation.getImageH();
//apply clip as well if exists and not inline image
if (customImageHandler != null && clipping_shape != null && clipping_shape.getBounds().getWidth() > 1 &&
clipping_shape.getBounds().getHeight() > 1 && !customImageHandler.imageHasBeenScaled()) {
//see if clip is wider than image and ignore if so
if (!clipping_shape.contains(x, y, w, h)) {
//do the clipping
image_transformation.clipImage(clipping_shape);
//get ALTERED values
x = image_transformation.getImageX();
y = image_transformation.getImageY();
w = image_transformation.getImageW();
h = image_transformation.getImageH();
}
}
image = image_transformation.getImage();
//allow for null image returned (ie if too small)
if (image != null) {
pdfImages.setImageInfo(currentImage, parserOptions.getPageNumber(), x, y, w, h);
//save the scaled/clipped version of image if allowed
if (parserOptions.isFinalImagesExtracted()) {
objectStoreStreamRef.saveStoredImageAsBytes(currentImage, ImageCommands.addBackgroundToMask(image, isMask), false);
}
}
}
/**
* read in the image and process and save raw image
*/
BufferedImage processImage(GenericColorSpace decodeColorData,
final ImageData imageData, final boolean imageMask,
final PdfObject XObject) throws PdfException {
//track its use
cache.put(PdfObjectCache.ColorspacesUsed, decodeColorData.getID(), "x");
imageData.getFilter(XObject);
BufferedImage image = null; //
//allow user to process image
if (customImageHandler instanceof ImageDataHandler) {
image = customImageHandler.processImageData(gs, XObject);
} else if (imageData.isJPX()) {
removeJPXEncodingFromImageData(imageData, XObject);
} else if (imageData.isDCT()) {
int adobeColorTransform = 1;
final PdfObject decodeParms = XObject.getDictionary(PdfDictionary.DecodeParms);
if (decodeParms != null) {
adobeColorTransform = decodeParms.getInt(PdfDictionary.ColorTransform);
}
byte[] objectData;
try {
objectData = JPEGDecoder.getUnconvertedBytesFromJPEG(imageData.getObjectData(), adobeColorTransform);
if (objectData == null) { //fix for lgpl version
objectData = JPEGDecoder.getBytesFromJPEGWithImageIO(imageData.getObjectData(), decodeColorData, XObject);
decodeColorData = new DeviceRGBColorSpace();
imageData.setCompCount(3);
imageData.setDecodeArray(null);
}
} catch (final Exception e) {
LogWriter.writeLog("[PDF] Exception " + e + " Processing JPEG data in ImageDecoder");
objectData = JPEGDecoder.getBytesFromJPEGWithImageIO(imageData.getObjectData(), decodeColorData, XObject);
decodeColorData = new DeviceRGBColorSpace();
imageData.setCompCount(3);
imageData.setDecodeArray(null);
}
XObject.setMixedArray(PdfDictionary.Filter, null);
XObject.setDecodedStream(objectData);
imageData.setObjectData(objectData);
imageData.setDCT(false);
imageData.setDepth(8);
imageData.wasDCT(true);
}
if (image != null) {
return image;
} else {
return convertDataToImage(imageMask, imageData, XObject, decodeColorData);
}
}
static void removeJPXEncodingFromImageData(final ImageData imageData, final PdfObject XObject) {
final byte[] objectData = JPeg2000ImageDecoder.getUnconvertedBytesFromJPEG2000(imageData.getObjectData());
imageData.setObjectData(objectData);
XObject.setMixedArray(PdfDictionary.Filter, null);
XObject.setDecodedStream(objectData);
imageData.setDepth(8);
imageData.setIsJPX(false);
}
private BufferedImage convertDataToImage(final boolean imageMask, final ImageData imageData, final PdfObject XObject, GenericColorSpace decodeColorData) {
int sampling = 1;
BufferedImage image;
//setup any imageMask
byte[] maskCol = null;
if (imageMask) {
maskCol = ImageCommands.getMaskColor(gs);
}
//setup sub-sampling
if (parserOptions.isRenderPage() && streamType != ValueTypes.PATTERN) {
setDownsampledImageSize(imageData, XObject, multiplyer, decodeColorData);
}
//down-sample size if displaying (some cases excluded at present)
if (parserOptions.isRenderPage() &&
decodeColorData.getID() != ColorSpaces.ICC &&
imageData.getMode() != ImageCommands.ID &&
(imageData.getDepth() == 1 || imageData.getDepth() == 8)
&& imageData.getpX() > 0 && imageData.getpY() > 0 && (SamplingFactory.isPrintDownsampleEnabled || !parserOptions.isPrinting())) {
sampling = setSampling(imageData, decodeColorData);
if (sampling > 1 && multiplyer > 1) {
sampling = (int) (sampling / multiplyer);
}
}
//get sampling and exit from this code as we don't need to go further
if (getSamplingOnly) {
final int w = imageData.getWidth();
final int h = imageData.getHeight();
if (imageData.getpX() > 0 && imageData.getpY() > 0) {
final float scaleX = (((float) w) / imageData.getpX());
final float scaleY = (((float) h) / imageData.getpY());
if (scaleX > 100 || scaleY > 100) {
//ignore
} else if (scaleX < scaleY) {
parserOptions.setSamplingUsed(scaleX);
} else {
parserOptions.setSamplingUsed(scaleY);
}
}
return null;
}
//handle any decode array
final float[] decodeArray = imageData.getDecodeArray();
if (decodeArray != null && decodeArray.length > 0 && decodeColorData.getIndexedMap() == null) { //for the moment ignore if indexed (we may need to recode)
ImageCommands.applyDecodeArray(imageData.getObjectData(), imageData.getDepth(), decodeArray, decodeColorData.getID());
}
//apply any transfer function directly to data (does not work on DCT data)
final Object[] TRvalues = gs.getTR();
if (TRvalues != null) { //array of values
ImageCommands.applyTR(imageData, TRvalues, currentPdfFile);
}
//switch to 8 bit and reduce bw image size by averaging
if (sampling > 1 && !imageData.wasDCT()) {
//choose whether we cache raw data so we can redecode images at different resolutions in Viewer
if (cacheLargeImages && decodeColorData.getIndexedMap() == null) {
decodeColorData.dataToRGBByteArray(imageData.getObjectData(), imageData.getWidth(), imageData.getHeight());
if (SwingDisplay.testSampling) {
System.out.println("cached image full size= " + imageData.getWidth() + ", " + imageData.getHeight() + " Bits=" + imageData.getDepth() +
" " + decodeColorData + " count=" + imageData.getCompCount() + " bytes=" + imageData.getObjectData().length);
}
objectStoreStreamRef.saveRawImageData(parserOptions.getPageNumber() + String.valueOf(imageCount), imageData.getObjectData(),
imageData.getWidth(), imageData.getHeight(), imageData.getDepth(), imageData.getpX(), imageData.getpY(),
maskCol, ColorSpaces.DeviceRGB);
}
decodeColorData = DownSampler.downSampleImage(decodeColorData, imageData, maskCol, sampling);
}
if (maskCol != null) {
image = ImageDataToJavaImage.makeMaskImage(parserOptions, gs, current, imageData, decodeColorData, maskCol);
} else { //handle other types
LogWriter.writeLog(imageData.getWidth() + "W * " + imageData.getHeight() + "H BPC=" + imageData.getDepth() + ' ' + decodeColorData);
image = ImageDataToJavaImage.makeImage(decodeColorData, imageData);
}
if (image == null && !imageData.isRemoved()) {
parserOptions.imagesProcessedFully = false;
}
if (maskCol != null && gs.nonstrokeColorSpace.getColor().isTexture()) { //case 19095 vistair
convertTextureToImage(imageData, image);
}
//image=ImageDataToJavaImage.sharpen(image);
return image;
}
private void convertTextureToImage(final ImageData imageData, final BufferedImage image) {
final float[][] mm = gs.CTM;
final int w = imageData.getWidth();
final int h = imageData.getHeight();
final AffineTransform affine = new AffineTransform(mm[0][0], mm[0][1], mm[1][0], mm[1][1], mm[2][0], mm[2][1]);
final BufferedImage temp = ((PatternColorSpace) gs.nonstrokeColorSpace).getRawImage(affine);
final BufferedImage scrap = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
if (temp != null) {
final TexturePaint tp = new TexturePaint(temp, new Rectangle(0, 0, temp.getWidth(), temp.getHeight()));
final Graphics2D g2 = scrap.createGraphics();
g2.setPaint(tp);
final Rectangle rect = new Rectangle(0, 0, w, h);
g2.fill(rect);
}
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
if (image.getRGB(x, y) == -16777216) { //255 0 0 0
final int pRGB = scrap.getRGB(x, y);
image.setRGB(x, y, pRGB);
}
}
}
}
private int setSampling(final ImageData imageData, final GenericColorSpace decodeColorData) {
//see what we could reduce to and still be big enough for page
int sampling = 1;
final int w = imageData.getWidth();
final int h = imageData.getHeight();
int newW = w;
int newH = h;
int pX = imageData.getpX();
int pY = imageData.getpY();
//limit size (allow bigger grayscale
if (multiplyer <= 1 && !parserOptions.isPrinting()) {
int maxAllowed = 1000;
if (decodeColorData.getID() == ColorSpaces.DeviceGray) {
maxAllowed = 4000;
}
if (pX > maxAllowed) {
pX = maxAllowed;
}
if (pY > maxAllowed) {
pY = maxAllowed;
}
}
final int smallestH = pY << 2; //double so comparison works
final int smallestW = pX << 2;
//cannot be smaller than page
while (newW > smallestW && newH > smallestH) {
sampling <<= 1;
newW >>= 1;
newH >>= 1;
}
int scaleX = w / pX;
if (scaleX < 1) {
scaleX = 1;
}
int scaleY = h / pY;
if (scaleY < 1) {
scaleY = 1;
}
//choose smaller value so at least size of page
sampling = scaleX;
if (sampling > scaleY) {
sampling = scaleY;
}
imageData.setpX(pX);
imageData.setpY(pY);
return sampling;
}
private void setDownsampledImageSize(final ImageData imageData, final PdfObject XObject, final float multiplyer, final GenericColorSpace decodeColorData) {
final int w = imageData.getWidth();
final int h = imageData.getHeight();
if (parserOptions.isPrinting() && SamplingFactory.isPrintDownsampleEnabled && w < 4000) {
imageData.setpX(pageData.getCropBoxWidth(parserOptions.getPageNumber()) * 4);
imageData.setpY(pageData.getCropBoxHeight(parserOptions.getPageNumber()) * 4);
} else if (SamplingFactory.downsampleLevel == SamplingFactory.high || getSamplingOnly) { // && w>500 && h>500){ // ignore small items
//ensure all positive for comparison
final float[][] CTM = new float[3][3];
for (int ii = 0; ii < 3; ii++) {
for (int jj = 0; jj < 3; jj++) {
if (gs.CTM[ii][jj] < 0) {
CTM[ii][jj] = -gs.CTM[ii][jj];
} else {
CTM[ii][jj] = gs.CTM[ii][jj];
}
}
}
if (CTM[0][0] == 0 || CTM[0][0] < CTM[0][1]) {
imageData.setpX((int) (CTM[0][1]));
} else {
imageData.setpX((int) (CTM[0][0]));
}
if (CTM[1][1] == 0 || CTM[1][1] < CTM[1][0]) {
imageData.setpY((int) (CTM[1][0]));
} else {
imageData.setpY((int) (CTM[1][1]));
}
//don't bother on small itemsS
if (!getSamplingOnly && (w < 500 || (h < 600 && w < 1000) || imageData.isJPX())) { //change??
imageData.setpX(0);
imageData.setpX(0);
}
} else if (SamplingFactory.downsampleLevel == SamplingFactory.medium) {
imageData.setpX(pageData.getCropBoxWidth(parserOptions.getPageNumber()));
imageData.setpY(pageData.getCropBoxHeight(parserOptions.getPageNumber()));
}
final boolean hasMask = XObject.getRawObjectType() == PdfDictionary.Mask || XObject.getIntArray(PdfDictionary.Mask) != null;
/*
* turn off all scaling and allow user to control if switched off or HTML/svg/JavaFX
* (we still trap very large images in these cases as they blow away the
* memory footprint)
*/
final int maxHTMLImageSize = 4000;
if ((w < maxHTMLImageSize && h < maxHTMLImageSize &&
current.isHTMLorSVG() &&
(imageData.getDepth() != 1 || !hasMask))) {
imageData.setpX(-1);
imageData.setpY(1);
}
//needs to be factored in or images poor on hires modes
if ((imageData.isDCT() || imageData.isJPX()) && multiplyer > 1) {
imageData.setpX((int) (imageData.getpX() * multiplyer));
imageData.setpY((int) (imageData.getpX() * multiplyer));
}
//avoid for scanned text
if (imageData.getDepth() == 1 && !hasMask &&
decodeColorData.getID() == ColorSpaces.DeviceGray && imageData.getHeight() < 300) {
imageData.setpX(0);
imageData.setpY(0);
}
}
public void setRes(final PdfObjectCache cache) {
this.cache = cache;
}
public int processImage(final String s, final int dataPointer, final PdfObject xObject) throws Exception {
return 0;
}
public int processImage(final int dataPointer, final int startInlineStream, final byte[] stream, final int tokenNumber) throws Exception {
return 0;
}
}