org.jpedal.FileAccess Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of OpenViewerFX Show documentation
Show all versions of OpenViewerFX Show documentation
An Open Source JavaFX PDF Viewer
/*
* ===========================================
* 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-2016 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
*
* ---------------
* FileAccess.java
* ---------------
*/
package org.jpedal;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.stream.ImageInputStream;
import org.jpedal.constants.PDFflags;
import org.jpedal.display.Display;
import org.jpedal.display.PageOffsets;
import org.jpedal.exception.PdfException;
import org.jpedal.external.ExternalHandlers;
import org.jpedal.io.ObjectStore;
import org.jpedal.io.PdfFileReader;
import org.jpedal.io.PdfObjectReader;
import org.jpedal.io.security.DecryptionFactory;
import org.jpedal.linear.LinearParser;
import org.jpedal.objects.Javascript;
import org.jpedal.objects.PdfPageData;
import org.jpedal.objects.PdfResources;
import org.jpedal.objects.acroforms.AcroRenderer;
import org.jpedal.objects.acroforms.actions.ActionHandler;
import org.jpedal.objects.raw.PageObject;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.parser.DecoderOptions;
import org.jpedal.render.DynamicVectorRenderer;
import org.jpedal.render.SwingDisplay;
import org.jpedal.utils.LogWriter;
public class FileAccess {
/**user can open encrypted file with certificate*/
Certificate certificate;
private PageOffsets currentOffset;
private String password;
/**the actual display object*/
private DynamicVectorRenderer currentDisplay; //
//flag to track if page decoded twice
private int lastPageDecoded = -1;
/**used for opening encrypted file*/
PrivateKey key;
/** holds page information used in grouping*/
private PdfPageData pageData = new PdfPageData();
/** the ObjectStore for this file */
private ObjectStore objectStoreRef = new ObjectStore();
public boolean isOpen;
/**
* the size above which objects stored on disk (-1 is off)
*/
int minimumCacheSize = -1;//20000;
public static int bb=-5;
/**
* sed to stop close on method
*/
private boolean closeOnExit=true;
private String filename;
/**
* provide access to pdf file objects
*/
private PdfObjectReader currentPdfFile;
/** count of how many pages loaded */
private int pageCount;
/**current page*/
private int pageNumber=1;
/**flag to stop multiple attempts to decode*/
private boolean isDecoding;
/**handlers file reading/decoding of linear PDF data*/
final LinearParser linearParser=new LinearParser();
final ExternalHandlers externalHandlers;
private final PdfResources res;
private final DecoderOptions options;
public FileAccess(final ExternalHandlers externalHandlers, final PdfResources res, final DecoderOptions options) {
this.externalHandlers=externalHandlers;
this.res=res;
this.options=options;
}
public boolean isFileViewable(final PdfObjectReader currentPdfFile) {
if (currentPdfFile != null){
final PdfFileReader objectReader=currentPdfFile.getObjectReader();
final DecryptionFactory decryption=objectReader.getDecryptionObject();
return decryption==null || decryption.getBooleanValue(PDFflags.IS_FILE_VIEWABLE) || certificate!=null;
}else {
return false;
}
}
void openPdfArray(final byte[] data, final String password) throws PdfException {
this.password=password;
openPdfArray(data);
}
void openPdfArray(final byte[] data) throws PdfException {
if(data==null) {
throw new RuntimeException("Attempting to open null byte stream");
}
if(isOpen) {
//throw new RuntimeException("Previous file not closed");
closePdfFile(); //also checks decoding done
}
isOpen = false;
res.flush();
res.flushObjects();
try {
if(password!=null){
currentPdfFile = new PdfObjectReader(password);
}else{
currentPdfFile = new PdfObjectReader();
}
currentPdfFile.openPdfFile(data);
openPdfFile();
/* store file name for use elsewhere as part of ref key without .pdf */
objectStoreRef.storeFileName("r" + System.currentTimeMillis());
} catch (final PdfException e) {
throw new PdfException("[PDF] OpenPdfArray generated exception "
+ e.getMessage());
}
}
/**
* gets DynamicVector Object
*/
DynamicVectorRenderer getDynamicRenderer() {
return currentDisplay;
}
public PdfResources getRes() {
return res;
}
/**
* gets DynamicVector Object - NOT PART OF API (used by ULC for Swing) and subject to change (DO NOT USE)
*/
DynamicVectorRenderer getDynamicRenderer(final boolean reset) {
final DynamicVectorRenderer latestVersion=currentDisplay;
if(reset) {
currentDisplay=new SwingDisplay(0, objectStoreRef,false);
}
return latestVersion;
}
/**
* routine to open PDF file and extract key info from pdf file so we can
* decode any pages which also sets password.
* Does not actually decode the pages themselves. Also
* reads the form data. You must explicitly close your stream!!
*/
public final void openPdfFileFromStream(final Object filename, final String password) throws PdfException {
//tell JPedal NOT to close stream!!!
closeOnExit=false;
if(filename instanceof ImageInputStream){
final ImageInputStream iis=(ImageInputStream)filename;
if(isOpen) {
//throw new RuntimeException("Previous file not closed");
closePdfFile(); //also checks decoding done
}
isOpen = false;
this.filename = "ImageInputStream" + System.currentTimeMillis();
org.jpedal.DevFlags.currentFile = this.filename;
res.flush();
res.flushObjects();
/* store file name for use elsewhere as part of ref key without .pdf */
objectStoreRef.storeFileName(this.filename);
currentPdfFile = new PdfObjectReader(password);
/* get reader object to open the file */
currentPdfFile.openPdfFile(iis);
openPdfFile();
}else{
throw new RuntimeException(filename+" not currently an option");
}
}
void openPdfFile(final String filename, final String password) throws PdfException {
if(isOpen) {
//throw new RuntimeException("Previous file not closed");
closePdfFile(); //also checks decoding done
}
isOpen = false;
org.jpedal.DevFlags.currentFile = filename;
this.filename = filename;
res.flush();
res.flushObjects();
/* store file name for use elsewhere as part of ref key without .pdf */
objectStoreRef.storeFileName(filename);
currentPdfFile = new PdfObjectReader(password);
/* get reader object to open the file */
currentPdfFile.openPdfFile(filename);
openPdfFile();
}
int getPageNumber() {
return pageNumber;
}
public void setPageNumber(final int newPage) {
this.pageNumber=newPage;
}
/**
* get page count of current PDF file
*/
public int getPageCount() {
return pageCount;
}
void setPageCount(final int newPageCount) {
pageCount=newPageCount;
}
boolean isDecoding() {
return isDecoding;
}
void setDecoding(final boolean b) {
isDecoding=b;
}
public boolean isPasswordSupplied(final PdfObjectReader currentPdfFile) {
//allow through if user has verified password or set certificate
if (currentPdfFile != null){
final PdfFileReader objectReader=currentPdfFile.getObjectReader();
final DecryptionFactory decryption=objectReader.getDecryptionObject();
if(decryption != null && decryption.getEncHash()!= null){
objectStoreRef.setEncHash(decryption.getEncHash().clone());
}
return decryption!=null && (decryption.getBooleanValue(PDFflags.IS_PASSWORD_SUPPLIED) || certificate!=null);
}else {
return false;
}
}
/**
* return object which provides access to file images and name
*/
public ObjectStore getObjectStore() {
return objectStoreRef;
}
/**
* return object which provides access to file images and name (use not
* recommended)
*/
void setObjectStore(final ObjectStore newStore) {
objectStoreRef = newStore;
}
public void setUserEncryption(final Certificate certificate, final PrivateKey key) {
this.certificate=certificate;
this.key=key;
}
public PdfObjectReader getNewReader() {
final PdfObjectReader currentPdfFile;
if(certificate!=null){
currentPdfFile = new PdfObjectReader(certificate, key);
}else {
currentPdfFile = new PdfObjectReader();
}
return currentPdfFile;
}
public boolean isOpen() {
return isOpen;
}
public PdfObjectReader getIO() {
return currentPdfFile;
}
public void setIO(final PdfObjectReader pdfObjectReader) {
currentPdfFile =pdfObjectReader;
}
/**
* return true if the current pdf file is encrypted
* check isFileViewable(),
*
* if file is encrypted and not viewable - a user specified password is
* needed.
*/
public final boolean isEncrypted() {
if (currentPdfFile != null) {
final PdfFileReader objectReader= currentPdfFile.getObjectReader();
final DecryptionFactory decryption=objectReader.getDecryptionObject();
return decryption!=null && decryption.getBooleanValue(PDFflags.IS_FILE_ENCRYPTED);
}else {
return false;
}
}
public void dispose() {
//current=null;
if(currentPdfFile!=null) {
currentPdfFile.dispose();
}
currentPdfFile=null;
currentDisplay=null;
bb += 1;
}
public String getFilename() {
return filename;
}
float[] defaultMediaSize;
float[] defaultCropSize;
/**
* read the data from pages lists and pages so we can open each page.
*
* object reference to first trailer
*/
public int readAllPageReferences(final boolean ignoreRecursion, final PdfObject pdfObject , final Map rotations, final Map parents, int tempPageCount, final AcroRenderer formRenderer, final PdfResources res, final int insetW, final int insetH) {
final String currentPageOffset=pdfObject.getObjectRefAsString();
final boolean debug=false;
int rotation=0;
int type=pdfObject.getParameterConstant(PdfDictionary.Type);
if(debug) {
System.out.println("currentPageOffset="+currentPageOffset+" type="+type+ ' '+PdfDictionary.showAsConstant(type));
}
if(type== PdfDictionary.Unknown) {
if(pdfObject.getKeyArray(PdfDictionary.Kids)!=null){
type= PdfDictionary.Pages;
}else{
type= PdfDictionary.Page;
}
}
/*
* handle common values which can occur at page level or higher
*/
/* page rotation for this or up tree*/
int rawRotation=pdfObject.getInt(PdfDictionary.Rotate);
String parent=pdfObject.getStringKey(PdfDictionary.Parent);
if(rawRotation==-1 ){
while(parent!=null && rawRotation==-1){
if(parent!=null){
final Integer savedRotation=rotations.get(parent);
if(savedRotation!=null) {
rawRotation= savedRotation;
}
}
if(rawRotation==-1) {
parent=parents.get(parent);
}
}
//save
if(rawRotation!=-1){
rotations.put(currentPageOffset, rawRotation);
parents.put(currentPageOffset,parent);
}
}else{ //save so we can lookup
rotations.put(currentPageOffset, rawRotation);
parents.put(currentPageOffset,parent);
}
if(rawRotation!=-1) {
rotation=rawRotation;
}
final PdfPageData pageData= this.pageData;
pageData.setPageRotation(rotation, tempPageCount);
/*
* handle media and crop box, defaulting to higher value if needed (ie
* Page uses Pages and setting crop box
*/
float[] mediaBox=pdfObject.getFloatArray(PdfDictionary.MediaBox);
float[] cropBox=pdfObject.getFloatArray(PdfDictionary.CropBox);
if(mediaBox==null) {
mediaBox = defaultMediaSize;
}
if(cropBox==null) {
cropBox = defaultCropSize;
}
if (mediaBox != null) {
pageData.setMediaBox(mediaBox);
}
if (cropBox != null) {
pageData.setCropBox(cropBox);
}
/* process page to read next level down */
if (type==PdfDictionary.Pages) {
if(pdfObject.getDictionary(PdfDictionary.Resources)!=null) {
res.setPdfObject(PdfResources.GlobalResources, pdfObject.getDictionary(PdfDictionary.Resources));
}
final byte[][] kidList = pdfObject.getKeyArray(PdfDictionary.Kids);
int kidCount=0;
if(kidList!=null) {
kidCount=kidList.length;
}
if(debug) {
System.out.println("PAGES---------------------currentPageOffset="+currentPageOffset+" kidCount="+kidCount);
}
/* allow for empty value and put next pages in the queue */
if (kidCount> 0) {
if(debug) {
System.out.println("KIDS---------------------currentPageOffset="+currentPageOffset);
}
PdfObject nextObject;
for(int ii=0;ii
* Please note: Structure of PdfPageData is not guaranteed to remain
* constant. Please contact IDRsolutions for advice.
*
* @return PdfPageData object
*/
PdfPageData getPdfPageData() {
return pageData;
}
/**
* used by remote printing to pass in page metrics
*
* @param pageData
*/
void setPageData(final PdfPageData pageData) {
this.pageData = pageData;
}
/**
* wait for decoding to finish
*/
public void waitForDecodingToFinish() {
//wait to die
while (isDecoding) {
// System.out.println("Waiting to die");
try {
Thread.sleep(100);
} catch (final InterruptedException e) {
LogWriter.writeLog("Exception: "+e.getMessage());
//ensure will exit loop
isDecoding = false;
}
}
}
/**
* common code for reading URL and InputStream
* @param supportLinearized
* @param is
* @param rawFileName
* @return
* @throws PdfException
*/
boolean readFile(final boolean supportLinearized, final InputStream is, final String rawFileName, final String password) throws PdfException {
//make sure it will be deleted
objectStoreRef.setFileToDeleteOnFlush(ObjectStore.temp_dir + rawFileName);
objectStoreRef.setFileToDeleteOnFlush(rawFileName);
res.flush();
res.flushObjects();
if(password==null){
currentPdfFile = new PdfObjectReader();
}else{
currentPdfFile = new PdfObjectReader(password);
}
if(is!=null){
try {
final File tempURLFile;
if(rawFileName.startsWith("inputstream")){
tempURLFile = new File(ObjectStore.temp_dir+rawFileName);
filename = tempURLFile.getAbsolutePath();
}else {
tempURLFile = ObjectStore.createTempFile(rawFileName);
}
/* store file name for use elsewhere as part of ref key without .pdf */
objectStoreRef.storeFileName(tempURLFile.getName().substring(0, tempURLFile.getName().lastIndexOf('.')));
if(supportLinearized){
final byte[] linearBytes=linearParser.readLinearData(currentPdfFile,tempURLFile,is, this);
if(linearBytes!=null){
currentPdfFile.openPdfFile(linearBytes);
openPdfFile();
//force back if only 1 page
if (pageCount < 2) {
options.setDisplayView(Display.SINGLE_PAGE);
} else {
options.setDisplayView(options.getPageMode());
}
//read rest of file and reset
linearParser.linearizedBackgroundReaderer.start();
return true;
}
}else{
currentPdfFile.openPdfFile(is);
openPdfFile();
//force back if only 1 page
if (pageCount < 2) {
options.setDisplayView(Display.SINGLE_PAGE);
} else {
options.setDisplayView(options.getPageMode());
}
}
if(supportLinearized){
/* get reader object to open the file */
openPdfFile(tempURLFile.getAbsolutePath());
/* store file name for use elsewhere as part of ref key without .pdf */
objectStoreRef.storeFileName(tempURLFile.getName().substring(0, tempURLFile.getName().lastIndexOf('.')));
}
} catch (final IOException e) {
LogWriter.writeLog("[PDF] Exception " + e + " opening URL ");
}
}
return false;
}
public void closePdfFile() {
waitForDecodingToFinish();
if (!isOpen) {
return;
}
isOpen = false;
//flush linearization objects and
//make sure we have stopped thread doing background linear reading
linearParser.closePdfFile();
final Javascript javascript=externalHandlers.getJavaScript();
if (javascript != null){
javascript.closeFile();
}
// pass handle into renderer
final AcroRenderer formRenderer=externalHandlers.getFormRenderer();
if (formRenderer != null) {
formRenderer.openFile(pageCount,0,0, pageData, currentPdfFile, null);
formRenderer.removeDisplayComponentsFromScreen();
}
objectStoreRef.flush();
ObjectStore.flushPages();
pageCount = 0;
if (currentPdfFile != null && closeOnExit){
currentPdfFile.closePdfFile();
currentPdfFile = null;
}
lastPageDecoded = -1;
currentDisplay.writeCustom(DynamicVectorRenderer.FLUSH, null);
}
/**
* allows user to cache large objects to disk to avoid memory issues,
* setting minimum size in bytes (of uncompressed stream) above which object
* will be stored on disk if possible (default is -1 bytes which is all
* objects stored in memory) - Must be set before file opened.
*
*/
public void setStreamCacheSize(final int size) {
this.minimumCacheSize = size;
}
int getLastPageDecoded() {
return lastPageDecoded;
}
public void setLastPageDecoded(final int page) {
lastPageDecoded = page;
}
public void setDVR(final DynamicVectorRenderer swingDisplay) {
currentDisplay=swingDisplay;
}
public PageOffsets getOffset() {
return currentOffset;
}
public void setOffset(final PageOffsets newOffset) {
currentOffset=newOffset;
}
public void openPdfFile() throws PdfException {
currentPdfFile.setJavaScriptObject(externalHandlers.getJavaScript());
pageNumber = 1; // reset page number for metadata
isDecoding = true;
final AcroRenderer formRenderer=externalHandlers.getFormRenderer();
try {
// set cache size to use
currentPdfFile.getObjectReader().setCacheSize(minimumCacheSize);
// reset page data - needed to flush crop settings
pageData = new PdfPageData();
final PdfPageData pageData= this.pageData;
// read and log the version number of pdf used
final String pdfVersion = currentPdfFile.getObjectReader().getType();
LogWriter.writeLog("Pdf version : " + pdfVersion);
if (pdfVersion == null) {
currentPdfFile = null;
isDecoding = false;
throw new PdfException( "No version on first line ");
}
// read reference table so we can find all objects and say if
// encrypted
PdfObject pdfObject;
int linearPageCount=-1;
//linear page object set differently
if(linearParser.hasLinearData()){
/*
* read and decode the hints table and the ref table
*/
pdfObject=linearParser.readHintTable(currentPdfFile);
linearPageCount=linearParser.getPageCount();
}else {
pdfObject= currentPdfFile.getObjectReader().readReferenceTable(null, currentPdfFile.getObjectReader());
}
//new load code - be more judicious in how far down tree we scan
final boolean ignoreRecursion=true;
// open if not encrypted or has password
if (!isEncrypted() || isPasswordSupplied(currentPdfFile)) {
if (pdfObject != null){
pdfObject.ignoreRecursion(ignoreRecursion);
res.setValues(pdfObject, currentPdfFile);
//read Names, Dest, PageLabels (we process last once we have page count)
currentPdfFile.readDocumentMetaData(pdfObject,externalHandlers.getJavaScript());
}
final int type=pdfObject.getParameterConstant(PdfDictionary.Type);
if(type!=PdfDictionary.Page){
final PdfObject pageObj= pdfObject.getDictionary(PdfDictionary.Pages);
if(pageObj!=null){ //do this way incase in separate compressed stream
pdfObject=new PageObject(pageObj.getObjectRefAsString());
currentPdfFile.readObject(pdfObject);
// System.out.println("page="+pageObj+" "+pageObj.getObjectRefAsString());
//catch for odd files
if(pdfObject.getParameterConstant(PdfDictionary.Type)==-1) {
pdfObject=pageObj;
}
}
}
if (pdfObject != null) {
LogWriter.writeLog("Pages being read from "+pdfObject+ ' '+pdfObject.getObjectRefAsString());
pageNumber = 1; // reset page number for metadata
//flush annots before we reread
if(formRenderer!=null) {
formRenderer.resetAnnotData(options.getInsetW(), options.getInsetW(), pageData, 1, currentPdfFile,null);
}
//recursively read all pages
final int tempPageCount=readAllPageReferences(ignoreRecursion, pdfObject, new HashMap(1000), new HashMap(1000),1,formRenderer,res, options.getInsetW(), options.getInsetH());
//set PageCount if in Linearized data
if(linearPageCount>0){
pageCount = linearPageCount;
}else{
pageCount = tempPageCount - 1; // save page count
}
//now read PageLabels
currentPdfFile.readPageLabels(pageCount);
//pageNumber = 0; // reset page number for metadata;
if (pageCount == 0) {
LogWriter.writeLog("No pages found");
}
}
// pass handle into renderer
if (formRenderer != null) {
pageCount=formRenderer.openFile(pageCount,options.getInsetW(), options.getInsetH(), pageData, currentPdfFile, res.getPdfObject(PdfResources.AcroFormObj));
DevFlags.formsLoaded=false;
}
pageData.setPageCount(pageCount);
}
ActionHandler handler = externalHandlers.getFormActionHandler();
if(handler!=null){
handler.init(null, externalHandlers.getJavaScript(), formRenderer);
}
currentOffset = null;
isOpen = true;
} catch (final PdfException e) {
//ensure all data structures/handles flushed
isDecoding = false;
isOpen=true; //temporarily set to true as otherwise will not work
closePdfFile();
isOpen=false;
throw new PdfException(e.getMessage() + " opening file");
}finally{
isDecoding = false;
}
}
public void openPdfFile(final String filename) throws PdfException {
isOpen = false;
org.jpedal.DevFlags.currentFile = filename;
//System.out.println(filename);
this.filename = filename;
res.flush();
res.flushObjects();
//pagesReferences.clear();
/* store file name for use elsewhere as part of ref key without .pdf */
objectStoreRef.storeFileName(filename);
/*
* create Reader, passing in certificate if set
*/
currentPdfFile = getNewReader();
/* get reader object to open the file */
currentPdfFile.openPdfFile(filename);
/*test code in case we need to test byte[] version
//get size
try{
File file=new File(filename);
int length= (int) file.length();
byte[] fileData=new byte[length];
FileInputStream fis=new FileInputStream(filename);
fis.read(fileData);
fis.close();
currentPdfFile.openPdfFile(fileData);
}catch(Exception e){
}/**/
openPdfFile();
//force back if only 1 page
if (pageCount < 2) {
options.setDisplayView(Display.SINGLE_PAGE);
} else {
options.setDisplayView(options.getPageMode());
}
}
}