org.jpedal.parser.ImageDecoder Maven / Gradle / Ivy
The newest version!
/*
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.idrsolutions.com
* Help section for developers at http://www.idrsolutions.com/java-pdf-library-support/
*
* (C) Copyright 1997-2013, IDRsolutions and Contributors.
*
* This file is part of JPedal
*
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;
import java.awt.Color;
import java.awt.color.ColorSpace;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.awt.geom.Path2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Kernel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import org.jpedal.PdfDecoder;
import org.jpedal.color.ColorSpaces;
import org.jpedal.color.ColorspaceFactory;
import org.jpedal.color.DeviceCMYKColorSpace;
import org.jpedal.color.DeviceGrayColorSpace;
import org.jpedal.color.DeviceRGBColorSpace;
import org.jpedal.color.GenericColorSpace;
import org.jpedal.constants.PDFImageProcessing;
import org.jpedal.exception.PdfException;
import org.jpedal.external.ImageHandler;
import org.jpedal.images.ImageOps;
import org.jpedal.images.ImageTransformer;
import org.jpedal.images.ImageTransformerDouble;
import org.jpedal.images.SamplingFactory;
import org.jpedal.io.ColorSpaceConvertor;
import org.jpedal.io.IDObjectDecoder;
import org.jpedal.io.JAIHelper;
import org.jpedal.io.ObjectStore;
import org.jpedal.io.ObjectUtils;
import org.jpedal.io.PdfFilteredReader;
import org.jpedal.objects.GraphicsState;
import org.jpedal.objects.PdfImageData;
import org.jpedal.objects.PdfPageData;
import org.jpedal.objects.raw.MaskObject;
import org.jpedal.objects.raw.PdfArrayIterator;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.objects.raw.XObject;
import org.jpedal.render.DynamicVectorRenderer;
import org.jpedal.render.RenderUtils;
import org.jpedal.utils.LogWriter;
public class ImageDecoder extends BaseDecoder {
PdfImageData pdfImages = null;
private boolean getSamplingOnly = false;
/** flag to show if image transparent */
boolean isMask = true;
String imagesInFile = null;
boolean isPrinting;
ImageHandler customImageHandler;
boolean useHiResImageForDisplay;
boolean isType3Font;
boolean renderDirectly;
int formLevel;
PdfPageData pageData;
ObjectStore objectStoreStreamRef;
/** flag to show raw images extracted */
boolean clippedImagesExtracted = true;
private boolean extractRawCMYK = false;
/** flag to show raw images extracted */
boolean finalImagesExtracted = true;
private boolean doNotRotate = false;
/**
* flag to show if we physical generate a scaled version of the images extracted
*/
boolean createScaledVersion = true;
/** flag to show content is being rendered */
boolean renderImages = false;
/** flag to show raw images extracted */
boolean rawImagesExtracted = true;
// used internally to show optimisations
private int optionsApplied = PDFImageProcessing.NOTHING;
/** name of current image in pdf */
private String currentImage = "";
private String formName;
public ImageDecoder(ImageHandler customImageHandler, ObjectStore objectStoreStreamRef, boolean renderDirectly, PdfImageData pdfImages,
int formLevel, PdfPageData pageData, String imagesInFile, String formName) {
this.formName = formName;
this.customImageHandler = customImageHandler;
this.objectStoreStreamRef = objectStoreStreamRef;
this.renderDirectly = renderDirectly;
this.pdfImages = pdfImages;
this.formLevel = formLevel;
this.pageData = pageData;
this.imagesInFile = imagesInFile;
}
private GenericColorSpace setupXObjectColorspace(PdfObject XObject, int depth, int width, int height, byte[] objectData) {
PdfObject ColorSpace = XObject.getDictionary(PdfDictionary.ColorSpace);
// handle colour information
GenericColorSpace decodeColorData = new DeviceRGBColorSpace();
if (ColorSpace != null) {
decodeColorData = ColorspaceFactory.getColorSpaceInstance(this.currentPdfFile, ColorSpace, this.cache.XObjectColorspaces);
decodeColorData.setPrinting(this.isPrinting);
// track colorspace use
this.cache.put(PdfObjectCache.ColorspacesUsed, decodeColorData.getID(), "x");
if (depth == 1 && decodeColorData.getID() == ColorSpaces.DeviceRGB && XObject.getDictionary(PdfDictionary.Mask) == null) {
byte[] data = decodeColorData.getIndexedMap();
// no index or first colour is white so use grayscale
if (decodeColorData.getIndexedMap() == null || (data.length == 6 && data[0] == 0 && data[1] == 0 && data[2] == 0)) decodeColorData = new DeviceGrayColorSpace();
}
}
// fix for odd itext file (/PDFdata/baseline_screens/debug3/Leistung.pdf)
byte[] indexData = decodeColorData.getIndexedMap();
if (depth == 8 && indexData != null && decodeColorData.getID() == ColorSpaces.DeviceRGB && width * height == objectData.length) {
PdfObject newMask = XObject.getDictionary(PdfDictionary.Mask);
if (newMask != null) {
int[] maskArray = newMask.getIntArray(PdfDictionary.Mask);
// 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();
}
}
}
// pass through decode params
PdfObject parms = XObject.getDictionary(PdfDictionary.DecodeParms);
if (parms != null) decodeColorData.setDecodeParms(parms);
// set any intent
decodeColorData.setIntent(XObject.getName(PdfDictionary.Intent));
return decodeColorData;
}
public BufferedImage processImageXObject(PdfObject XObject, String image_name, byte[] objectData, boolean saveRawData, String details)
throws PdfException {
boolean imageMask;
BufferedImage image = null;
// add filename to make it unique
image_name = this.fileName + '-' + image_name;
int depth = 1;
int width = XObject.getInt(PdfDictionary.Width);
int height = XObject.getInt(PdfDictionary.Height);
int newDepth = XObject.getInt(PdfDictionary.BitsPerComponent);
if (newDepth != PdfDictionary.Unknown) depth = newDepth;
this.isMask = XObject.getBoolean(PdfDictionary.ImageMask);
imageMask = this.isMask;
GenericColorSpace decodeColorData = setupXObjectColorspace(XObject, depth, width, height, objectData);
// tell user and log
if (LogWriter.isOutput()) LogWriter.writeLog("Processing XObject: " + image_name + ' ' + XObject.getObjectRefAsString() + " width=" + width
+ " Height=" + height + " Depth=" + depth + " colorspace=" + decodeColorData);
/**
* allow user to process image
*/
if (this.customImageHandler != null) image = this.customImageHandler.processImageData(this.gs, XObject);
/**
* fix for add case where image blank and actual image on SMask (customer-June2011/10664.pdf)
*/
PdfObject newSMask = XObject.getDictionary(PdfDictionary.SMask);
byte[] index = decodeColorData.getIndexedMap();
if (newSMask != null && index != null && index.length == 3 && decodeColorData.getID() != ColorSpaces.ICC) { // swap out the image with
// inverted SMask if empty
XObject = newSMask;
XObject.setFloatArray(PdfDictionary.Decode, new float[] { 1, 0 });
objectData = this.currentPdfFile.readStream(XObject, true, true, false, false, false, null);
depth = 1;
width = XObject.getInt(PdfDictionary.Width);
height = XObject.getInt(PdfDictionary.Height);
newDepth = XObject.getInt(PdfDictionary.BitsPerComponent);
if (newDepth != PdfDictionary.Unknown) depth = newDepth;
decodeColorData = setupXObjectColorspace(XObject, depth, width, height, objectData);
}
// deal with special case of 1x1 pixel backed onto large inverted Smask which would be very slow in Generic code
// see (Customers-dec2011/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
byte opacity = (byte) 255;
XObject = newSMask;
/**
* get opacity if not default
*/
float[] maskDecode = XObject.getFloatArray(PdfDictionary.Decode);
if (maskDecode != null) {
opacity = (byte) ((maskDecode[0]) * 255);
}
// use black/white colour
byte[] maskIndex = new byte[] { 0, 0, 0, (byte) 255, (byte) 255, (byte) 255 };
// get raw 1 bit data for smask
byte[] data = this.currentPdfFile.readStream(XObject, true, true, false, false, false, null);
// get dimensions
width = XObject.getInt(PdfDictionary.Width);
height = XObject.getInt(PdfDictionary.Height);
// buffer for byte data
int length = width * height * 4;
objectData = new byte[length];
// create ARGB data from 1bit data and opacity
ColorSpaceConvertor.flatten1bpc(width, data, maskIndex, true, length, opacity, objectData);
// build image
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
DataBuffer db = new DataBufferByte(objectData, objectData.length);
WritableRaster raster = Raster.createInterleavedRaster(db, width, height, width * 4, 4, new int[] { 0, 1, 2, 3 }, null);
image.setData(raster);
}
// extract and process the image
else
if (this.customImageHandler == null || (image == null && !this.customImageHandler.alwaysIgnoreGenericHandler())) image = processImage(
decodeColorData, objectData, image_name, width, height, depth, imageMask, XObject, saveRawData, ImageCommands.XOBJECT);
// add details to string so we can pass back
if (ImageCommands.trackImages && image != null && details != null) {
// work out effective dpi
float dpi = this.gs.CTM[0][0];
if (dpi == 0) dpi = this.gs.CTM[0][1];
if (dpi < 0) dpi = -dpi;
dpi = (int) (width / dpi * 100);
// add details to string
StringBuilder imageInfo = new StringBuilder(details);
imageInfo.append(" w=");
imageInfo.append(String.valueOf(width));
imageInfo.append(" h=");
imageInfo.append(String.valueOf(height));
imageInfo.append(' ');
imageInfo.append(String.valueOf((int) dpi));
imageInfo.append(' ');
imageInfo.append(ColorSpaces.IDtoString(decodeColorData.getID()));
imageInfo.append(" (");
imageInfo.append(String.valueOf(image.getWidth()));
imageInfo.append(' ');
imageInfo.append(String.valueOf(image.getHeight()));
imageInfo.append(" type=");
imageInfo.append(String.valueOf(image.getType()));
imageInfo.append(')');
if (this.imagesInFile.length() == 0) this.imagesInFile = imageInfo.toString();
else {
imageInfo.append('\n');
imageInfo.append(this.imagesInFile);
this.imagesInFile = imageInfo.toString();
}
}
return image;
}
/**
* process image in XObject (XForm handled in PdfStreamDecoder)
*/
public int processDOImage(String name, int dataPointer, PdfObject XObject) throws PdfException {
// name is not unique if in form so we add form level to separate out
if (this.formLevel > 0) name = this.formName + '_' + this.formLevel + '_' + name;
// set if we need
String key = null;
if (ImageCommands.rejectSuperimposedImages) {
key = ((int) this.gs.CTM[2][0]) + "-" + ((int) this.gs.CTM[2][1]) + '-' + ((int) this.gs.CTM[0][0]) + '-' + ((int) this.gs.CTM[1][1])
+ '-' + ((int) this.gs.CTM[0][1]) + '-' + ((int) this.gs.CTM[1][0]);
}
try {
processXImage(name, name, key, XObject);
}
catch (Error e) {
// tell user and log
if (LogWriter.isOutput()) LogWriter.writeLog("Error: " + e.getMessage());
this.imagesProcessedFully = false;
this.errorTracker.addPageFailureMessage("Error " + e + " in DO");
}
catch (Exception e) {
if (LogWriter.isOutput()) LogWriter.writeLog("Exception " + e);
this.imagesProcessedFully = false;
this.errorTracker.addPageFailureMessage("Error " + e + " in DO");
if (e.getMessage().contains("JPeg 2000")) {
throw new RuntimeException("XX JPeg 2000 Images needs the VM parameter -Dorg.jpedal.jai=true switch turned on");
}
}
return dataPointer;
}
private void processXImage(String name, String details, String key, PdfObject XObject) throws PdfException {
final int previousUse = -1;
if (ImageCommands.trackImages) {
details = details + " Image";
if (this.imagesInFile == null) this.imagesInFile = "";
}
boolean isForHTML = ((this.current.getType() == DynamicVectorRenderer.CREATE_HTML
|| this.current.getType() == DynamicVectorRenderer.CREATE_SVG || this.current.getType() == DynamicVectorRenderer.CREATE_JAVAFX));
/** don't process unless needed */
if (this.renderImages || this.finalImagesExtracted || this.clippedImagesExtracted || this.rawImagesExtracted) {
// read stream for image
byte[] objectData;
if (previousUse == -1) {
objectData = this.currentPdfFile.readStream(XObject, true, true, false, false, false,
XObject.getCacheName(this.currentPdfFile.getObjectReader()));
// flag issue
if (objectData == null) this.imagesProcessedFully = false;
}
if (objectData != null || previousUse > 0) {
boolean alreadyCached = false;// (useHiResImageForDisplay && current.isImageCached(this.pageNum));
BufferedImage image = null;
// generate name including filename to make it unique less /
this.currentImage = this.fileName + '-' + name;
// process the image and save raw version
if (!alreadyCached && previousUse == -1) {
// last flag change from true to false to fix issue
image = processImageXObject(XObject, name, objectData, true, details);
}
// fix for oddity in Annotation
if (image != null && image.getWidth() == 1 && image.getHeight() == 1 && this.isType3Font) {
image.flush();
image = null;
}
// save transformed image
if (image != null || alreadyCached || previousUse > 0) {
// manipulate CTM to allow for image truncated
float[][] savedCMT = null;
if (!isForHTML && (this.renderDirectly || this.useHiResImageForDisplay || previousUse > 0)) {
this.gs.x = this.gs.CTM[2][0];
this.gs.y = this.gs.CTM[2][1];
/** save details if we are tracking */
if (this.finalImagesExtracted || this.rawImagesExtracted) {
int w = (int) Math.abs(this.gs.CTM[0][0]);
if (w == 0) w = (int) Math.abs(this.gs.CTM[0][1]);
int h = (int) Math.abs(this.gs.CTM[1][1]);
if (h == 0) h = (int) Math.abs(this.gs.CTM[1][0]);
this.pdfImages.setImageInfo(this.currentImage, this.pageNum, this.gs.x, this.gs.x, w, h);
}
if (this.renderDirectly) { // in own bit as other code not needed
this.current.drawImage(this.pageNum, image, this.gs, alreadyCached, name, this.optionsApplied, -1);
}
else
if (image != null || alreadyCached || previousUse > 0) {
int id = this.current.drawImage(this.pageNum, image, this.gs, alreadyCached, name, this.optionsApplied, previousUse);
/**
* delete previous used if this not transparent
*/
/**
* ignore multiple overlapping images
*/
// if (ImageCommands.rejectSuperimposedImages && image != null && image.getType() != BufferedImage.TYPE_INT_ARGB) {
//
// Object lastRef = cache.getImposedKey(key);
//
// //delete under image....
// if (lastRef != null && gs.getClippingShape() == null) //limit to avoid issues on other files
// current.flagImageDeleted((Integer) lastRef);
// }
/**
* store last usage in case it reappears unless it is transparent
*/
if (ImageCommands.rejectSuperimposedImages && key != null) this.cache.setImposedKey(key, id);
}
}
else {
if (this.clippedImagesExtracted || isForHTML) {
generateTransformedImage(image, name);
}
else {
try {
generateTransformedImageSingle(image, name);
}
catch (Exception e) {
if (LogWriter.isOutput()) LogWriter.writeLog("Exception " + e + " on transforming image in file");
}
}
}
if (image != null) image.flush();
// restore
if (savedCMT != null) this.gs.CTM = savedCMT;
}
}
}
}
ImageDecoder() {}
public ImageDecoder(ImageHandler customImageHandler, boolean useHiResImageForDisplay, ObjectStore objectStoreStreamRef, boolean renderDirectly,
PdfImageData pdfImages, int formLevel, PdfPageData pageData) {
this.customImageHandler = customImageHandler;
this.useHiResImageForDisplay = useHiResImageForDisplay;
this.objectStoreStreamRef = objectStoreStreamRef;
this.renderDirectly = renderDirectly;
this.pdfImages = pdfImages;
this.formLevel = formLevel;
this.pageData = pageData;
}
public void setSamplingOnly(boolean getSamplingOnly) {
this.getSamplingOnly = getSamplingOnly;
}
public String getImagesInFile() {
return this.imagesInFile;
}
public void setParameters(boolean isPageContent, boolean renderPage, int renderMode, int extractionMode, boolean isPrinting, boolean isType3Font,
boolean useHiResImageForDisplay) {
this.isPageContent = isPageContent;
this.renderPage = renderPage;
this.renderMode = renderMode;
this.extractionMode = extractionMode;
this.isPrinting = isPrinting;
this.isType3Font = isType3Font;
this.useHiResImageForDisplay = useHiResImageForDisplay;
this.renderImages = renderPage && (renderMode & PdfDecoder.RENDERIMAGES) == PdfDecoder.RENDERIMAGES;
this.finalImagesExtracted = (extractionMode & PdfDecoder.FINALIMAGES) == PdfDecoder.FINALIMAGES;
this.extractRawCMYK = (extractionMode & PdfDecoder.CMYKIMAGES) == PdfDecoder.CMYKIMAGES;
this.clippedImagesExtracted = (extractionMode & PdfDecoder.CLIPPEDIMAGES) == PdfDecoder.CLIPPEDIMAGES;
this.rawImagesExtracted = (extractionMode & PdfDecoder.RAWIMAGES) == PdfDecoder.RAWIMAGES;
this.createScaledVersion = this.finalImagesExtracted || this.renderImages;
}
public void setImageName(String currentImage) {
this.currentImage = currentImage;
}
public int getOptionsApplied() {
return this.optionsApplied;
}
public int processIDImage(int dataPointer, int startInlineStream, byte[] stream, int tokenNumber) throws Exception {
/**
* read Dictionary
*/
PdfObject XObject = new XObject(PdfDictionary.ID);
IDObjectDecoder objectDecoder = new IDObjectDecoder(this.currentPdfFile.getObjectReader());
objectDecoder.setEndPt(dataPointer - 2);
objectDecoder.readDictionaryAsObject(XObject, startInlineStream, stream);
BufferedImage image = null;
boolean inline_imageMask;
// store pointer to current place in file
int inline_start_pointer = dataPointer + 1, i_w, i_h, i_bpc;
// find end of stream
int i = inline_start_pointer, streamLength = stream.length;
// find end
while (true) {
// look for end EI
// handle Pdflib variety
if (streamLength - i > 3 && stream[i + 1] == 69 && stream[i + 2] == 73 && stream[i + 3] == 10) break;
// general case
if ((streamLength - i > 3) && (stream[i] == 32 || stream[i] == 10 || stream[i] == 13 || (stream[i + 3] == 32 && stream[i + 4] == 'Q'))
&& (stream[i + 1] == 69) && (stream[i + 2] == 73) && (stream[i + 3] == 32 || stream[i + 3] == 10 || stream[i + 3] == 13)) break;
i++;
if (i == streamLength) break;
}
if (this.renderImages || this.finalImagesExtracted || this.clippedImagesExtracted || this.rawImagesExtracted) {
// load the data
// generate the name including file name to make it unique
String image_name = this.fileName + "-IN-" + tokenNumber;
int endPtr = i;
// hack for odd files
if (i < stream.length && stream[endPtr] != 32 && stream[endPtr] != 10 && stream[endPtr] != 13) endPtr++;
// correct data (ie idoq/FC1100000021259.pdf )
if (stream[inline_start_pointer] == 10) inline_start_pointer++;
/**
* put image data in array
*/
byte[] i_data = new byte[endPtr - inline_start_pointer];
System.arraycopy(stream, inline_start_pointer, i_data, 0, endPtr - inline_start_pointer);
// System.out.print(">>");
// for(int ss=inline_start_pointer-5;ss 1
&& clipping_shape.getBounds().getHeight() > 1 && !this.customImageHandler.imageHasBeenScaled()) {
// see if clip is wider than image and ignore if so
boolean ignore_image = clipping_shape.contains(x, y, w, h);
if (!ignore_image) {
// 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();
}
}
// alter 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) {
/** turn correct way round if needed */
// if((currentGraphicsState.CTM[0][1]!=0 )&&(currentGraphicsState.CTM[1][0]!=0 )&&(currentGraphicsState.CTM[0][0]>=0 )){
/*
* if((currentGraphicsState.CTM[0][1]>0 )&&(currentGraphicsState.CTM[1][0]>0 )&&(currentGraphicsState.CTM[0][0]>=0 )){ double
* dx=1,dy=1,scaleX=0,scaleY=0; if(currentGraphicsState.CTM[0][1]>0){ dx=-1; scaleX=image.getWidth(); }
* if(currentGraphicsState.CTM[1][0]>0){ dy=-1; scaleY=image.getHeight(); } AffineTransform image_at =new AffineTransform();
* image_at.scale(dx,dy); image_at.translate(-scaleX,-scaleY); AffineTransformOp invert= new AffineTransformOp(image_at,
* ColorSpaces.hints); image = invert.filter(image,null); }
*/
// store final image on disk & in memory
if (this.finalImagesExtracted || this.rawImagesExtracted) {
this.pdfImages.setImageInfo(this.currentImage, this.pageNum, x, y, w, h);
// if(includeImagesInData){
//
// float xx=x;
// float yy=y;
//
// if(clipping_shape!=null){
//
// int minX=(int)clipping_shape.getBounds().getMinX();
// int maxX=(int)clipping_shape.getBounds().getMaxX();
//
// int minY=(int)clipping_shape.getBounds().getMinY();
// int maxY=(int)clipping_shape.getBounds().getMaxY();
//
// if((xx>0 && xx0 && yy500 && h>500){ // ignore small items
// ensure all positive for comparison
float[][] CTM = new float[3][3];
for (int ii = 0; ii < 3; ii++) {
for (int jj = 0; jj < 3; jj++) {
if (this.gs.CTM[ii][jj] < 0) CTM[ii][jj] = -this.gs.CTM[ii][jj];
else CTM[ii][jj] = this.gs.CTM[ii][jj];
}
}
if (CTM[0][0] == 0 || CTM[0][0] < CTM[0][1]) pX = (int) (CTM[0][1]);
else pX = (int) (CTM[0][0]);
if (CTM[1][1] == 0 || CTM[1][1] < CTM[1][0]) pY = (int) (CTM[1][0]);
else pY = (int) (CTM[1][1]);
// don't bother on small itemsS
if (!this.getSamplingOnly && (w < 500 || (h < 600 && (w < 1000 || isJPX)))) { // change??
pX = 0;// pageData.getCropBoxWidth(this.pageNum);
pY = 0;// pageData.getCropBoxHeight(this.pageNum);
}
}
else
if (SamplingFactory.downsampleLevel == SamplingFactory.medium) {
pX = this.pageData.getCropBoxWidth(this.pageNum);
pY = this.pageData.getCropBoxHeight(this.pageNum);
}
}
/**
* 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 (this.current.avoidDownSamplingImage()
|| (w < maxHTMLImageSize && h < maxHTMLImageSize && (this.current.getType() == DynamicVectorRenderer.CREATE_HTML
|| this.current.getType() == DynamicVectorRenderer.CREATE_SVG || this.current.getType() == DynamicVectorRenderer.CREATE_JAVAFX))) {
pX = -1;
pY = -1;
}
/**
* }else if((current.getType()== OutputDisplay.CREATE_HTML || current.getType()== OutputDisplay.CREATE_SVG || current.getType()==
* OutputDisplay.CREATE_JAVAFX)){
*
* //float wScale=Math.abs(gs.CTM[0][0]/w); //float hScale=Math.abs(gs.CTM[1][1]/h);
*
* //ignore silly figures if(h>4000 && w>4000){// ||wScale<0.1f || hScale<0.1f){ pX = -1; pY=-1;
*
* //System.out.println(w+" "+h+" "+name+" "+gs.CTM[0][0]+" "+gs.CTM[1][1]+" "+wScale+" "+hScale); }
*
* }
*
* /
**/
// needs to be factored in or images poor on hires modes
if ((isDCT || isJPX) && this.multiplyer > 1) {
pX = (int) (pX * this.multiplyer);
pY = (int) (pY * this.multiplyer);
}
PdfObject DecodeParms = XObject.getDictionary(PdfDictionary.DecodeParms), newMask, newSMask;
newMask = XObject.getDictionary(PdfDictionary.Mask);
newSMask = XObject.getDictionary(PdfDictionary.SMask);
// avoid for scanned text
if (d == 1 && (newSMask != null || XObject.getObjectType() != PdfDictionary.Mask) && decodeColorData.getID() == ColorSpaces.DeviceGray
&& h < 300) {
// System.out.println("XObject="+XObject.getObjectType());
// System.out.println("newSMask="+newSMask);
pX = 0;
pY = 0;
}
// flag masks
if ((newMask != null || newSMask != null) && LogWriter.isOutput()) LogWriter.writeLog("newMask= " + newMask + " newSMask=" + newSMask);
// work out if inverted (assume true and disprove)
// work this into saved data @mariusz so 125% works
boolean arrayInverted = false;
if (decodeArray != null) {
arrayInverted = true;
int count = decodeArray.length;
for (int aa = 0; aa < count; aa = aa + 2) {
if (decodeArray[aa] == 1f && decodeArray[aa + 1] == 0f) {
// okay
}
else {
arrayInverted = false;
aa = count;
}
}
}
/**
* also needs to be inverted in this case see Customers3/Architectural_Specification.pdf page 31 onwards 20100816 - no longer seems needed and
* removed by MS to fix abacus file (see Rog email 13th august 2010)
*/
// if(!arrayInverted && decodeColorData.getID()==ColorSpaces.DeviceGray && index!=null){// && index[0]==-1 && index[1]==-1 && index[2]==-1){
// arrayInverted=true;
// }
/**
* down-sample size if displaying (some cases excluded at present)
*/
if (this.renderPage && newMask == null && decodeColorData.getID() != ColorSpaces.ICC
&& (arrayInverted || decodeArray == null || decodeArray.length == 0) && (d == 1 || d == 8) && pX > 0 && pY > 0
&& (SamplingFactory.isPrintDownsampleEnabled || !this.isPrinting)) {
// @speed - debug
// see what we could reduce to and still be big enough for page
newW = w;
newH = h;
// limit size (allow bigger grayscale
if (this.multiplyer <= 1 && !this.isPrinting) {
int maxAllowed = 1000;
if (decodeColorData.getID() == ColorSpaces.DeviceGray) {
maxAllowed = 4000;
}
if (pX > maxAllowed) pX = maxAllowed;
if (pY > maxAllowed) pY = maxAllowed;
}
int smallestH = pY << 2; // double so comparison works
int smallestW = pX << 2;
// cannot be smaller than page
while (newW > smallestW && newH > smallestH) {
sampling = sampling << 1;
newW = newW >> 1;
newH = 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;
}
// get sampling and exit from this code as we don't need to go further
if (this.getSamplingOnly) {// && pX>0 && pY>0){
float scaleX = (((float) w) / pX);
float scaleY = (((float) h) / pY);
if (scaleX < scaleY) {
this.samplingUsed = scaleX;
}
else {
this.samplingUsed = scaleY;
}
// we may need to check mask as well
boolean checkMask = false;
if (newSMask != null) {
/** read the stream */
byte[] objectData = this.currentPdfFile.readStream(newSMask, true, true, false, false, false,
newSMask.getCacheName(this.currentPdfFile.getObjectReader()));
if (objectData != null) {
if (DecodeParms == null) DecodeParms = newSMask.getDictionary(PdfDictionary.DecodeParms);
int maskW = newSMask.getInt(PdfDictionary.Width);
int maskH = newSMask.getInt(PdfDictionary.Height);
// if all white image with mask, use mask
boolean isDownscaled = maskW / 2 > w && maskH / 2 > h;
boolean ignoreMask = isDownscaled && DecodeParms != null && DecodeParms.getInt(PdfDictionary.Colors) != -1
&& DecodeParms.getInt(PdfDictionary.Predictor) != 15;
// ignoreMask is hack to fix odd Visuality files
if (!ignoreMask) checkMask = true;
}
}
if (!checkMask) {
// getSamplingOnly=false;
return null;
}
}
{
if (sampling > 1 && this.multiplyer > 1) {
// samplingUsed= sampling;
sampling = (int) (sampling / this.multiplyer);
}
// switch to 8 bit and reduce bw image size by averaging
if (sampling > 1) {
isDownsampled = true;
newW = w / sampling;
newH = h / sampling;
boolean saveData = false;
// flatten out high res raw data in this case so we can store and resample (see deebug3/DOC002.PDF and DOC003.PDF
if (imageMask && w > 2000 && h > 2000 && d == 1 && decodeColorData.getID() == ColorSpaces.DeviceRGB && this.gs.CTM[0][0] > 0
&& this.gs.CTM[1][1] > 0) {
saveData = true;
}
if (d == 1 && (decodeColorData.getID() != ColorSpaces.DeviceRGB || index == null)) {
// save raw 1 bit data
// code in DynamicVectorRenderer may need alignment if it changes
// 20090929 - re-enabled by Mark with deviceGray limit for Abacus files
// breaks if form rotated so only use at top level
// (sample file breaks so we added this as hack for fattura 451-10 del 31.10.10.pdf in customers3)
if (this.formLevel < 2 && (saveData || (!imageMask && saveRawData && decodeColorData.getID() == ColorSpaces.DeviceGray))) {
// copy and turn upside down first
int count = data.length;
byte[] turnedData = new byte[count];
System.arraycopy(data, 0, turnedData, 0, count);
// turnedData=ImageOps.invertImage(turnedData, w, h, d, 1, null);
boolean isInverted = !saveData && !this.doNotRotate && (this.renderDirectly || this.useHiResImageForDisplay)
&& RenderUtils.isInverted(this.gs.CTM);
boolean isRotated = !saveData && !this.doNotRotate && (this.renderDirectly || this.useHiResImageForDisplay)
&& RenderUtils.isRotated(this.gs.CTM);
if (this.renderDirectly) {
isInverted = false;
isRotated = false;
}
if (isRotated) { // rotate at byte level with copy New Code still some issues
turnedData = ImageOps.rotateImage(turnedData, w, h, d, 1, index);
// reset
int temp = h;
h = w;
w = temp;
temp = pX;
pX = pY;
pY = temp;
}
if (isInverted) // invert at byte level with copy
turnedData = ImageOps.invertImage(turnedData, w, h, d, 1, index);
// invert all the bits if needed before we store
if (arrayInverted) {
for (int aa = 0; aa < count; aa++)
turnedData[aa] = (byte) (turnedData[aa] ^ 255);
}
// cache if binary image (not Mask)
if (decodeColorData.getID() == ColorSpaces.DeviceRGB && d == 1 && maskCol != null) { // avoid cases like Hand_test/DOC028.PDF
}
else
if (((w < 4000 && h < 4000) || decodeColorData.getID() == ColorSpaces.DeviceGray) && !(XObject instanceof MaskObject)) { // limit
// added
// after
// silly
// sizes
// on
// Customers3/1773_A2.pdf
// Integer pn = new Integer(this.pageNum);
// Integer iC = new Integer(imageCount);
String key = this.pageNum + String.valueOf(this.imageCount);
if (saveData) {
this.current.getObjectStore().saveRawImageData(key, turnedData, w, h, pX, pY, maskCol, decodeColorData.getID());
}
else {
this.current.getObjectStore().saveRawImageData(key, turnedData, w, h, pX, pY, null, decodeColorData.getID());
}
}
if (isRotated) {
// reset
int temp = h;
h = w;
w = temp;
temp = pX;
pX = pY;
pY = temp;
}
}
// make 1 bit indexed flat
if (index != null) index = decodeColorData.convertIndexToRGB(index);
int size = newW * newH;
if (imageMask) {
size = size * 4;
maskCol[3] = (byte) 255;
}
else
if (index != null) size = size * 3;
byte[] newData = new byte[size];
final int[] flag = { 1, 2, 4, 8, 16, 32, 64, 128 };
int origLineLength = (w + 7) >> 3;
int bit;
byte currentByte;
// scan all pixels and down-sample
for (int y = 0; y < newH; y++) {
for (int x = 0; x < newW; x++) {
int bytes = 0, count = 0;
// allow for edges in number of pixels left
int wCount = sampling, hCount = sampling;
int wGapLeft = w - x;
int hGapLeft = h - y;
if (wCount > wGapLeft) wCount = wGapLeft;
if (hCount > hGapLeft) hCount = hGapLeft;
// count pixels in sample we will make into a pixel (ie 2x2 is 4 pixels , 4x4 is 16 pixels)
int ptr;
for (int yy = 0; yy < hCount; yy++) {
for (int xx = 0; xx < wCount; xx++) {
ptr = ((yy + (y * sampling)) * origLineLength) + (((x * sampling) + xx) >> 3);
if (ptr < data.length) {
currentByte = data[ptr];
}
else {
currentByte = 0;
}
if (imageMask && !arrayInverted) currentByte = (byte) (currentByte ^ 255);
bit = currentByte & flag[7 - (((x * sampling) + xx) & 7)];
if (bit != 0) bytes++;
count++;
}
}
// set value as white or average of pixels
int offset = x + (newW * y);
if (count > 0) {
if (imageMask) {
// System.out.println("xx");
for (int ii = 0; ii < 4; ii++) {
if (arrayInverted) newData[(offset * 4) + ii] = (byte) (255 - (((maskCol[ii] & 255) * bytes) / count));
else newData[(offset * 4) + ii] = (byte) ((((maskCol[ii] & 255) * bytes) / count));
// System.out.println(newData[(offset*4)+ii]+" "+(byte)(((maskCol[ii] & 255)*bytes)/count);
}
}
else
if (index != null && decodeColorData.getID() == ColorSpaces.Separation && 1 == 2) {
for (int ii = 0; ii < 3; ii++)
if ((bytes / count) > 0.5f) {
newData[(offset * 3) + ii] = (byte) (((index[3 + ii] & 255)));
}
else {
newData[(offset * 3) + ii] = (byte) (((index[ii] & 255)));
}
}
else
if (index != null && d == 1) {
int av;
for (int ii = 0; ii < 3; ii++) {
// can be in either order so look at index
if (index[0] == -1 && index[1] == -1 && index[2] == -1) {
av = (index[ii] & 255) + (index[ii + 3] & 255);
newData[(offset * 3) + ii] = (byte) (255 - ((av * bytes) / count));
}
else {// if(decodeColorData.getID()==ColorSpaces.DeviceCMYK){ //avoid color 'smoothing' - see
// CustomersJune2011/lead base paint.pdf
float ratio = bytes / count;
if (ratio > 0.5) newData[(offset * 3) + ii] = index[ii + 3];
else newData[(offset * 3) + ii] = index[ii];
}
}
}
else
if (index != null) {
for (int ii = 0; ii < 3; ii++)
newData[(offset * 3) + ii] = (byte) (((index[ii] & 255) * bytes) / count);
}
else newData[offset] = (byte) ((255 * bytes) / count);
}
else {
if (imageMask) {
for (int ii = 0; ii < 3; ii++)
newData[(offset * 4) + ii] = (byte) 0;
}
else
if (index != null) {
for (int ii = 0; ii < 3; ii++)
newData[((offset) * 3) + ii] = 0;
}
else newData[offset] = (byte) 255;
}
}
}
data = newData;
if (index != null) compCount = 3;
h = newH;
w = newW;
decodeColorData.setIndex(null, 0);
// remap Separation as already converted here
if (decodeColorData.getID() == ColorSpaces.Separation) {
decodeColorData = new DeviceRGBColorSpace();
// needs to have these settings if 1 bit not indexed
if (d == 1 && index == null) {
compCount = 1;
int count = data.length;
for (int aa = 0; aa < count; aa++)
data[aa] = (byte) (data[aa] ^ 255);
}
}
d = 8;
// imageMask=false;
}
else
if (d == 8 && (Filters == null || (!isDCT && !isJPX))) {
boolean hasIndex = decodeColorData.getIndexedMap() != null
&& (decodeColorData.getID() == ColorSpaces.DeviceRGB || decodeColorData.getID() == ColorSpaces.CalRGB
|| decodeColorData.getID() == ColorSpaces.DeviceCMYK || decodeColorData.getID() == ColorSpaces.ICC);
int oldSize = data.length;
int x = 0, y = 0, xx = 0, yy = 0, jj = 0, comp = 0, origLineLength = 0, indexCount = 1;
try {
if (hasIndex) { // convert to sRGB
comp = 1;
compCount = 3;
indexCount = 3;
index = decodeColorData.convertIndexToRGB(index);
decodeColorData.setIndex(null, 0);
}
else {
comp = decodeColorData.getColorComponentCount();
}
// black and white
if (w * h == oldSize || decodeColorData.getID() == ColorSpaces.DeviceGray) comp = 1;
byte[] newData;
if (hasIndex) { // hard-coded to 3 values
newData = new byte[newW * newH * indexCount];
origLineLength = w;
}
else {
newData = new byte[newW * newH * comp];
origLineLength = w * comp;
}
// System.err.println(w+" "+h+" "+data.length+" comp="+comp+" scaling="+sampling+" "+decodeColorData);
// System.err.println("size="+w*h*comp+" filter"+filter+" scaling="+sampling+" comp="+comp);
// System.err.println("w="+w+" h="+h+" data="+data.length+" origLineLength="+origLineLength+" sampling="+sampling);
// scan all pixels and down-sample
for (y = 0; y < newH; y++) {
for (x = 0; x < newW; x++) {
// allow for edges in number of pixels left
int wCount = sampling, hCount = sampling;
int wGapLeft = w - x;
int hGapLeft = h - y;
if (wCount > wGapLeft) wCount = wGapLeft;
if (hCount > hGapLeft) hCount = hGapLeft;
int[] indexAv;
for (jj = 0; jj < comp; jj++) {
int byteTotal = 0, count = 0, ptr, newPtr;
// noinspection ObjectAllocationInLoop
indexAv = new int[indexCount];
// count pixels in sample we will make into a pixel (ie 2x2 is 4 pixels , 4x4 is 16 pixels)
for (yy = 0; yy < hCount; yy++) {
for (xx = 0; xx < wCount; xx++) {
ptr = ((yy + (y * sampling)) * origLineLength) + (((x * sampling * comp) + (xx * comp) + jj));
if (ptr < oldSize) {
if (!hasIndex) {
byteTotal = byteTotal + (data[ptr] & 255);
}
else {
for (int aa = 0; aa < indexCount; aa++)
indexAv[aa] = indexAv[aa] + (index[(((data[ptr] & 255) * indexCount) + aa)] & 255);
}
count++;
}
}
}
// set value as white or average of pixels
if (hasIndex) {
newPtr = jj + (x * indexCount) + (newW * y * indexCount);
for (int aa = 0; aa < indexCount; aa++)
newData[newPtr + aa] = (byte) ((indexAv[aa]) / count);
}
else
if (count > 0) {
newPtr = jj + (x * comp) + (newW * y * comp);
newData[newPtr] = (byte) ((byteTotal) / count);
}
}
}
}
data = newData;
h = newH;
w = newW;
}
catch (Exception e) {
// tell user and log
if (LogWriter.isOutput()) LogWriter.writeLog("Exception: " + e.getMessage());
}
}
else
if (!isDCT && !isJPX && index == null) {}
}
}
/** handle any decode array */
if (decodeArray == null || decodeArray.length == 0) {}
else
if (Filters != null && (isJPX || isDCT)) { // don't apply on jpegs
}
else
if (index == null) { // for the moment ignore if indexed (we may need to recode)
ImageCommands.applyDecodeArray(data, d, decodeArray, colorspaceID, XObject);
}
if (imageMask) {
/** create an image from the raw data */
// allow for 1 x 1 pixel
/**
* allow for 1 x 1 pixels scaled up or fine lines
*/
float ratio = ((float) h) / (float) w;
if ((this.isPrinting && ratio < 0.1f && w > 4000 && h > 1) || (ratio < 0.001f && w > 4000 && h > 1) || (w == 1 && h == 1)) {// &&
// data[0]!=0){
float ix = this.gs.CTM[2][0];
float iy = this.gs.CTM[2][1];
float ih = this.gs.CTM[1][1];
if (ih == 0) ih = this.gs.CTM[1][0];
if (ih < 0) {
iy = iy + ih;
ih = -ih;
}
float iw = this.gs.CTM[0][0];
if (iw == 0) iw = this.gs.CTM[0][1];
if (iw < 0) {
ix = ix + iw;
iw = -iw;
}
// factor in GS rotation and swap w and h
if (this.gs.CTM[0][0] == 0 && this.gs.CTM[0][1] > 0 && this.gs.CTM[1][0] != 0 && this.gs.CTM[1][1] == 0) {
float tmp = ih;
ih = iw;
iw = tmp;
}
// allow for odd values less than 1 and ensure minimum width
if (iw < 1) iw = 1;
if (ih < 1) ih = 1;
int lwidth = -1;
// for thin lines, use line width to ensure appears
if (ih < 3) {
lwidth = (int) ih;
ih = 1;
}
else
if (iw < 3) {
lwidth = (int) iw;
iw = 1;
}
GeneralPath currentShape = new GeneralPath(Path2D.WIND_NON_ZERO);
currentShape.moveTo(ix, iy);
currentShape.lineTo(ix, iy + ih);
currentShape.lineTo(ix + iw, iy + ih);
currentShape.lineTo(ix + iw, iy);
currentShape.closePath();
// save for later
if (this.renderPage && currentShape != null) {
float lastLineWidth = this.gs.getLineWidth();
if (lwidth > 0) this.gs.setLineWidth(lwidth);
this.gs.setNonstrokeColor(this.gs.nonstrokeColorSpace.getColor());
this.gs.setFillType(GraphicsState.FILL);
this.current.drawShape(currentShape, this.gs, Cmd.F);
// restore after draw
if (lwidth > 0) this.gs.setLineWidth(lastLineWidth);
}
return null;
}
else
if (h == 2 && d == 1 && ImageCommands.isRepeatingLine(data, h)) {
/* Takes account of ef1603e.pdf. A thin horizontal dotted line is not scaled properly, therefore its converted to a shape. */
/* Condition is only executed if line is uniform along vertical axis */
float ix = this.gs.CTM[2][0];
float iy = this.gs.CTM[2][1];
float ih = this.gs.CTM[1][1];
float iw = this.gs.CTM[0][0];
// factor in GS rotation and swap w and h
if (this.gs.CTM[0][0] == 0 && this.gs.CTM[0][1] > 0 && this.gs.CTM[1][0] != 0 && this.gs.CTM[1][1] == 0) {
float tmp = ih;
ih = iw;
iw = tmp;
}
double byteWidth = iw / (data.length / h);
double bitWidth = byteWidth / 8;
for (int col = 0; col < data.length / h; col++) {
int currentByte = data[col] & 0xff;
currentByte = ~currentByte & 0xff;
int bitCount = 8;
double endX = 0, startX;
boolean draw = false;
while (currentByte != 0 || draw) {
bitCount--;
if ((currentByte & 0x1) == 1) {
if (!draw) {
endX = ((bitCount + 0.5) * bitWidth) + (col * byteWidth);
draw = true;
}
}
else
if (draw) {
draw = false;
startX = ((bitCount + 0.5) * bitWidth) + (col * byteWidth);
GeneralPath currentShape = new GeneralPath(Path2D.WIND_NON_ZERO);
currentShape.moveTo((float) (ix + startX), iy);
currentShape.lineTo((float) (ix + startX), iy + ih);
currentShape.lineTo((float) (ix + endX), iy + ih);
currentShape.lineTo((float) (ix + endX), iy);
currentShape.closePath();
// save for later
if (this.renderPage && currentShape != null) {
this.gs.setNonstrokeColor(this.gs.nonstrokeColorSpace.getColor());
this.gs.setFillType(GraphicsState.FILL);
this.current.drawShape(currentShape, this.gs, Cmd.F);
}
}
currentByte = currentByte >>> 1;
}
}
return null;
}
else {
// see if black and back object
if (isDownsampled) {
/** create an image from the raw data */
DataBuffer db = new DataBufferByte(data, data.length);
int[] bands = { 0, 1, 2, 3 };
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Raster raster = Raster.createInterleavedRaster(db, w, h, w * 4, 4, bands, null);
image.setData(raster);
}
else {
// try to keep as binary if possible
boolean hasObjectBehind = true;
if (h < 20) // added as found file with huge number of tiny tiles
hasObjectBehind = true;
else
if (mode != ImageCommands.ID) // not worth it for inline image
hasObjectBehind = this.current.hasObjectsBehind(this.gs.CTM);
// remove empty images in some files
boolean isBlank = false, keepNonTransparent = false;
if (imageMask && d == 1 && decodeColorData.getID() == ColorSpaces.DeviceRGB && maskCol[0] == 0 && maskCol[1] == 0
&& maskCol[2] == 0) {
// see if blank (assume true and disprove) and remove as totally see-through
isBlank = true;
for (int aa = 0; aa < data.length; aa++) {
if (data[aa] != -1) {
isBlank = false;
aa = data.length;
}
}
if (this.isPrinting && (mode == ImageCommands.ID || this.isType3Font || d == 1)) { // avoid transparency if possible
WritableRaster raster = Raster.createPackedRaster(new DataBufferByte(data, data.length), w, h, 1, null);
image = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY);
image.setData(raster);
keepNonTransparent = true;
}
else
if (isBlank) {
image = null;
removed = true;
}
else {
byte[] newIndex = { (maskCol[0]), (maskCol[1]), (maskCol[2]), (byte) 255, (byte) 255, (byte) 255 };
image = ColorSpaceConvertor.convertIndexedToFlat(d, w, h, data, newIndex, true, true);
}
}
if (isBlank) {
// done above so ignore
}
else
if (!this.isPrinting && maskCol[0] == 0 && maskCol[1] == 0 && maskCol[2] == 0 && !hasObjectBehind && !this.isType3Font
&& decodeColorData.getID() != ColorSpaces.DeviceRGB) {
if (d == 1) {
WritableRaster raster = Raster.createPackedRaster(new DataBufferByte(data, data.length), w, h, 1, null);
image = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY);
image.setData(raster);
}
else { // down-sampled above //never called
final int[] bands = { 0 };
Raster raster = Raster.createInterleavedRaster(new DataBufferByte(data, data.length), w, h, w, 1, bands, null);
image = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
image.setData(raster);
}
}
else
if (!keepNonTransparent) {
// if(hasObjectBehind){
// image=ColorSpaceConvertor.convertToARGB(image);
if (d == 8 && isDownsampled) { // never called
byte[] newIndex = { (maskCol[0]), (maskCol[1]), (maskCol[2]), (byte) 255, (byte) 255, (byte) 255 };
image = ColorSpaceConvertor.convertIndexedToFlat(d, w, h, data, newIndex, true, true);
// }else if(isType3Font){
// WritableRaster raster =Raster.createPackedRaster(new DataBufferByte(data, data.length), w, h, 1, null);
// image =new BufferedImage(w,h,BufferedImage.TYPE_BYTE_BINARY);
// image.setData(raster);
// System.out.println(image.getType()+" "+image);
}
else
if ((w < 4000 && h < 4000) || hasObjectBehind) { // needed for hires
byte[] newIndex = { maskCol[0], maskCol[1], maskCol[2], (byte) 255, (byte) 255, (byte) 255 };
image = ColorSpaceConvertor.convertIndexedToFlat(1, w, h, data, newIndex, true, false);
// }
}
else {
// WritableRaster raster =Raster.createPackedRaster(new DataBufferByte(data, data.length), w, h, d, null);
// ismage = new BufferedImage(new IndexColorModel(d, 1, maskCol, 0, false), raster, false, null);
/**/
}
}
}
}
}
else
if (Filters == null) { // handle no filters
// save out image
if (LogWriter.isOutput()) LogWriter.writeLog("Image " + name + ' ' + w + "W * " + h + "H with No Compression at BPC " + d);
image = makeImage(decodeColorData, w, h, d, data, compCount, XObject);
}
else
if (isDCT) { // handle JPEGS
if (LogWriter.isOutput()) LogWriter.writeLog("JPeg Image " + name + ' ' + w + "W * " + h + 'H' + " arrayInverted="
+ arrayInverted);
/**
* get image data,convert to BufferedImage from JPEG & save out
*/
if (colorspaceID == ColorSpaces.DeviceCMYK && this.extractRawCMYK) {
if (LogWriter.isOutput()) LogWriter.writeLog("Raw CMYK image " + name + " saved.");
if (!this.objectStoreStreamRef.saveRawCMYKImage(data, name)) this.errorTracker
.addPageFailureMessage("Problem saving Raw CMYK image " + name);
}
/**
* try { java.io.FileOutputStream a =new java.io.FileOutputStream("/Users/markee/Desktop/"+ name + ".jpg");
*
* a.write(data); a.flush(); a.close();
*
* } catch (Exception e) { LogWriter.writeLog("Unable to save jpeg " + name);
*
* } /
**/
// if ICC with Alt RGB, use alternative first
boolean decodedOnAltColorspace = false;
if (decodeColorData.getID() == ColorSpaces.ICC) {
// try first and catch any error
int alt = decodeColorData.getAlternateColorSpace();
GenericColorSpace altDecodeColorData = null;
if (alt == ColorSpaces.DeviceRGB) altDecodeColorData = new DeviceRGBColorSpace();
else
if (alt == ColorSpaces.DeviceCMYK) altDecodeColorData = new DeviceCMYKColorSpace();
// try if any alt found
if (altDecodeColorData != null) {
try {
image = altDecodeColorData.JPEGToRGBImage(data, w, h, decodeArray, pX, pY, arrayInverted, XObject);
// if it returns image it worked flag and switch over
if (image != null) {
decodedOnAltColorspace = true;
decodeColorData = altDecodeColorData;
// flag if YCCK
if (decodeColorData.isImageYCCK()) this.hasYCCKimages = true;
}
}
catch (Exception e) {
this.errorTracker.addPageFailureMessage("Unable to use alt colorspace with " + name + " to JPEG");
// tell user and log
if (LogWriter.isOutput()) LogWriter.writeLog("Exception: " + e.getMessage());
image.flush();
image = null;
}
}
}
/** decode if not done above */
if (!decodedOnAltColorspace) {
// separation, renderer
try {
image = decodeColorData.JPEGToRGBImage(data, w, h, decodeArray, pX, pY, arrayInverted, XObject);
// flag if YCCK
if (decodeColorData.isImageYCCK()) this.hasYCCKimages = true;
// image=simulateOP(image);
}
catch (Exception e) {
this.errorTracker.addPageFailureMessage("Problem converting " + name + " to JPEG");
// tell user and log
if (LogWriter.isOutput()) LogWriter.writeLog("Exception: " + e.getMessage());
image.flush();
image = null;
}
/**
* catch(Error err){ addPageFailureMessage("Problem converting "+name+" to JPEG");
*
* image=null; }/
**/
}
type = "jpg";
// set in makeImage so not set for JPEGS - we do it explicitly here
setRotationOptionsOnJPEGImage();
}
else
if (isJPX) { // needs imageio library
if (LogWriter.isOutput()) LogWriter.writeLog("JPeg 2000 Image " + name + ' ' + w + "W * " + h + 'H');
/**
* try { java.io.FileOutputStream a =new java.io.FileOutputStream("/Users/markee/Desktop/"+ name + ".jpg");
*
* a.write(data); a.flush(); a.close();
*
* } catch (Exception e) { LogWriter.writeLog("Unable to save jpeg " + name);
*
* } /
**/
if (JAIHelper.isJAIused()) {
image = decodeColorData.JPEG2000ToRGBImage(data, w, h, decodeArray, pX, pY);
type = "jpg";
}
else {
if (System.getProperty("org.jpedal.jai") != null && System.getProperty("org.jpedal.jai").toLowerCase().equals("true")) {
if (!ImageCommands.JAImessageShow) {
ImageCommands.JAImessageShow = true;
System.err.println("JPeg 2000 Images need both JAI and imageio.jar on classpath");
}
throw new RuntimeException("JPeg 2000 Images need both JAI and imageio.jar on classpath");
}
else {
System.err.println("JPeg 2000 Images needs the VM parameter -Dorg.jpedal.jai=true switch turned on");
throw new RuntimeException("JPeg 2000 Images needs the VM parameter -Dorg.jpedal.jai=true switch turned on");
}
}
// set in makeImage so not set for JPEGS - we do it explicitly here
setRotationOptionsOnJPEGImage();
}
else { // handle other types
if (LogWriter.isOutput()) LogWriter.writeLog(name + ' ' + w + "W * " + h + "H BPC=" + d + ' ' + decodeColorData);
image = makeImage(decodeColorData, w, h, d, data, compCount, XObject);
// choose type on basis of size and avoid ICC as they seem to crash the Java class
if (d == 8 || this.gs.nonstrokeColorSpace.getID() == ColorSpaces.DeviceRGB
|| this.gs.nonstrokeColorSpace.getID() == ColorSpaces.ICC) type = "jpg";
}
if (image != null) {
if (newSMask != null && DecodeParms == null) {
DecodeParms = newSMask.getDictionary(PdfDictionary.DecodeParms);
/**
* handle tiny gray dot used with larger mask for images in MSword (Customers customers-dec2011/January_Good_News2.pdf)
*/
if (decodeColorData.getID() == ColorSpaces.DeviceGray && w < newSMask.getInt(PdfDictionary.Width)
&& h < newSMask.getInt(PdfDictionary.Height)) {
// check data is all black
int bytes = data.length;
boolean isEmpty = true;
for (int aa = 0; aa < bytes; aa++) {
if (data[aa] != 0) {
isEmpty = false;
aa = bytes;
}
}
if (isEmpty) {
image = new BufferedImage(newSMask.getInt(PdfDictionary.Width), newSMask.getInt(PdfDictionary.Height),
BufferedImage.TYPE_BYTE_GRAY);
}
}
}
/** handle any soft mask */
if (newSMask != null) image = addSMaskObject(decodeColorData, data, name, w, h, XObject, isDCT, isJPX, image, DecodeParms, newSMask);
else
if (newMask != null) image = ImageCommands.addMaskObject(decodeColorData, d, isDCT, isJPX, image, colorspaceID, index, newMask,
this.optionsApplied, this.currentPdfFile);
if (image != null) image = ImageCommands.simulateOverprint(decodeColorData, data, isDCT, isJPX, image, colorspaceID, newMask, newSMask,
this.current, this.gs);
if (image == null) return null;
if ((this.current.getType() == DynamicVectorRenderer.CREATE_HTML || this.current.getType() == DynamicVectorRenderer.CREATE_SVG || this.current
.getType() == DynamicVectorRenderer.CREATE_JAVAFX)) {}
else
if (!this.renderDirectly && (this.finalImagesExtracted || this.rawImagesExtracted) && !this.cache.testIfImageAlreadySaved(XObject)) saveImage(
name, this.createScaledVersion, image, type);
// //Original save image code
// if(!renderDirectly)
// saveImage(name, createScaledVersion, image, type);
}
if (image == null && !removed) {
this.imagesProcessedFully = false;
}
// apply any transfer function
PdfObject TR = this.gs.getTR();
if (TR != null) // array of values
image = ImageCommands.applyTR(image, TR, this.currentPdfFile);
// try to simulate some of blend by removing white if not bottom image
if (DecodeParms != null && DecodeParms.getInt(PdfDictionary.Blend) != PdfDictionary.Unknown && this.current.hasObjectsBehind(this.gs.CTM)
&& image != null && image.getType() != 2 && (!isDCT || DecodeParms.getInt(PdfDictionary.QFactor) == 0)) image = ImageCommands
.makeBlackandWhiteTransparent(image);
// sharpen 1 bit
if (pX > 0 && pY > 0 && rawd == 1 && ImageCommands.sharpenDownsampledImages
&& (decodeColorData.getID() == ColorSpaces.DeviceGray || decodeColorData.getID() == ColorSpaces.DeviceRGB)) {
Kernel kernel = new Kernel(3, 3, new float[] { -1, -1, -1, -1, 9, -1, -1, -1, -1 });
BufferedImageOp op = new ConvolveOp(kernel);
image = op.filter(image, null);
}
// number of images used for caching
this.imageCount++;
/**
* transparency slows down printing so try to reduce if possible in printing
*/
if (mode == ImageCommands.ID && this.isPrinting && image != null && d == 1 && maskCol != null && maskCol[0] == 0 && maskCol[1] == 0
&& maskCol[2] == 0 && maskCol[3] == 0) {
int iw = image.getWidth();
int ih = image.getHeight();
BufferedImage newImage = new BufferedImage(iw, ih, BufferedImage.TYPE_BYTE_GRAY);
newImage.getGraphics().setColor(Color.WHITE);
newImage.getGraphics().fillRect(0, 0, iw, ih);
newImage.getGraphics().drawImage(image, 0, 0, null);
image = newImage;
}
return image;
}
/**
* needs to be explicitly set for JPEG images if getting image from object
*/
private void setRotationOptionsOnJPEGImage() {
if (this.imageStatus > 0 && this.gs.CTM[0][0] > 0 && this.gs.CTM[0][1] > 0 && this.gs.CTM[1][1] > 0 && this.gs.CTM[1][0] < 0) {
/**/
// we need a different op for Image and viewer as we handle in diff ways
if (this.imageStatus == IMAGE_getImageFromPdfObject) {
// optionsApplied=optionsApplied+ PDFImageProcessing.IMAGE_INVERTED;
this.gs.CTM[0][1] = -this.gs.CTM[0][1];
// gs.CTM[1][0]=gs.CTM[1][0];
this.gs.CTM[1][1] = -this.gs.CTM[1][1];
this.gs.CTM[2][1] = this.gs.CTM[2][1] - this.gs.CTM[1][1];
}
else
if (this.imageStatus == SCREEN_getImageFromPdfObject) this.optionsApplied = this.optionsApplied + PDFImageProcessing.IMAGE_INVERTED;
/**/
}
else
if (this.optionsApplied > 0 && this.gs.CTM[0][0] < 0 && this.gs.CTM[1][1] < 0 && this.gs.CTM[0][1] == 0 && this.gs.CTM[1][0] == 0) { // fix
// for
// elfo.pdf
// optionsApplied=optionsApplied+ PDFImageProcessing.IMAGE_INVERTED;
// gs.CTM[0][1]=-gs.CTM[0][1];
// gs.CTM[1][0]=gs.CTM[1][0];
this.gs.CTM[1][1] = -this.gs.CTM[1][1];
this.gs.CTM[2][1] = this.gs.CTM[2][1] - this.gs.CTM[1][1] + this.gs.CTM[1][0];
// gs.CTM[2][0]=gs.CTM[2][0]+30;
this.gs.CTM[2][0] = this.gs.CTM[2][0] - this.gs.CTM[0][1];
}
}
private void saveImage(String name, boolean createScaledVersion, BufferedImage image, String type) {
if (image != null && image.getSampleModel().getNumBands() == 1) type = "tif";
if (this.isPageContent && (this.renderImages || this.finalImagesExtracted || this.clippedImagesExtracted || this.rawImagesExtracted)) {
/** create copy and scale if required */
this.objectStoreStreamRef.saveStoredImage(name, ImageCommands.addBackgroundToMask(image, this.isMask), false, createScaledVersion, type);
}
}
/**
* turn raw data into a BufferedImage
*/
private BufferedImage makeImage(GenericColorSpace decodeColorData, int w, int h, int d, byte[] data, int comp, PdfObject XObject) {
// ensure correct size
if (decodeColorData.getID() == ColorSpaces.DeviceGray) {
if (d == 1) {
int requiredSize = ((w + 7) >> 3) * h;
int oldSize = data.length;
if (oldSize < requiredSize) {
byte[] oldData = data;
data = new byte[requiredSize];
System.arraycopy(oldData, 0, data, 0, oldSize);
// and fill rest with 255 for white
for (int aa = oldSize; aa < requiredSize; aa++)
data[aa] = (byte) 255;
}
}
else
if (d == 8) {
int requiredSize = w * h;
int oldSize = data.length;
if (oldSize < requiredSize) {
byte[] oldData = data;
data = new byte[requiredSize];
System.arraycopy(oldData, 0, data, 0, oldSize);
}
}
}
/**
* put data into separate array. If we keep in PdfData then on pages where same image reused such as adobe/Capabilities and precisons, its
* flipped each time as its an object :-(
*/
// int byteCount=rawData.length;
// byte[] data=new byte[byteCount];
// System.arraycopy(rawData, 0, data, 0, byteCount);
ColorSpace cs = decodeColorData.getColorSpace();
int ID = decodeColorData.getID();
BufferedImage image = null;
byte[] index = decodeColorData.getIndexedMap();
this.optionsApplied = PDFImageProcessing.NOTHING;
/** fast op on data to speed up image manipulation */
// optimse rotations here as MUCH faster and flag we have done this
// something odd happens if CTM[2][1] is negative so factor ignore this case
boolean isInverted = !this.doNotRotate && this.useHiResImageForDisplay && RenderUtils.isInverted(this.gs.CTM);
boolean isRotated = !this.doNotRotate && this.useHiResImageForDisplay && RenderUtils.isRotated(this.gs.CTM);
// do not apply to mask
// last condition is for /Users/markee/PDFdata/baseline_screens/debug2/StampsProblems.pdf
if (XObject.getGeneralType(PdfDictionary.Type) == PdfDictionary.Mask && this.streamType != ValueTypes.FORM) {
isInverted = false;
isRotated = false;
}
// This fix was masking miscalculation.
// fix for image wrong on Customers3/ImageQualitySample_v0.2.docx.pdf
// if(isInverted && gs.CTM[1][1]>0 && gs.lastCTM[1][1]<0)
// isInverted=false;
//
// if(renderDirectly && ! this.isType3Font){
// isInverted=false;
// isRotated=false;
// }
// I optimised the code slightly - you were setting booleans are they had been
// used - I removed so it keeps code shorter
if (isInverted) {// invert at byte level with copy
// needs to be 1 for sep
int count = comp;
if (ID == ColorSpaces.Separation) count = 1;
else
if (ID == ColorSpaces.DeviceN) count = decodeColorData.getColorComponentCount();
byte[] processedData = ImageOps.invertImage(data, w, h, d, count, index);
if (processedData != null) {
data = processedData;
this.optionsApplied = this.optionsApplied + PDFImageProcessing.IMAGE_INVERTED;
}
}
if (isRotated) { // rotate at byte level with copy New Code still some issues
byte[] processedData = ImageOps.rotateImage(data, w, h, d, comp, index);
if (processedData != null) {
data = processedData;
this.optionsApplied = this.optionsApplied + PDFImageProcessing.IMAGE_ROTATED;
// reset
int temp = h;
h = w;
w = temp;
}
}
// data=ColorSpaceConvertor.convertIndexedToFlat(d,w, h, data, index, 255);
// System.out.println("index="+index);
if (index != null) { // indexed images
if (LogWriter.isOutput()) LogWriter.writeLog("Indexed " + w + ' ' + h);
/** convert index to rgb if CMYK or ICC */
if (!decodeColorData.isIndexConverted()) {
index = decodeColorData.convertIndexToRGB(index);
}
// workout size and check in range
// int size =decodeColorData.getIndexSize()+1;
// pick out daft setting of totally empty iamge and ignore
if (d == 8 && decodeColorData.getIndexSize() == 0 && decodeColorData.getID() == ColorSpaces.DeviceRGB) {
boolean hasPixels = false;
int indexCount = index.length;
for (int ii = 0; ii < indexCount; ii++) {
if (index[ii] != 0) {
hasPixels = true;
ii = indexCount;
}
}
if (!hasPixels) {
int pixelCount = data.length;
for (int ii = 0; ii < pixelCount; ii++) {
if (data[ii] != 0) {
hasPixels = true;
ii = pixelCount;
}
}
}
if (!hasPixels) {
return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
}
}
// allow for half bytes (ie bootitng.pdf)
// if(d==4 && size>16)
// size=16;
// WritableRaster raster =Raster.createPackedRaster(db, w, h, d, null);
// ColorModel cm=new IndexColorModel(d, size, index, 0, false);
// image = new BufferedImage(cm, raster, false, null);
// if(debugColor)
// System.out.println("xx d="+d+" w="+w+" data="+data.length+" index="+index.length+" size="+size);
try {
// remove image in Itext which is white on white
if (d == 1 && index.length == 6 && index[0] == index[3] && index[1] == index[4] && index[2] == index[5]) {
image = null;
// optimise silly Itext case of 1=1 white indexed image customers-dec2011/gattest.pdf
}
else
if (d == 8 && w == 1 && h == 1 && index[0] == -1 && index[1] == -1 && index[2] == -1 && allBytesZero(data)) {
image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
image.createGraphics().setPaint(Color.CYAN);
Raster raster = ColorSpaceConvertor.createInterleavedRaster((new byte[] { (byte) 255, (byte) 255, (byte) 255 }), 1, 1);
image.setData(raster);
}
else {
image = ColorSpaceConvertor.convertIndexedToFlat(d, w, h, data, index, false, false);
}
}
catch (Exception e) {
// tell user and log
if (LogWriter.isOutput()) LogWriter.writeLog("Exception: " + e.getMessage());
}
}
else
if (d == 1) { // bitmaps next
image = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY);
/** create an image from the raw data */
DataBuffer db = new DataBufferByte(data, data.length);
// needs to be inverted in this case (ie customers-dec2012/mariners_annual_2012.pdf)
if (decodeColorData.getID() == ColorSpaces.Separation) {
int count = data.length;
for (int aa = 0; aa < count; aa++)
data[aa] = (byte) (data[aa] ^ 255);
}
WritableRaster raster = Raster.createPackedRaster(db, w, h, d, null);
image.setData(raster);
}
else
if (ID == ColorSpaces.Separation || ID == ColorSpaces.DeviceN) {
if (LogWriter.isOutput()) LogWriter.writeLog("Converting Separation/DeviceN colorspace to sRGB ");
image = decodeColorData.dataToRGB(data, w, h);
// direct images
}
else
if (comp == 4) { // handle CMYK or ICC
if (LogWriter.isOutput()) LogWriter.writeLog("Converting ICC/CMYK colorspace to sRGB ");
image = ColorSpaceConvertor.convertFromICCCMYK(w, h, data);
// ShowGUIMessage.showGUIMessage("y",image,"y");
}
else
if (comp == 3) {
if (LogWriter.isOutput()) LogWriter.writeLog("Converting 3 comp colorspace to sRGB index=" + index);
// work out from size what sort of image data we have
if (w * h == data.length) {
if (d == 8 && index != null) {
image = ColorSpaceConvertor.convertIndexedToFlat(d, w, h, data, index, false, false);
}
else {
/** create an image from the raw data */
DataBuffer db = new DataBufferByte(data, data.length);
int[] bands = { 0 };
image = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
Raster raster = Raster.createInterleavedRaster(db, w, h, w, 1, bands, null);
image.setData(raster);
}
}
else {
if (LogWriter.isOutput()) LogWriter.writeLog("Converting data to sRGB ");
// expand out 4 bit raster as does not appear to be easy way
if (d == 4) {
int origSize = data.length;
int newSize = w * h * 3;
boolean isOdd = (w & 1) == 1;
int scanLine = ((w * 3) + 1) >> 1;
byte[] newData = new byte[newSize];
byte rawByte;
int ptr = 0, currentLine = 0;
for (int ii = 0; ii < origSize; ii++) {
rawByte = data[ii];
currentLine++;
newData[ptr] = (byte) (rawByte & 240);
if (newData[ptr] == -16) // fix for white
newData[ptr] = (byte) 255;
ptr++;
if ((currentLine) == scanLine && isOdd) { // ignore pack bit at end of odd line
currentLine = 0;
}
else {
newData[ptr] = (byte) ((rawByte & 15) << 4);
if (newData[ptr] == -16) // fix for white
newData[ptr] = (byte) 255;
ptr++;
}
if (ptr == newSize) ii = origSize;
}
data = newData;
}
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
data = ImageOps.checkSize(data, w, h, 3);
Raster raster = ColorSpaceConvertor.createInterleavedRaster(data, w, h);
image.setData(raster);
}
}
else
if (comp == 1) {
if (LogWriter.isOutput()) LogWriter.writeLog("comp=1 and d= " + d);
if (d != 8) {
int newSize = w * h;
byte[] newData = new byte[newSize];
// Java needs 8 bit so expand out
switch (d) {
case 2:
ColorSpaceConvertor.flatten2bpc(w, data, null, false, newSize, newData);
break;
case 4:
ColorSpaceConvertor.flatten4bpc(w, data, newSize, newData);
break;
default:
if (LogWriter.isOutput()) LogWriter.writeLog("unknown comp= " + d);
}
data = newData;
}
/** create an image from the raw data */
DataBuffer db = new DataBufferByte(data, data.length);
int[] bands = { 0 };
image = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
Raster raster = Raster.createInterleavedRaster(db, w, h, w, 1, bands, null);
image.setData(raster);
}
else
if (LogWriter.isOutput()) LogWriter.writeLog("Image " + cs.getType() + " not currently supported with components "
+ comp);
return image;
}
private static boolean allBytesZero(byte[] data) {
boolean allZero = true;
for (byte bytes : data) {
if (bytes != 0) {
allZero = false;
break;
}
}
return allZero;
}
private BufferedImage addSMaskObject(GenericColorSpace decodeColorData, byte[] data, String name, int w, int h, PdfObject XObject, boolean isDCT,
boolean isJPX, BufferedImage image, PdfObject DecodeParms, PdfObject newSMask) throws PdfException {
{
BufferedImage smaskImage;
/** read the stream */
byte[] objectData = this.currentPdfFile.readStream(newSMask, true, true, false, false, false,
newSMask.getCacheName(this.currentPdfFile.getObjectReader()));
if (objectData != null) {
boolean ignoreMask = DecodeParms != null && DecodeParms.getInt(PdfDictionary.Colors) != -1
&& DecodeParms.getInt(PdfDictionary.Predictor) != 15 && decodeColorData.getID() != ColorSpaces.ICC;
// special case
PdfObject maskColorSpace = newSMask.getDictionary(PdfDictionary.ColorSpace);
if (ignoreMask && (decodeColorData.getID() == ColorSpaces.DeviceRGB || decodeColorData.getID() == ColorSpaces.DeviceCMYK)
&& maskColorSpace.getParameterConstant(PdfDictionary.ColorSpace) == ColorSpaces.DeviceGray) ignoreMask = false;
// ignore in this case of blank Smask on jpeg with grayscale
if (isDCT && maskColorSpace.getParameterConstant(PdfDictionary.ColorSpace) == ColorSpaces.DeviceGray) {
int len = objectData.length;
ignoreMask = true;
for (int aa = 0; aa < len; aa++) {
if (objectData[aa] != -1) {
ignoreMask = false;
aa = len;
}
}
}
// ignoreMask is hack to fix odd Visuality files
if (!ignoreMask) {
int rawOptions = this.optionsApplied;
if (this.optionsApplied == PDFImageProcessing.NOTHING) this.doNotRotate = true;
int maskW = newSMask.getInt(PdfDictionary.Width);
int maskH = newSMask.getInt(PdfDictionary.Height);
boolean isWhiteAndDownscaled = false;
boolean isIndexed = false;
if (isWhiteAndDownscaled) {
PdfObject XObjectColorSpace = XObject.getDictionary(PdfDictionary.ColorSpace);
// PdfObject maskColorSpace=newSMask.getDictionary(PdfDictionary.ColorSpace);
PdfArrayIterator maskFilters = newSMask.getMixedArray(PdfDictionary.Filter);
boolean isJBIG2 = false;
// only needed for this case
if (XObjectColorSpace.getParameterConstant(PdfDictionary.ColorSpace) == ColorSpaces.DeviceRGB) {
int maskFirstValue;
if (maskFilters != null && maskFilters.hasMoreTokens()) {
while (maskFilters.hasMoreTokens()) {
maskFirstValue = maskFilters.getNextValueAsConstant(true);
isJBIG2 = maskFirstValue == PdfFilteredReader.JBIG2Decode;
}
}
}
// special case customers3/si_test.pdf
isIndexed = data.length == 2 && XObjectColorSpace.getParameterConstant(PdfDictionary.ColorSpace) == ColorSpaces.Indexed;
isWhiteAndDownscaled = XObjectColorSpace != null
&& ((XObjectColorSpace.getParameterConstant(PdfDictionary.ColorSpace) == ColorSpaces.DeviceRGB && (!isDCT || isJBIG2)) || (isIndexed && XObjectColorSpace
.getDictionary(PdfDictionary.Indexed).getParameterConstant(PdfDictionary.ColorSpace) == ColorSpaces.DeviceRGB))
&& maskColorSpace.getParameterConstant(PdfDictionary.ColorSpace) == ColorSpaces.DeviceGray;
}
if ((isWhiteAndDownscaled && (isDCT || isJPX || isIndexed))) {
// invert and get image
int c = objectData.length;
for (int ii = 0; ii < c; ii++)
objectData[ii] = (byte) (((byte) 255) - objectData[ii]);
image = processImageXObject(newSMask, name, objectData, true, null);
}
else {
smaskImage = processImageXObject(newSMask, name, objectData, true, null);
// factor in a rotation to SMask
if (this.pageNum > 0 && this.pageData.getRotation(this.pageNum) == 0 && rawOptions == PDFImageProcessing.IMAGE_ROTATED) {
int width = smaskImage.getWidth();
int height = smaskImage.getHeight();
BufferedImage biFlip = new BufferedImage(height, width, smaskImage.getType());
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++) {
biFlip.setRGB(height - 1 - j, i, smaskImage.getRGB(i, j));
}
smaskImage = biFlip;
}
// restore
this.doNotRotate = false;
this.optionsApplied = rawOptions;
// apply mask
if (smaskImage != null) {
image = ImageCommands.applySmask(image, smaskImage, newSMask, false, decodeColorData.getID() == ColorSpaces.DeviceRGB,
newSMask.getDictionary(PdfDictionary.ColorSpace), XObject, this.gs);
smaskImage.flush();
}
}
}
}
}
return image;
}
}