
org.jpedal.parser.XFormDecoder Maven / Gradle / Ivy
/*
* ===========================================
* 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-2015 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
*
* ---------------
* XFormDecoder.java
* ---------------
*/
package org.jpedal.parser;
import org.jpedal.PdfDecoderInt;
import org.jpedal.color.GenericColorSpace;
import org.jpedal.exception.PdfException;
import org.jpedal.external.ExternalHandlers;
import org.jpedal.objects.GraphicsState;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.parser.image.ImageCommands;
import org.jpedal.parser.image.MaskUtils;
import org.jpedal.parser.image.XForm;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.Matrix;
import java.awt.*;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
public class XFormDecoder {
/**
* recursive subroutine so in actual body of PdfStreamDecoder so it can recall decodeStream
* @param pdfStreamDecoder
* @param dataPointer
*/
static void processXForm(final PdfStreamDecoder pdfStreamDecoder, final int dataPointer, final PdfObject XObject, final Shape defaultClip, final CommandParser parser) {
final boolean debug=false;
if(debug) {
System.out.println("processImage " + dataPointer + ' ' + XObject.getObjectRefAsString() + ' ' + defaultClip);
}
final String oldFormName= pdfStreamDecoder.formName;
final String name=parser.generateOpAsString(0, true);
//Removed to fix issue with render to g2
//name is not unique if in form so we add form level to separate out
//if(formLevel>1)
// name= formName+'_'+ formLevel+'_'+name;
//string to hold image details
try {
if(ImageCommands.trackImages){
//add details to string so we can pass back
if(pdfStreamDecoder.imagesInFile ==null) {
pdfStreamDecoder.imagesInFile = name + " Form";
} else {
pdfStreamDecoder.imagesInFile = name + " Form\n" + pdfStreamDecoder.imagesInFile;
}
}
//reset operand
parser.reset();
//read stream for image
final byte[] objectData = pdfStreamDecoder.currentPdfFile.readStream(XObject, true, true, false, false, false, XObject.getCacheName(pdfStreamDecoder.currentPdfFile.getObjectReader()));
if (objectData != null) {
final String oldIndent= PdfStreamDecoder.indent;
PdfStreamDecoder.indent += " ";
//set value and see if Transform matrix
float[] transformMatrix=new float[6];
float[] matrix=XObject.getFloatArray(PdfDictionary.Matrix);
/**
* see if we should ignore scaling because we have already scaled in
* see 14jan/test_de_signature.pdf
*/
final float[] formBBox = XObject.getFloatArray(PdfDictionary.BBox);
if (matrix != null && pdfStreamDecoder.BBox != null && formBBox != null) {
final float Bwidth;
final float Bheight;
if (pdfStreamDecoder.parserOptions.isFlattenedForm()) {
Bwidth = (pdfStreamDecoder.BBox[2] - pdfStreamDecoder.BBox[0]);
Bheight = (pdfStreamDecoder.BBox[3] - pdfStreamDecoder.BBox[1]);
} else {
Bwidth = (pdfStreamDecoder.BBox[2] - pdfStreamDecoder.BBox[0]) / matrix[0];
Bheight = (pdfStreamDecoder.BBox[3] - pdfStreamDecoder.BBox[1]) / matrix[3];
}
if (Bwidth == (formBBox[2] - formBBox[0]) && Bheight == (formBBox[3] - formBBox[1]) && matrix[4]==0 && matrix[5]==0) {
matrix = null;
}
}
final boolean isIdentity=matrix==null || XForm.isIdentity(matrix);
if(matrix!=null) {
transformMatrix = matrix;
}
final float[][] CTM;
final float[][] oldCTM;
final int currentDepth= pdfStreamDecoder.graphicsStates.getDepth();
//allow for stroke line width being altered by scaling
float lineWidthInForm=-1; //negative values not used
//save current
final float[][] currentCTM=new float[3][3];
for(int i=0;i<3;i++) {
System.arraycopy(pdfStreamDecoder.gs.CTM[i], 0, currentCTM[i], 0, 3);
}
oldCTM = currentCTM;
CTM= pdfStreamDecoder.gs.CTM;
float[][] scaleF= pdfStreamDecoder.gs.scaleFactor;
if(matrix!=null && !isIdentity) {
final float[][] scaleFactor={{transformMatrix[0],transformMatrix[1],0},
{transformMatrix[2],transformMatrix[3],0},
{transformMatrix[4],transformMatrix[5],1}};
scaleF=scaleFactor;
pdfStreamDecoder.gs.CTM= Matrix.multiply(scaleFactor, CTM);
//work out line width
lineWidthInForm=transformMatrix[0]* pdfStreamDecoder.gs.getLineWidth();
if (lineWidthInForm == 0) {
lineWidthInForm = transformMatrix[1] * pdfStreamDecoder.gs.getLineWidth();
}
if(lineWidthInForm<0) {
lineWidthInForm = -lineWidthInForm;
}
if(debug) {
System.out.println("setMatrix " + pdfStreamDecoder.gs.CTM[0][0] + ' ' + pdfStreamDecoder.gs.CTM[0][1] + ' ' + pdfStreamDecoder.gs.CTM[1][0] + ' ' + pdfStreamDecoder.gs.CTM[1][1] + ' ' + pdfStreamDecoder.gs.CTM[2][0] + ' ' + pdfStreamDecoder.gs.CTM[2][1]);
}
}
//track depth
pdfStreamDecoder.formLevel++;
//track name so we can make unique key for image name
if(pdfStreamDecoder.formLevel ==1) {
pdfStreamDecoder.formName = name;
} else if(pdfStreamDecoder.formLevel <20) //stop memory issue on silly files
{
pdfStreamDecoder.formName = pdfStreamDecoder.formName + '_' + name;
}
//preserve colorspaces
final GenericColorSpace mainStrokeColorData=(GenericColorSpace) pdfStreamDecoder.gs.strokeColorSpace.clone();
final GenericColorSpace mainnonStrokeColorData=(GenericColorSpace) pdfStreamDecoder.gs.nonstrokeColorSpace.clone();
//set form line width if appropriate
if(lineWidthInForm>0) {
pdfStreamDecoder.gs.setLineWidth(lineWidthInForm);
}
//set gs max to current so child gs values can not exceed
final float maxStrokeValue= pdfStreamDecoder.gs.getAlphaMax(GraphicsState.STROKE);
final float maxFillValue= pdfStreamDecoder.gs.getAlphaMax(GraphicsState.FILL);
final float currentFillValue= pdfStreamDecoder.gs.getAlpha(GraphicsState.FILL);
pdfStreamDecoder.gs.setMaxAlpha(GraphicsState.STROKE, pdfStreamDecoder.gs.getAlpha(GraphicsState.STROKE));
//if(pdfStreamDecoder.formLevel ==1) {
if(pdfStreamDecoder.formLevel<3 && currentFillValue1 && BBox[3]>1 && pdfStreamDecoder.gs.getClippingShape()==null && pdfStreamDecoder.gs.CTM[0][1]==0 && pdfStreamDecoder.gs.CTM[1][0]==0 && pdfStreamDecoder.gs.CTM[2][1]!=0 && pdfStreamDecoder.gs.CTM[2][0]<0){
if(debug) {
System.out.println("setClip1 ");
}
clip = XForm.setClip(defaultClip, BBox, pdfStreamDecoder.gs, pdfStreamDecoder.current);
clipChanged=true;
//System.out.println(BBox[0]+" "+BBox[1]+" "+BBox[2]+" "+BBox[3]);
//Matrix.show(gs.CTM);
}
else if(BBox!=null && BBox[0]==0 && BBox[1]==0 && BBox[2]>1 && BBox[3]>1 && BBox[2]!=BBox[3] && (pdfStreamDecoder.gs.CTM[0][0]>0.99 || pdfStreamDecoder.gs.CTM[2][1]<-1) && (pdfStreamDecoder.gs.CTM[2][0]<-1 || pdfStreamDecoder.gs.CTM[2][0]>1) && pdfStreamDecoder.gs.CTM[2][1]!=0 ){//) && BBox[2]>1 && BBox[3]>1 ){//if(BBox!=null && matrix==null && BBox[0]==0 && BBox[1]==0){
if(debug) {
System.out.println("setClip2");
}
clip = XForm.setClip(defaultClip, BBox, pdfStreamDecoder.gs, pdfStreamDecoder.current);
clipChanged=true;
}
//attempt to fix odd customers3/slides1.pdf text off page issue
//no obvious reason to ignore text on form other than negative y value
//adjusted to fix 11jun/Request_For_Quotation.pdf
//if(formLevel==1 && gs.CTM[0][0]!=0 && gs.CTM[0][0]!=gs.CTM[0][1] && gs.CTM[0][0]!=1 && currentTextState.Tm[0][0]==1 && gs.CTM[1][1]<1f && (gs.CTM[1][1]>0.92f || gs.CTM[0][1]!=0) && currentTextState.Tm[2][1]<0){
//adjusted again to fix 15jan/21130.pdf
//else if(BBox!=null && BBox[0]==0 && BBox[1]==0 && BBox[2]>1 && BBox[3]>1 && (pdfStreamDecoder.formLevel>0 || pdfStreamDecoder.gs.getClippingShape()!=null)){
else if(BBox!=null && BBox[0]==0 && BBox[1]==0 && BBox[2]>1 && BBox[3]>1 && !(pdfStreamDecoder.gs.CTM[2][0]<0) && (pdfStreamDecoder.formLevel>0 || pdfStreamDecoder.gs.getClippingShape()!=null)){
//&& (gs.CTM[0][0]>0.99 || gs.CTM[2][1]<-1) && (gs.CTM[2][0]<-1 || gs.CTM[2][0]>1) && gs.CTM[2][1]!=0 ){
if(debug) {
System.out.println("setClip3");
}
clip = XForm.setClip(defaultClip, BBox, pdfStreamDecoder.gs, pdfStreamDecoder.current);
clipChanged=true;
}else if(pdfStreamDecoder.formLevel >1 && BBox!=null && BBox[0]>50 && BBox[1]>50 && pdfStreamDecoder.gs.getClippingShape()!=null && (BBox[0]-1)> pdfStreamDecoder.gs.getClippingShape().getBounds().x &&
(BBox[1]-1)> pdfStreamDecoder.gs.getClippingShape().getBounds().y ){
// System.out.println("XX form="+formLevel);
// System.out.println(BBox[0]+" "+BBox[1]+" "+BBox[2]+" "+BBox[3]);
// System.out.println(gs.getClippingShape().getBounds()+" "+defaultClip);
if(debug) {
System.out.println("setClip4");
}
clip = XForm.setClip(defaultClip, BBox, pdfStreamDecoder.gs, pdfStreamDecoder.current);
clipChanged=true;
}else if(BBox!=null && BBox[2]>1 && BBox[3]>1 && pdfStreamDecoder.gs.getClippingShape()==null && pdfStreamDecoder.gs.CTM[0][1]>0 && pdfStreamDecoder.gs.CTM[1][0]<0 && pdfStreamDecoder.gs.CTM[0][0]==0 && pdfStreamDecoder.gs.CTM[1][1]==0){
if(debug) {
System.out.println("setClip5");
}
clip = XForm.setClip(defaultClip, BBox, pdfStreamDecoder.gs, pdfStreamDecoder.current);
clipChanged=true;
}else if(debug){
System.out.println("no Clip set");
}
/**decode the stream*/
if(objectData.length>0){
final PdfObject newSMask = XForm.getSMask(BBox, pdfStreamDecoder.gs, pdfStreamDecoder.currentPdfFile); //check for soft mask we need to apply
final int blendMode= pdfStreamDecoder.gs.getBMValue();
/**
* option to include Form as image if extracting images
*/
if((pdfStreamDecoder.parserOptions.getExtractionMode() & PdfDecoderInt.RASTERIZE_FORMS)==PdfDecoderInt.RASTERIZE_FORMS){
processXFormAsImage(XObject, pdfStreamDecoder);
}
if(newSMask!=null || blendMode!=PdfDictionary.Normal){
processXFormWithMaskOrBlend(debug, newSMask, pdfStreamDecoder, blendMode, XObject, name);
}else{
if(debug) {
System.out.println("decode");
}
final int BM= pdfStreamDecoder.gs.getBMValue();
pdfStreamDecoder.decodeStreamIntoObjects(objectData, false);
pdfStreamDecoder.current.setGraphicsState(GraphicsState.STROKE, pdfStreamDecoder.gs.getAlpha(GraphicsState.STROKE), BM);
pdfStreamDecoder.current.setGraphicsState(GraphicsState.FILL, pdfStreamDecoder.gs.getAlpha(GraphicsState.FILL), BM);
}
}
//restore clip if changed
if(clipChanged){
pdfStreamDecoder.gs.setClippingShape(clip);
pdfStreamDecoder.current.drawClip(pdfStreamDecoder.gs, clip, false) ;
}
//restore settings
pdfStreamDecoder.formLevel--;
//allow for stream not correctly setup
pdfStreamDecoder.graphicsStates.correctDepth(currentDepth, pdfStreamDecoder.gs, pdfStreamDecoder.current);
//
//restore old matrix or set default
//fixes 12dec/81564885_1355243032.pdf
if(oldCTM!=null){
pdfStreamDecoder.gs.CTM=oldCTM;
}else if(pdfStreamDecoder.gs.CTM[0][0]==1f && pdfStreamDecoder.gs.CTM[1][1]==1f){
pdfStreamDecoder.gs.CTM=new float[][]{{1,0,0},{0,1,0},{0,0,1}};
}
pdfStreamDecoder.gs.scaleFactor=scaleF;
/**restore old colorspace and fonts*/
pdfStreamDecoder.gs.strokeColorSpace=mainStrokeColorData;
pdfStreamDecoder.gs.nonstrokeColorSpace=mainnonStrokeColorData;
//put back original state
pdfStreamDecoder.cache.restore(mainCache);
//restore gs max to current so child gs values can not exceed
pdfStreamDecoder.gs.setMaxAlpha(GraphicsState.STROKE, maxStrokeValue);
pdfStreamDecoder.gs.setMaxAlpha(GraphicsState.FILL, maxFillValue);
PdfStreamDecoder.indent=oldIndent;
}
} catch (final Error e) {
//tell user and log
if(LogWriter.isOutput()) {
LogWriter.writeLog("Exception: " + e.getMessage());
}
//
pdfStreamDecoder.parserOptions.imagesProcessedFully=false;
pdfStreamDecoder.errorTracker.addPageFailureMessage("Error " + e + " in DO");
if (ExternalHandlers.throwMissingCIDError && e.getMessage()!=null && e.getMessage().contains("kochi")) {
throw e;
}
} catch (final PdfException e) {
if(LogWriter.isOutput()) {
LogWriter.writeLog("Exception " + e);
}
//
pdfStreamDecoder.parserOptions.imagesProcessedFully=false;
pdfStreamDecoder.errorTracker.addPageFailureMessage("Error " + e + " in DO");
}
pdfStreamDecoder.formName =oldFormName;
}
static void processXFormWithMaskOrBlend(final boolean debug, final PdfObject newSMask, final PdfStreamDecoder pdfStreamDecoder, final int blendMode, final PdfObject XObject, final String name) {
// || gs.getAlpha(GraphicsState.FILL)<1f){
if(debug) {
System.out.println("createMaskForm " + newSMask);
}
if(newSMask==null){ //needs to be normal for actual Mask
pdfStreamDecoder.current.setGraphicsState(GraphicsState.STROKE, pdfStreamDecoder.gs.getAlpha(GraphicsState.STROKE), PdfDictionary.Normal);
pdfStreamDecoder.current.setGraphicsState(GraphicsState.FILL, pdfStreamDecoder.gs.getAlpha(GraphicsState.FILL), PdfDictionary.Normal);
}
final boolean useTransparancy=newSMask!=null || blendMode!=PdfDictionary.Normal || pdfStreamDecoder.gs.getAlpha(GraphicsState.FILL)==1f;
MaskUtils.createMaskForm(XObject, name, newSMask, pdfStreamDecoder.gs, pdfStreamDecoder.current, pdfStreamDecoder.currentPdfFile, pdfStreamDecoder.parserOptions, pdfStreamDecoder.formLevel, pdfStreamDecoder.multiplyer, useTransparancy);
if(newSMask==null){
pdfStreamDecoder.current.setGraphicsState(GraphicsState.STROKE, pdfStreamDecoder.gs.getAlpha(GraphicsState.STROKE), blendMode);
pdfStreamDecoder.current.setGraphicsState(GraphicsState.FILL, pdfStreamDecoder.gs.getAlpha(GraphicsState.FILL), blendMode);
}
}
static void processXFormAsImage(final PdfObject XObject, final PdfStreamDecoder pdfStreamDecoder) {
float[] BBox;
BBox= XObject.getFloatArray(PdfDictionary.BBox);
/**get form as an image*/
int fx=(int)BBox[0];
final int fy=(int)BBox[1];
final int fw=(int)BBox[2];
final int fh=(int)(BBox[3]);
//check x,y offsets and factor in
if(fx<0) {
fx = 0;
}
//get the form as an image
final BufferedImage currentImage= MaskUtils.createTransparentForm(XObject, fx, fy, fw, fh, true, pdfStreamDecoder.currentPdfFile, pdfStreamDecoder.parserOptions, pdfStreamDecoder.formLevel, pdfStreamDecoder.multiplyer);
final String imgName='R'+ pdfStreamDecoder.formName;
//store final image on disk & in memory
pdfStreamDecoder.pdfImages.setImageInfo(imgName, pdfStreamDecoder.parserOptions.getPageNumber(), pdfStreamDecoder.gs.CTM[2][0], pdfStreamDecoder.gs.CTM[2][1], fw, fh);
//save the image (R and normal so works with existing code)
pdfStreamDecoder.objectStoreStreamRef.saveStoredImage('R' + imgName, currentImage, false, false, "jpg");
pdfStreamDecoder.objectStoreStreamRef.saveStoredImage(imgName, currentImage, false, false, "jpg");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy