/**
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.jpedal.org
* (C) Copyright 1997-2011, 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
*
* ---------------
* PdfDecoder.java
* ---------------
*/
package org.jpedal;
import org.jpedal.constants.*;
//<start-adobe><start-ulc><start-thin>
import org.jpedal.examples.simpleviewer.gui.SwingGUI;
//<end-thin><end-ulc><end-adobe>
import org.jpedal.fonts.tt.TTGlyph;
import org.jpedal.io.ColorSpaceConvertor;
import org.jpedal.io.ObjectStore;
import org.jpedal.linear.LinearParser;
import org.jpedal.objects.raw.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Pageable;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import javax.print.attribute.SetOfIntegerSyntax;
import org.jpedal.text.TextLines;
import org.jpedal.utils.repositories.Vector_Int;
import org.jpedal.utils.repositories.Vector_Rectangle;
import org.w3c.dom.Document;
import java.io.*;
import java.net.URL;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
import javax.swing.border.Border;
import org.jpedal.objects.acroforms.creation.FormFactory;
import org.jpedal.objects.acroforms.rendering.AcroRenderer;
import org.jpedal.objects.acroforms.rendering.DefaultAcroRenderer;
//<start-adobe>
import org.jpedal.grouping.PdfGroupingAlgorithms;
//<start-thin>
import org.jpedal.examples.simpleviewer.gui.swing.SwingMouseListener;
//<end-thin><end-adobe>
import org.jpedal.color.ColorSpaces;
import org.jpedal.exception.PdfException;
import org.jpedal.io.*;
import org.jpedal.objects.*;
import org.jpedal.objects.layers.PdfLayerList;
//<start-adobe>
import org.jpedal.objects.outlines.OutlineData;
import org.jpedal.objects.structuredtext.MarkedContentGenerator;
//<end-adobe>
import org.jpedal.parser.*;
import org.jpedal.utils.*;
import org.jpedal.external.*;
import org.jpedal.render.*;
import org.jpedal.fonts.FontMappings;
import org.jpedal.fonts.StandardFonts;
import org.jpedal.fonts.objects.FontData;
/**
* Provides an object to decode pdf files and provide a rasterizer if required -
* Normal usage is to create instance of PdfDecoder and access via public
* methods. Examples showing usage in org.jpedal.examples
* <p/>
* We recommend you access JPedal using only public methods listed in API
*/
public class PdfDecoder extends JPanel
{
/**
****************************** static constants ******************************
*/
public static final String version = "4.75b25-OS";
/**/
/**
* flag to show extraction mode includes any text
*/
public static final int TEXT = 1;
/**
* flag to show extraction mode includes original images
*/
public static final int RAWIMAGES = 2;
/**
* flag to show extraction mode includes final scaled/clipped
*/
public static final int FINALIMAGES = 4;
/**
* undocumented flag to allow shape extraction
*/
protected static final int PAGEDATA = 8;
/**
* flag to show extraction mode includes final scaled/clipped
*/
public static final int RAWCOMMANDS = 16;
/**
* flag to show extraction of clipped images at highest res
*/
public static final int CLIPPEDIMAGES = 32;
/**
* flag to show extraction of clipped images at highest res
*/
public static final int TEXTCOLOR = 64;
/**
* flag to show extraction of raw cmyk images
*/
public static final int CMYKIMAGES = 128;
/**
* flag to show extraction of xforms metadata
*/
public static final int XFORMMETADATA = 256;
/**
* flag to show extraction of color required (used in Storypad grouping)
*/
public static final int COLOR = 512;
/**
* flag to show render mode includes any text
*/
public static final int RENDERTEXT = 1;
/**
* flag to show render mode includes any images
*/
public static final int RENDERIMAGES = 2;
/**
* flag to show render mode includes any images
*/
public static final int REMOVE_RENDERSHAPES = 16;
/**
* flag to show text highlights need to be done last
*/
public static final int OCR_PDF = 32;
/**
* printing mode using inbuilt java fonts and getting java to rasterize
* fonts using Java font if match found (added to get around limitations in
* PCL printing via JPS) - this is the default off setting
*/
public static final int NOTEXTPRINT = 0;
/**
* printing mode using inbuilt java fonts and getting java to rasterize
* fonts using Java font if match found (added to get around limitations in
* PCL printing via JPS)
*/
public static final int TEXTGLYPHPRINT = 1;
/**
* printing mode using inbuilt java fonts and getting java to rasterize
* fonts using Java font if match found (added to get around limitations in
* PCL printing via JPS)
*/
public static final int TEXTSTRINGPRINT = 2;
/**
* printing mode using inbuilt java fonts and getting java to rasterize
* fonts using Java font if match found (added to get around limitations in
* PCL printing via JPS) - overrides embedded fonts for standard fonts (ie Arial)
*/
public static final int STANDARDTEXTSTRINGPRINT = 3;
public static final int SUBSTITUTE_FONT_USING_FILE_NAME = 1;
public static final int SUBSTITUTE_FONT_USING_POSTSCRIPT_NAME = 2;
public static final int SUBSTITUTE_FONT_USING_FAMILY_NAME = 3;
public static final int SUBSTITUTE_FONT_USING_FULL_FONT_NAME = 4;
public static final int SUBSTITUTE_FONT_USING_POSTSCRIPT_NAME_USE_FAMILY_NAME_IF_DUPLICATES= 5;
/**
****************************** static variables ******************************
*/
/**
* flag to show if on mac so we can code around certain bugs
*/
public static boolean isRunningOnMac = false;
public static boolean isRunningOnWindows = false;
public static boolean isRunningOnAIX = false;
public static boolean isRunningOnLinux = false;
/**
* version number
*/
public static float javaVersion=0f;
/**
* The transparency of the highlighting box around the text stored as a float
*/
public static float highlightComposite = 0.35f;
//Show onscreen mouse dragged box
public static boolean showMouseBox = false;
/**
* dpi for final images
*/
public static int dpi = 72;
/**
* flag to tell software to embed x point after each character so we can
* merge any overlapping text together
*/
public static boolean embedWidthData = false;
/**
****************************** static objects ******************************
*/
//allow user to override code
public static JPedalHelper Helper=null;//new org.jpedal.examples.ExampleHelper();
DecoderOptions options=new DecoderOptions();
/**
****************************** swing objects ******************************
*/
SwingPainter swingPainter= new SwingPainter(this,options);
public SwingPrinter swingPrinter = new SwingPrinter();
/**
****************************** general ******************************
*/
FileAccess fileAcces=new FileAccess();
/**handlers file reading/decoding of linear PDF data*/
LinearParser linearParser=new LinearParser();
private DPIFactory scalingdpi=new DPIFactory();
ExternalHandlers externalHandlers=new ExternalHandlers();
//Darker background, glowing pages
public boolean useNewGraphicsMode = true;
protected Display pages=null;
/**default renderer for acroforms*/
protected AcroRenderer formRenderer;
private PageOffsets currentOffset;
/**copy of flag to tell program whether to create
* (and possibly update) screen display
*/
protected boolean renderPage = false;
/**display mode (continuous, facing, single)*/
protected int displayView=Display.SINGLE_PAGE;
/**amount we scroll screen to make visible*/
private int scrollInterval=10;
/** count of how many pages loaded */
protected int pageCount = 0;
/** when true setPageParameters draws the page rotated for use with scale to window */
boolean isNewRotationSet=false;
/**flag to stop multiple attempts to decode*/
protected boolean isDecoding=false;
/**current page*/
public int pageNumber=1;
protected int alignment=Display.DISPLAY_LEFT_ALIGNED;
/** used by setPageParameters to draw rotated pages */
public int displayRotation=0;
/**used to reduce or increase display size*/
protected AffineTransform displayScaling;
/** holds page information used in grouping*/
private PdfPageData pageData = new PdfPageData();
/**allow for inset of display*/
public int insetW=0;
public int insetH=0;
/**width of the BufferedImage in pixels*/
int x_size = 100;
/**height of the BufferedImage in pixels*/
int y_size = 100;
/**unscaled page height*/
int max_y;
/**unscaled page Width*/
int max_x;
/**any scaling factor being used to convert co-ords into correct values and to alter image size
*/
public float scaling=1;
/**border for component*/
protected Border myBorder=null;
/** the ObjectStore for this file */
public ObjectStore objectStoreRef = new ObjectStore();
/**the actual display object*/
protected DynamicVectorRenderer currentDisplay=new SwingDisplay(1,objectStoreRef,false); //
protected boolean useAcceleration=true;
/**
* provide access to pdf file objects
*/
public PdfObjectReader currentPdfFile;
/**
* flag to stop multiple access to background decoding
*/
private boolean isBackgroundDecoding = false;
/**
* store image data extracted from pdf
*/
private PdfImageData pdfImages = new PdfImageData();
/**
* store image data extracted from pdf
*/
private PdfImageData pdfBackgroundImages = new PdfImageData();
/**
* store text data and can be passed out to other classes
*/
private PdfData pdfData;
/**
* store text data and can be passed out to other classes
*/
private PdfData pdfBackgroundData;
//<start-adobe>
private RefreshLayout viewListener = null;
//<end-adobe>
/**
* direct graphics 2d to render onto
*/
private Graphics2D g2 = null;
/**
* list of fonts for decoded page
*/
private String fontsInFile = "";
/**
* list of images for decoded page
*/
private String imagesInFile= "";
/**custom upscale val for JPedal settings*/
private float multiplyer = 1;
/**
* current extraction mode
*/
private int extractionMode = 7;
/**
* current render mode
*/
protected int renderMode = 7;
/**
* decodes page or image
*/
//private PdfStreamDecoder current;
PdfResources res=new PdfResources();
/**
* interactive status Bar
*/
private StatusBar statusBar = null;
//<start-wrap>
/**
* tells JPedal to display screen using hires images
*/
boolean useHiResImageForDisplay = false;
/**
//<end-wrap>
boolean useHiResImageForDisplay = true;
/**/
String filename;
/**/
//flag to track if page decoded twice
private int lastPageDecoded = -1;
/**
* see if file open - may not be open if user interrupted open or problem
* encountered
*/
public boolean isOpen() {
return isOpen;
}
//<start-adobe>
/**
* return markedContent object as XML Document
* @return Document containing XML structure with data
*/
public Document getMarkedContent() {
/**
* objects for structured content
*/
return new MarkedContentGenerator().getMarkedContentTree(res, this.pageData,currentPdfFile);
}
/**
* used by remote printing to pass in page metrics
*
* @param pageData
*/
public void setPageData(PdfPageData pageData) {
this.pageData = pageData;
}
//<end-adobe>
/**
* return page number for last page decoded (only use in SingleDisplay mode)
*/
public int getlastPageDecoded() {
return lastPageDecoded;
}
/**
* return details on page for type (defined in org.jpedal.constants.PageInfo) or null if no values
* Unrecognised key will throw a RunTime exception
*
* null returned if JPedal not clear on result
*/
public Iterator getPageInfo(int type) {
return resultsFromDecode.getPageInfo(type);
}
//<start-adobe>
/**
* provide direct access to outlineData object
* @return OutlineData
*/
public OutlineData getOutlineData() {
return res.getOutlineData();
}
/**
* track if file still loaded in background
* @return
*/
public boolean isLoadingLinearizedPDF() {
return false;
/**/
}
protected void resetMultiPageForms(int page) {
swingPainter.resetMultiPageForms(page, this, formRenderer, pages);
}
/**
* class to repaint multiple views
*/
private class RefreshLayout extends ComponentAdapter {
java.util.Timer t2 = null;
/*
* (non-Javadoc)
*
* @see java.awt.event.ComponentListener#componentMoved(java.awt.event.ComponentEvent)
*/
public void componentMoved(ComponentEvent e) {
startTimer();
// screenNeedsRedrawing=true;
}
/*
*/
public void componentResized(ComponentEvent e) {
startTimer();
// screenNeedsRedrawing=true;
}
private void startTimer() {
//whatever else, stop current decode
//pages.stopGeneratingPage();
//turn if off if running
if (t2 != null)
t2.cancel();
//restart - if its not stopped it will trigger page update
TimerTask listener = new PageListener();
t2 = new java.util.Timer();
t2.schedule(listener, 500);
}
/**
* fix submitted by Niklas Matthies
*/
public void dispose() {
if(t2!=null)
t2.cancel();
}
/**
* used to update statusBar object if exists
*/
class PageListener extends TimerTask {
public void run() {
if (Display.debugLayout)
System.out.println("ActionPerformed " + pageCount);
try{
if(pages!=null){
pages.stopGeneratingPage();
//Ensure page range does not drop below one
if(pageNumber<1)
pageNumber = 1;
if(pages!=null)
pages.decodeOtherPages(pageNumber, pageCount);
}
}catch(Exception ee){
LogWriter.writeLog("Exception in run() "+ee.getMessage());
}
}
}
}
//<end-adobe>
/**
* work out machine type so we can call OS X code to get around Java bugs.
*/
static {
/**
* see if mac
*/
try {
String name = System.getProperty("os.name");
if (name.equals("Mac OS X"))
PdfDecoder.isRunningOnMac = true;
else if (name.startsWith("Windows")) {
PdfDecoder.isRunningOnWindows = true;
}else if (name.startsWith("AIX")) {
PdfDecoder.isRunningOnAIX = true;
} else {
if (name.equals("Linux")) {
PdfDecoder.isRunningOnLinux = true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
/**
* get version number so we can avoid bugs in various versions
*/
try{
PdfDecoder.javaVersion=Float.parseFloat(System.getProperty("java.specification.version"));
}catch(Exception e){
e.printStackTrace();
}
}
//<start-demo>
/**
//<end-demo>
public void showExpiry(){
}
/**/
//<start-adobe>
/**
* turns off the viewable area, scaling the page back to original scaling
* <br>
* <br>
* NOT RECOMMENDED FOR GENERAL USE (this has been added for a specific
* client and we have found it can be unpredictable on some PDF files).
*/
public void resetViewableArea() {
swingPainter.resetViewableArea(this,pageData);
}
/**
* allows the user to create a viewport within the displayed page, the
* aspect ratio is keep for the PDF page <br>
* <br>
* Passing in a null value is the same as calling resetViewableArea()
* <p/>
* <br>
* <br>
* The viewport works from the bottom left of the PDF page <br>
* The general formula is <br>
* (leftMargin, <br>
* bottomMargin, <br>
* pdfWidth-leftMargin-rightMargin, <br>
* pdfHeight-bottomMargin-topMargin)
* <p/>
* <br>
* <br>
* NOT RECOMMENDED FOR GENERAL USE (this has been added for a specific
* client and we have found it can be unpredictable on some PDF files).
* <p/>
* <br>
* <br>
* The viewport will not be incorporated in printing <br>
* <br>
* Throws PdfException if the viewport is not totally enclosed within the
* 100% cropped pdf
*/
public AffineTransform setViewableArea(Rectangle viewport) throws PdfException {
return swingPainter.setViewableArea(viewport, this, pageData);
}
//<end-adobe>
//<start-adobe>
/**
* return type of alignment for pages if smaller than panel
* - see options in Display class.
*/
public int getPageAlignment() {
return alignment;
}
/**
* This will be needed for text extraction as it paramter makes sure widths
* included in text stream
*
* @param newEmbedWidthData -
* flag to embed width data in text fragments for use by grouping
* algorithms
*/
public static void init(boolean newEmbedWidthData) {
/** get local handles onto objects/data passed in */
embedWidthData = newEmbedWidthData;
}
//<end-adobe>
/**
* Recommend way to create a PdfDecoder if no rendering of page may be
* required<br>
* Otherwise use PdfDecoder()
*
* @param newRender flag to show if pages being rendered for JPanel or extraction
*/
public PdfDecoder(boolean newRender) {
pages = new SingleDisplay(this);
/** get local handles onto flag passed in */
this.renderPage = newRender;
startup();
// needs to be set so we can over-ride
if (renderPage) {
setLayout(null);
setPreferredSize(new Dimension(100, 100));
}
}
/**
*
*/
private void startup() {
formRenderer = new DefaultAcroRenderer();
//pass in user handler if set
formRenderer.resetHandler(null, this,Options.FormsActionHandler);
//once only setup for fonts (dispose sets flag to false just incase)
if(!FontMappings.fontsInitialised){
FontMappings.initFonts();
FontMappings.fontsInitialised=true;
}
// set global flags
String debugFlag = System.getProperty("debug");
if (debugFlag != null)
LogWriter.setupLogFile(true, 1, "", "v", false);
}
/**
* flag to enable popup of error messages in JPedal
*/
public static boolean showErrorMessages = false;
protected int specialMode= SpecialOptions.NONE;
/**
* Recommend way to create a PdfDecoder for renderer only viewer (not
* recommended for server extraction only processes)
*/
public PdfDecoder() {
pages = new SingleDisplay(this);
this.renderPage = true;
setLayout(null);
startup();
setPreferredSize(new Dimension(100, 100));
}
private boolean isOpen = false;
//<start-adobe>
//<end-adobe>
/**
* remove all static elements - only do once completely finished with JPedal
* as will not be reinitialised
*/
static public void disposeAllStatic() {
StandardFonts.dispose();
FontMappings.dispose();
}
/**
* convenience method to remove all items from memory
* If you wish to clear all static objects as well, you will also need to call
* disposeAllStatic()
*/
final public void dispose(){
if (SwingUtilities.isEventDispatchThread()){
disposeObjects();
}else {
final Runnable doPaintComponent = new Runnable() {
public void run() {
disposeObjects();
}
};
SwingUtilities.invokeLater(doPaintComponent);
}
}
final private void disposeObjects() {
FontMappings.fontsInitialised=false;
options.disposeObjects();
//<start-adobe>
//code fix from Niklas Matthies
if(viewListener!=null)
viewListener.dispose();
//<end-adobe>
if(pdfData!=null)
pdfData.dispose();
pdfData=null;
if(pages!=null)
pages.dispose();
pages=null;
FontMappings.defaultFont=null;
if(currentDisplay!=null)
currentDisplay.dispose();
currentDisplay=null;
// if(current!=null)
// current.dispose();
//current=null;
if(currentPdfFile!=null)
currentPdfFile.dispose();
currentPdfFile=null;
//dispose the javascript object before the formRenderer object as JS accesses the renderer object
if(formRenderer!=null)
formRenderer.dispose();
formRenderer=null;
}
/**
* convenience method to close the current PDF file
*/
final public void closePdfFile() {
//<start-demo>
/**
//<end-demo>
/**/
if (!isOpen)
return;
isOpen = false;
// ensure no previous file still being decoded
waitForDecodingToFinish();
//flush linearization objects and
//make sure we have stopped thread doing background linear reading
linearParser.closePdfFile();
displayScaling = null;
lastPageDecoded = -1;
if(pages!=null)
pages.stopGeneratingPage();
//we are closing the page so call the closeing page for forms actions.
//we need an example and then we can implement against
// we just have to find it hopefully attached to the page object
//if (formRenderer != null)
// formRenderer.getActionHandler().PO(pageNumber);
pages.disableScreen();
swingPrinter.clear();
// pass handle into renderer
if (formRenderer != null) {
formRenderer.openFile(pageCount);
formRenderer.resetFormData(insetW, insetH, pageData, currentPdfFile, res.getPdfObject(PdfResources.AcroFormObj));
formRenderer.removeDisplayComponentsFromScreen();
}
//<start-adobe>
// remove listener if setup
if (viewListener!=null) {
//flush any cached pages
pages.flushPageCaches();
removeComponentListener(viewListener);
viewListener.dispose();
viewListener=null;
}
//<end-adobe>
if (currentPdfFile != null){
currentPdfFile.closePdfFile();
currentPdfFile = null;
}
pages.disableScreen();
currentDisplay.flush();
ObjectStore.flushPages();
objectStoreRef.flush();
pageCount=0;
res.flushObjects();
//<start-adobe>
this.setDisplayView(Display.SINGLE_PAGE, Display.DISPLAY_CENTERED);
//<end-adobe>
if (SwingUtilities.isEventDispatchThread()){
validate();
}else {
final Runnable doPaintComponent = new Runnable() {
public void run() {
validate();
}
};
SwingUtilities.invokeLater(doPaintComponent);
}
}
//<start-adobe>
/**
* Access should not generally be required to
* this class. Please look at getBackgroundGroupingObject() - provide method
* for outside class to get data object containing text and metrics of text. -
* Viewer can only access data for finding on page
*
* @return PdfData object containing text content from PDF
*/
final public PdfData getPdfBackgroundData() {
return pdfBackgroundData;
}
/**
* Access should not generally be required to
* this class. Please look at getGroupingObject() - provide method for
* outside class to get data object containing raw text and metrics of text<br> -
* Viewer can only access data for finding on page
*
* @return PdfData object containing text content from PDF
*/
final public PdfData getPdfData() throws PdfException {
if ((extractionMode & PdfDecoder.TEXT) == 0)
throw new PdfException(
"[PDF] Page data object requested will be empty as text extraction disabled. Enable with PdfDecoder method setExtractionMode(PdfDecoder.TEXT | other values");
else
return pdfData;
}
/**
* flag to show if PDF document contains an outline
*/
final public boolean hasOutline() {
return res.hasOutline();
}
/**
* return a DOM document containing the PDF Outline object as a DOM Document - may return null
*/
final public Document getOutlineAsXML() {
return res.getOutlineAsXML(currentPdfFile,pageCount);
}
//<end-adobe>
/**
* Provides method for outside class to get data
* object containing information on the page for calculating grouping <br>
* Please note: Structure of PdfPageData is not guaranteed to remain
* constant. Please contact IDRsolutions for advice.
*
* @return PdfPageData object
*/
final public PdfPageData getPdfPageData() {
return pageData;
}
/**
* set page range (inclusive) -
* If end is less than start it will print them
* backwards (invalid range will throw PdfException)
*
* @throws PdfException
*/
public void setPagePrintRange(int start, int end) throws PdfException {
swingPrinter.setPagePrintRange(start, end, pageCount);
}
/**
* tells program to try and use Java's font printing if possible as work
* around for issue with PCL printing - values are PdfDecoder.TEXTGLYPHPRINT
* (use Java to rasterize font if available) PdfDecoder.TEXTSTRINGPRINT(
* print as text not raster - fastest option) PdfDecoder.NOTEXTPRINT
* (default - highest quality)
*/
public void setTextPrint(int textPrint) {
this.textPrint = textPrint;
}
/**
* flag to use Java's inbuilt font renderer if possible
*/
public int textPrint = 0;
/**
* the size above which objects stored on disk (-1 is off)
*/
private int minimumCacheSize = -1;//20000;
/**
* return any messages on decoding
*/
String decodeStatus = "";
private DecoderResults resultsFromDecode=new DecoderResults();
private Object customSwingHandle;
private Object userExpressionEngine;
private boolean generateGlyphOnRender;
/** use this to turn javascript on and off, default is on. */
public void setJavaScriptUsed(boolean jsEnabled){
options.setJavaScriptUsed(jsEnabled);
}
/**
* If you are printing PDFs using JPedal in your custom
* code, you may find pages missing, because JPedal does
* not know about these additional pages. This method
* allows you to tell JPedal you have already printed pagesPrinted
*/
public void useLogicalPrintOffset(int pagesPrinted){
swingPrinter.useLogicalPrintOffset(pagesPrinted);
}
/**used to render to image and then print the image*/
BufferedImage printImage =null;
/**
* generate BufferedImage of a page in current file
*
* Page size is defined by CropBox
*/
public BufferedImage getPageAsImage(int pageIndex) throws PdfException {
return getPageAsImage(pageIndex, false);
}
/**
* generate BufferedImage of a page in current file
*/
private BufferedImage getPageAsImage(int pageIndex, boolean imageIsTransparent) throws PdfException {
BufferedImage image = null;
// make sure in range
if (pageIndex > pageCount || pageIndex < 1) {
if(LogWriter.isOutput())
LogWriter.writeLog("Page " + pageIndex + " not in range");
} else {
if (currentPdfFile == null)
throw new PdfException("File not open - did you call closePdfFile() inside a loop and not reopen");
/** get pdf object id for page to decode */
String currentPageOffset = currentPdfFile.getReferenceforPage(pageIndex);
if (currentPageOffset != null) {
PDFtoImageConvertor PDFtoImage=new PDFtoImageConvertor(multiplyer,options);
image = PDFtoImage.convert(resultsFromDecode, displayRotation, res,displayView, externalHandlers,renderMode,pageData,formRenderer,scaling,currentPdfFile,pageIndex, imageIsTransparent, currentPageOffset);
multiplyer=PDFtoImage.getMultiplyer();
//just incase also rendered
if(pages!=null)
formRenderer.getCompData().resetScaledLocation(pages.getOldScaling(), displayRotation, 0);
}
//workaround for bug in AIX
if (!isRunningOnAIX && !imageIsTransparent && image != null)
image = ColorSpaceConvertor.convertToRGB(image);
}
return image;
}
//<start-wrap>
/**
* return scaleup factor applied to last Hires image of page generated
*
* negative values mean no upscaling applied and should be ignored
*/
public float getHiResUpscaleFactor(){
return multiplyer;
}
//<end-wrap>
/**
* provide method for outside class to clear store of objects once written
* out to reclaim memory
*
* @param reinit lag to show if image data flushed as well
*/
final public void flushObjectValues(boolean reinit) {
if (pdfData != null)
pdfData.flushTextList(reinit);
if (pdfImages != null && reinit)
pdfImages.clearImageData();
}
//<start-adobe>
/**
* provide method for outside class to get data object
* containing images
*
* @return PdfImageData containing image metadata
*/
final public PdfImageData getPdfImageData() {
return pdfImages;
}
/**
* provide method for outside class to get data object
* containing images.
*
* @return PdfImageData containing image metadata
*/
final public PdfImageData getPdfBackgroundImageData() {
return pdfBackgroundImages;
}
//<end-adobe>
/**
* set render mode to state what is displayed onscreen (ie
* RENDERTEXT,RENDERIMAGES) - only generally required if you do not wish to
* show all objects on screen (default is all). Add values together to
* combine settings.
*/
final public void setRenderMode(int mode) {
renderMode = mode;
extractionMode = mode;
}
/**
* set extraction mode telling JPedal what to extract -
* (TEXT,RAWIMAGES,FINALIMAGES - add together to combine) - See
* org.jpedal.examples for specific extraction examples
*/
final public void setExtractionMode(int mode) {
extractionMode = mode;
}
/**
* method to return null or object giving access info fields and metadata.
*/
final public PdfFileInformation getFileInformationData() {
return res.getMetaData(currentPdfFile);
}
/**
*
* Please do not use for general usage. Use setPageParameters(scalingValue, pageNumber) instead;
*/
final public void setExtractionMode(int mode, int imageDpi, float scaling) {
if (dpi % 72 != 0 && LogWriter.isOutput())
LogWriter.writeLog("Dpi is not a factor of 72- this may cause problems");
dpi = imageDpi;
this.scaling = scaling;
pageData.setScalingValue(scaling); //ensure aligned
extractionMode = mode;
PdfLayerList layers=res.getPdfLayerList();
if(layers!=null){
boolean layersChanged=layers.setZoom(scaling);
if(layersChanged){
try {
decodePage(-1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* return handle on PDFFactory which adjusts display size so matches size in Acrobat
* @return
*/
public DPIFactory getDPIFactory(){
return scalingdpi;
}
/**
* initialise panel and set size to fit PDF page<br>
* intializes display with rotation set to the default, specified in the PDF document
* scaling value of -1 means keep existing setting
*/
final public void setPageParameters(float scaling, int pageNumber) {
this.pageNumber = pageNumber;
//pick up flag to prevent loop
if (displayView==Display.PAGEFLOW3D && scaling==-100f)
return;
//ignore negative value or set
if(scaling>0)
this.scaling=scaling;
else
scaling=this.scaling;
if(pages!=null)
pages.setScaling(scaling);
PdfLayerList layers=res.getPdfLayerList();
if(layers!=null){
boolean layersChanged=layers.setZoom(scalingdpi.removeScaling(scaling));
if(layersChanged){
try {
decodePage(-1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
pageData.setScalingValue(scaling); //ensure aligned
int mediaW = pageData.getMediaBoxWidth(pageNumber);
max_y = pageData.getMediaBoxHeight(pageNumber);
max_x = pageData.getMediaBoxWidth(pageNumber);
int cropW = pageData.getCropBoxWidth(pageNumber);
int cropH = pageData.getCropBoxHeight(pageNumber);
this.x_size =(int) ((cropW)*scaling);
this.y_size =(int) ((cropH)*scaling);
//rotation is broken in viewer without this - you can't alter it
//can anyone remember why we added this code???
//it breaks PDFs if the rotation changes between pages
if(!isNewRotationSet && displayView!=Display.PAGEFLOW3D){
displayRotation = pageData.getRotation(pageNumber);
}else{
isNewRotationSet=false;
}
currentDisplay.init(mediaW,max_y,displayRotation,options.getPageColor());
/**update the AffineTransform using the current rotation*/
swingPainter.setPageRotation(displayRotation, pageData);
//refresh forms in case any effected
formRenderer.getCompData().setForceRedraw(true);
}
/** calls setPageParameters(scaling,pageNumber) after setting rotation to draw page */
final public void setPageParameters(float scaling, int pageNumber,int newRotation) {
isNewRotationSet=true;
displayRotation=newRotation;
if (displayView == Display.PAGEFLOW3D)
pages.init(0,0,displayRotation,0,null,false,null,0,0);
else
setPageParameters(scaling, pageNumber);
}
//<start-adobe>
/**
* set status bar to use when decoding a page - StatusBar provides a GUI
* object to display progress and messages.
*/
public void setStatusBarObject(StatusBar statusBar) {
this.statusBar = statusBar;
}
//<end-adobe>
/**
* wait for decoding to finish
*/
public void waitForDecodingToFinish() {
//wait to die
while (isDecoding) {
// System.out.println("Waiting to die");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// should never be called
e.printStackTrace();
//ensure will exit loop
isDecoding=false;
}
}
}
//<start-adobe>
/**
* gets DynamicVector Object
*/
public DynamicVectorRenderer getDynamicRenderer() {
return currentDisplay;
}
/**
* gets DynamicVector Object - NOT PART OF API and subject to change (DO NOT USE)
*/
public DynamicVectorRenderer getDynamicRenderer(boolean reset) {
DynamicVectorRenderer latestVersion=currentDisplay;
if(reset)
currentDisplay=new SwingDisplay(0,objectStoreRef,false);
return latestVersion;
}
//<end-adobe>
/**
* Override setCursor so that we can turn on and off
*/
public void setPDFCursor(Cursor c){
if(SingleDisplay.allowChangeCursor){
this.setCursor(c);
}
}
/**
* decode a page, - <b>page</b> must be between 1 and
* <b>PdfDecoder.getPageCount()</b> - Will kill off if already running
*
* returns minus page if trying to open linearized page not yet available
*/
final public void decodePage(int rawPage) throws Exception {
//allow us to insert our own version (ie HTML)
Object customDVR=externalHandlers.getExternalHandler(Options.CustomOutput);
if(customDVR!=null)
currentDisplay= (DynamicVectorRenderer) customDVR;
boolean isForm=false;
//flag to allow us to not do some things when we re decode the page with layers on for example
boolean isDuplicate = false;
if(rawPage==-1){
rawPage=lastPageDecoded;
isDuplicate = true;
}
boolean isSamePage = false;
if(rawPage==lastPageDecoded)
isSamePage = true;
final int page=rawPage;
if (isDecoding) {
if(LogWriter.isOutput())
LogWriter.writeLog("[PDF]WARNING - this file is being decoded already - use waitForDecodingToFinish() to check");
} else {
boolean isPageAvailable=isPageAvailable(rawPage);
PdfObject pdfObject=linearParser.getLinearPageObject();
/**
* if linearized and PdfObject then setup
*/
if(!isPageAvailable){
isDecoding=false;
return;
}else if(isPageAvailable && pdfObject!=null){
isDecoding=true;
readAllPageReferences(true, pdfObject, new HashMap(1000), new HashMap(1000),rawPage);
}
//reset
resultsFromDecode.resetTimeout();
try{
isDecoding = true;
PdfLayerList layers=res.getPdfLayerList();
if(layers!=null && layers.getChangesMade()){
lastPageDecoded=-1;
layers.setChangesMade(false);//set flag to say we have decoded the changes
//refresh forms in case any effected by layer change
formRenderer.getCompData().setForceRedraw(true);
formRenderer.getCompData().setLayerData(layers);
formRenderer.getCompData().resetScaledLocation(scaling,displayRotation,(int)swingPainter.getIndent());//indent here does nothing.
}
if (this.displayView != Display.SINGLE_PAGE){
isDecoding=false;
return;
}
lastPageDecoded = page;
decodeStatus = "";
swingPainter.setCursorBoxOnScreen(null,isSamePage,formRenderer);
/** flush renderer */
currentDisplay.flush();
pages.refreshDisplay();
/** check in range */
if (page > pageCount || page < 1) {
if(LogWriter.isOutput())
LogWriter.writeLog("Page out of bounds");
isDecoding=false;
} else{
//<start-adobe>
/**
* title changes to give user something to see under timer
* control
*/
Timer t = null;
if (statusBar != null) {
ActionListener listener = new ProgressListener();
t = new Timer(150, listener);
t.start(); // start it
}
//<end-adobe>
this.pageNumber = page;
String currentPageOffset =currentPdfFile.getReferenceforPage(page);
/**
* decode the file if not already decoded, there is a valid
* object id and it is unencrypted
*/
if (currentPageOffset != null && currentPdfFile == null)
throw new PdfException("File not open - did you call closePdfFile() inside a loop and not reopen");
/** get pdf object id for page to decode */
if(pdfObject==null){
pdfObject=new PageObject(currentPageOffset);
currentPdfFile.readObject(pdfObject);
}
PdfStreamDecoder current=null;
/** read page or next pages */
if (pdfObject != null) {
byte[][] pageContents= pdfObject.getKeyArray(PdfDictionary.Contents);
//needs to be out of loop as we can get flattened forms on pages with no content
current = new PdfStreamDecoder(currentPdfFile, useHiResImageForDisplay,res.getPdfLayerList());
/** set hires mode or not for display */
current.setXMLExtraction(options.isXMLExtraction());
currentDisplay.setHiResImageForDisplayMode(useHiResImageForDisplay);
currentDisplay.setPrintPage(page);
currentDisplay.setCustomColorHandler((ColorHandler) externalHandlers.getExternalHandler(Options.ColorHandler));
current.setParameters(true, renderPage, renderMode, extractionMode);
externalHandlers.addHandlers(current);
current.setObjectValue(ValueTypes.Name, filename);
current.setIntValue(ValueTypes.PageNum, page);
current.setObjectValue(ValueTypes.ObjectStore,objectStoreRef);
current.setObjectValue(ValueTypes.StatusBar, statusBar);
current.setObjectValue(ValueTypes.PDFPageData,pageData);
current.setObjectValue(ValueTypes.DynamicVectorRenderer,currentDisplay);
//if it has no content but it still has a name obj then we need to still do all this.
if (pageContents != null ) {
res.setupResources(current, false, pdfObject.getDictionary(PdfDictionary.Resources),pageNumber,currentPdfFile);
if (g2 != null)
current.setObjectValue(ValueTypes.DirectRendering, g2);
currentDisplay.init(pageData.getMediaBoxWidth(pageNumber), pageData.getMediaBoxHeight(pageNumber), pageData.getRotation(pageNumber),options.getPageColor());
try {
/*
* If highlights are required for page, reset highlights
*/
if(textLines!=null && true)
textLines.setLineAreas(null);
current.decodePageContent(pdfObject);
//All data loaded so now get all line areas for page
if(textLines!=null){
Vector_Rectangle vr = (Vector_Rectangle) current.getObjectValue(ValueTypes.TextAreas);
vr.trim();
Rectangle[] pageTextAreas = vr.get();
Vector_Int vi = (Vector_Int) current.getObjectValue(ValueTypes.TextDirections);
vi.trim();
int[] pageTextDirections = vi.get();
for(int k=0; k!=pageTextAreas.length; k++){
textLines.addToLineAreas(pageTextAreas[k], pageTextDirections[k], page);
}
}
} catch (Error err) {
decodeStatus = decodeStatus+ "Error in decoding page "+ err.toString();
}
/**
* set flags after decode
*/
fontsInFile = (String) current.getObjectValue(PdfDictionary.Font);
imagesInFile = (String) current.getObjectValue(PdfDictionary.Image);
pdfData = (PdfData) current.getObjectValue(ValueTypes.PDFData);
pdfImages = (PdfImageData) current.getObjectValue(ValueTypes.PDFImages);
//read flags
resultsFromDecode.update(current,true);
}else if(currentDisplay.getType()==DynamicVectorRenderer.CREATE_HTML || currentDisplay.getType()==DynamicVectorRenderer.CREATE_SVG){ //needed if no page content
int mediaW = pageData.getMediaBoxWidth(pageNumber);
int mediaH = pageData.getMediaBoxHeight(pageNumber);
//int mediaX = pageData.getMediaBoxX(pageNumber);
//int mediaY = pageData.getMediaBoxY(pageNumber);
int rotation = pageData.getRotation(pageNumber);
currentDisplay.init(mediaW, mediaH, rotation,options.getPageColor());
}
}
/** turn off status bar update */
// <start-adobe>
if (t != null) {
t.stop();
statusBar.setProgress(100);
}
// <end-adobe>
/**
* handle acroform data to display
*/
if (renderPage) {
if (!isDuplicate && formRenderer != null && !formRenderer.ignoreForms() && formRenderer.hasFormsOnPage(page)) {
//swing needs it to be done with invokeLater
if(!SwingUtilities.isEventDispatchThread() && formRenderer.getFormFactory().getType()==FormFactory.SWING){ //
final PdfStreamDecoder finalCurrent = current;
final Runnable doPaintComponent2 = new Runnable() {
public void run() {
createFormComponents(page, finalCurrent);
isDecoding=false;
//tell software page all done
currentDisplay.flagDecodingFinished();
}
};
//don't set isDecoding false at end but in our routine
isForm=true;
SwingUtilities.invokeLater(doPaintComponent2);
this.waitForDecodingToFinish();
}else{
createFormComponents(page,current);
isDecoding=false;
}
}
}
}
} finally {
if(!isForm){
isDecoding = false;
if(statusBar!=null)
statusBar.percentageDone=100;
}
}
}
//Check for exceptions in TrueType hinting and re decode if neccessary
if (TTGlyph.redecodePage) {
TTGlyph.redecodePage = false;
decodePage(rawPage);
}
//tell software page all done
currentDisplay.flagDecodingFinished();
}
/**
* see if page available if in Linearized mode or return true
* @param rawPage
* @return
*/
public synchronized boolean isPageAvailable(int rawPage) {
return linearParser.isPageAvailable(rawPage, currentPdfFile);
}
void createFormComponents(int page, PdfStreamDecoder current) {
//no forms on JavaFX
if(currentDisplay.getType()==DynamicVectorRenderer.CREATE_JAVAFX)
return;
if(currentOffset!=null)
formRenderer.getCompData().setPageValues(scaling, displayRotation,(int)swingPainter.getIndent(),0,0,Display.SINGLE_PAGE,currentOffset.widestPageNR,currentOffset.widestPageR);
formRenderer.createDisplayComponentsForPage(page,current);
}
/**
* store objects to use on a print
* @param page
* @param type
* @param colors
* @param obj
* @throws PdfException
*/
public void printAdditionalObjectsOverPage(int page, int[] type, Color[] colors, Object[] obj) throws PdfException {
swingPrinter.printAdditionalObjectsOverPage(page, type, colors, obj);
}
/**
* store objects to use on a print
* @param type
* @param colors
* @param obj
* @throws PdfException
*/
public void printAdditionalObjectsOverAllPages(int[] type, Color[] colors, Object[] obj) throws PdfException {
swingPrinter.printAdditionalObjectsOverAllPages(type, colors, obj);
}
/**
* uses hires images to create a higher quality display - downside is it is
* slower and uses more memory (default is false).- Does nothing in OS
* version
*
* @param value
*/
public void useHiResScreenDisplay(boolean value) {
}
//<start-adobe>
/**
* decode a page as a background thread (use
* other background methods to access data)
*
* we now recommend you use decodePage as this has been heavily optimised for speed
*/
final synchronized public void decodePageInBackground(int i) throws Exception {
if (isDecoding) {
if(LogWriter.isOutput()){
LogWriter.writeLog("[PDF]WARNING - this file is being decoded already in foreground");
LogWriter.writeLog("[PDF]Multiple access not recommended - use waitForDecodingToFinish() to check");
}
} else if (isBackgroundDecoding) {
if(LogWriter.isOutput())
LogWriter.writeLog("[PDF]WARNING - this file is being decoded already in background");
} else {
try{
isBackgroundDecoding = true;
/** check in range */
if (i > pageCount) {
if(LogWriter.isOutput())
LogWriter.writeLog("Page out of bounds");
} else {
/** get pdf object id for page to decode */
String currentPageOffset = currentPdfFile.getReferenceforPage(i);
/**
* decode the file if not already decoded, there is a valid
* object id and it is unencrypted
*/
if ((currentPageOffset != null)) {
if (currentPdfFile == null)
throw new PdfException(
"File not open - did you call closePdfFile() inside a loop and not reopen");
/** read page or next pages */
PdfObject pdfObject=new PageObject(currentPageOffset);
currentPdfFile.readObject(pdfObject);
PdfObject Resources=pdfObject.getDictionary(PdfDictionary.Resources);
if (pdfObject != null) {
ObjectStore backgroundObjectStoreRef = new ObjectStore();
PdfStreamDecoder backgroundDecoder = new PdfStreamDecoder(currentPdfFile);
backgroundDecoder.setParameters(true, false, 0, extractionMode);
backgroundDecoder.setXMLExtraction(options.isXMLExtraction());
externalHandlers.addHandlers(backgroundDecoder);
backgroundDecoder.setObjectValue(ValueTypes.Name, filename);
backgroundDecoder.setObjectValue(ValueTypes.ObjectStore,backgroundObjectStoreRef);
backgroundDecoder.setObjectValue(ValueTypes.PDFPageData,pageData);
backgroundDecoder.setIntValue(ValueTypes.PageNum, i);
res.setupResources(backgroundDecoder, false, Resources,pageNumber,currentPdfFile);
backgroundDecoder.decodePageContent(pdfObject);
//get extracted data
pdfBackgroundData = (PdfData)backgroundDecoder.getObjectValue(ValueTypes.PDFData);
pdfBackgroundImages = (PdfImageData) backgroundDecoder.getObjectValue(ValueTypes.PDFImages);
}
}
}
}catch(Exception e){
e.printStackTrace();
}finally {
isBackgroundDecoding = false;
}
}
}
//<end-adobe>
/**
* get page count of current PDF file
*/
final public int getPageCount() {
return pageCount;
}
/**
* return true if the current pdf file is encrypted <br>
* check <b>isFileViewable()</b>,<br>
* <br>
* if file is encrypted and not viewable - a user specified password is
* needed.
*/
final public boolean isEncrypted() {
if (currentPdfFile != null) {
PdfFileReader objectReader=currentPdfFile.getObjectReader();
DecryptionFactory decryption=objectReader.getDecryptionObject();
return decryption!=null && decryption.getBooleanValue(PDFflags.IS_FILE_ENCRYPTED);
}else
return false;
}
//<start-demo>
/**
//<end-demo>
/**
* show if encryption password has been supplied or set a certificate
*/
final public boolean isPasswordSupplied() {
FileAccess fileAccess= (FileAccess) getJPedalObject(PdfDictionary.FileAccess);
return fileAccess.isPasswordSupplied(currentPdfFile);
}
/**
* show if encrypted file can be viewed,<br>
* if false a password needs entering
*/
public boolean isFileViewable() {
FileAccess fileAccess= (FileAccess) getJPedalObject(PdfDictionary.FileAccess);
return fileAccess.isFileViewable(currentPdfFile);
}
/**
* show if content can be extracted
*/
public boolean isExtractionAllowed() {
if (currentPdfFile != null){
PdfFileReader objectReader=currentPdfFile.getObjectReader();
DecryptionFactory decryption=objectReader.getDecryptionObject();
return decryption==null || decryption.getBooleanValue(PDFflags.IS_EXTRACTION_ALLOWED);
}else
return false;
}
/**
* used to retest access and see if entered password is valid,<br>
* If so file info read and isFileViewable will return true
*/
private void verifyAccess() {
if (currentPdfFile != null) {
try {
openPdfFile();
} catch (Exception e) {
if(LogWriter.isOutput())
LogWriter.writeLog("Exception " + e + " opening file");
}
}
}
/**
* set a password for encryption - software will resolve if user or owner
* password- calls verifyAccess() from 2.74 so no separate call needed
*/
final public void setEncryptionPassword(String password) throws PdfException {
if (currentPdfFile == null)
throw new PdfException("Must open PdfDecoder file first");
currentPdfFile.getObjectReader().setPassword(password);
verifyAccess();
}
/**
* routine to open a byte stream containing the PDF file and extract key info
* from pdf file so we can decode any pages. Does not actually decode the
* pages themselves - By default files over 16384 bytes are cached to disk
* but this can be altered by setting PdfFileReader.alwaysCacheInMemory to a maximimum size or -1 (always keep in memory)
*
*/
final public void openPdfArray(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();
try {
currentPdfFile = new PdfReader();
/** get reader object to open the file */
currentPdfFile.openPdfFile(data);
openPdfFile();
/** store file name for use elsewhere as part of ref key without .pdf */
objectStoreRef.storeFileName("r" + System.currentTimeMillis());
} catch (Exception e) {
throw new PdfException("[PDF] OpenPdfArray generated exception "
+ e.getMessage());
}
}
/**
* allow user to open file using Certificate and key
* @param filename
* @param certificate
* @param key
*/
public void openPdfFile(String filename, Certificate certificate, PrivateKey key) throws PdfException{
/**
* set values and then call generic open
*/
fileAcces.setUserEncryption(certificate, key);
openPdfFile(filename);
}
/**
* routine to open PDF file and extract key info from pdf file so we can
* decode any pages. Does not actually decode the pages themselves. Also
* reads the form data. You must explicitly close any open files with
* closePdfFile() to Java will not release all the memory
*/
final public void openPdfFile(final String filename) throws PdfException {
if(isOpen && linearParser.linearizedBackgroundReaderer==null)
//throw new RuntimeException("Previous file not closed");
closePdfFile(); //also checks decoding done
isOpen = false;
displayScaling = null;
//System.out.println(filename);
this.filename = filename;
res.flush();
//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 =fileAcces.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();
}
/**
* 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 any open files with
* closePdfFile() or Java will not release all the memory
*/
final public void openPdfFile(final String filename,String password) throws PdfException {
if(isOpen)
//throw new RuntimeException("Previous file not closed");
closePdfFile(); //also checks decoding done
isOpen = false;
displayScaling = null;
this.filename = filename;
res.flush();
/** store file name for use elsewhere as part of ref key without .pdf */
objectStoreRef.storeFileName(filename);
currentPdfFile = new PdfReader(password);
/** get reader object to open the file */
currentPdfFile.openPdfFile(filename);
openPdfFile();
}
/**
* routine to open PDF file via URL and extract key info from pdf file so we
* can decode any pages - Does not actually decode the pages themselves -
* Also reads the form data - Based on an idea by Peter Jacobsen
* <br />
* You must explicitly close any open files with closePdfFile() so Java will
* release all the memory
* <br />
*
* If boolean supportLinearized is true, method will return with true value once Linearized part read
*/
final public boolean openPdfFileFromURL(String pdfUrl, boolean supportLinearized) throws PdfException {
InputStream is=null;
String rawFileName = null;
try{
URL url;
url = new URL(pdfUrl);
rawFileName = url.getPath().substring(url.getPath().lastIndexOf('/')+1);
is = url.openStream();
}catch(Exception e){
e.printStackTrace();
}
return readFile(supportLinearized, is, rawFileName);
}
/**
* routine to open PDF file via InputStream and extract key info from pdf file so we
* can decode any pages - Does not actually decode the pages themselves -
* <p/>
* You must explicitly close any open files with closePdfFile() to Java will
* not release all the memory
*
* IMPORTANT NOTE: If the stream does not contain enough bytes, test for Linearization may fail
* If boolean supportLinearized is true, method will return with true value once Linearized part read
* (we recommend use you false unless you know exactly what you are doing)
*/
final public boolean openPdfFileFromInputStream(InputStream is, boolean supportLinearized) throws PdfException {
String rawFileName = "inputstream"+System.currentTimeMillis()+ '-' +objectStoreRef.getKey()+".pdf";
//make sure it will be deleted
objectStoreRef.setFileToDeleteOnFlush(ObjectStore.temp_dir+rawFileName);
objectStoreRef.setFileToDeleteOnFlush(rawFileName);
return readFile(supportLinearized, is, rawFileName);
}
/**
* common code for reading URL and InputStream
* @param supportLinearized
* @param is
* @param rawFileName
* @return
* @throws PdfException
*/
private boolean readFile(boolean supportLinearized, InputStream is, String rawFileName) throws PdfException {
displayScaling = null;
res.flush();
currentPdfFile = new PdfReader();
if(is!=null){
try {
File tempURLFile;
if(rawFileName.startsWith("inputstream"))
tempURLFile = new File(ObjectStore.temp_dir+rawFileName);
else
tempURLFile = ObjectStore.createTempFile(rawFileName);
/** store fi name for use elsewhere as part of ref key without .pdf */
objectStoreRef.storeFileName(tempURLFile.getName().substring(0, tempURLFile.getName().lastIndexOf('.')));
if(supportLinearized){
byte[] linearBytes=linearParser.readLinearData(currentPdfFile,tempURLFile,is, this);
if(linearBytes!=null){
/**
* read partial data so we can display
*/
currentPdfFile.openPdfFile(linearBytes);
openPdfFile();
//read rest of file and reset
linearParser.linearizedBackgroundReaderer.start();
return true;
}
}else{
currentPdfFile.openPdfFile(is);
openPdfFile();
}
if(supportLinearized){
//else{
// System.out.println("xx");
/** get reader object to open the file */
openPdfFile(tempURLFile.getAbsolutePath());
/** store fi name for use elsewhere as part of ref key without .pdf */
objectStoreRef.storeFileName(tempURLFile.getName().substring(0, tempURLFile.getName().lastIndexOf('.')));
// }
}
} catch (IOException e) {
if(LogWriter.isOutput())
LogWriter.writeLog("[PDF] Exception " + e + " opening URL ");
e.printStackTrace();
}
}
return false;
}
/**
* common code to all open routines
*/
private synchronized void openPdfFile() throws PdfException {
//<start-demo>
/**
//<end-demo>
/**/
pageNumber = 1; // reset page number for metadata
// force redraw
swingPainter.forceRedraw();
try {
isDecoding = true;
pages.resetCachedValues();
//<start-adobe>
// remove listener if not removed by close
if (viewListener!=null) {
//flush any cached pages
pages.flushPageCaches();
removeComponentListener(viewListener);
viewListener.dispose();
viewListener=null;
}
//<end-adobe>
// set cache size to use
currentPdfFile.getObjectReader().setCacheSize(minimumCacheSize);
// reset printing
swingPrinter.lastPrintedPage = -1;
swingPrinter.currentPrintDecoder = null;
if (formRenderer != null) {
formRenderer.getCompData().setRootDisplayComponent(this);
}
//invalidate();
// reset page data - needed to flush crop settings
pageData = new PdfPageData();
// read and log the version number of pdf used
String pdfVersion = currentPdfFile.getObjectReader().getType();
if(LogWriter.isOutput())
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=null ;
int linearPageCount=-1;
//linear page object set differently
if(linearParser.hasLinearData()){
/**
* read and decode the hints table and the ref table
*/
pdfObject=linearParser.readHintTable(pdfObject,currentPdfFile);
linearPageCount=linearParser.getPageCount();
}else//<end-gpl>
pdfObject=currentPdfFile.getObjectReader().readReferenceTable(null);
//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()) {
if (pdfObject != null){
pdfObject.ignoreRecursion(ignoreRecursion);
res.setValues(pdfObject,currentPdfFile);
//check read as may be used for Dest
PdfObject nameObj=pdfObject.getDictionary(PdfDictionary.Names);
if (nameObj != null){
currentPdfFile.readNames(nameObj, options.getJS(),false);
}
}
int type=pdfObject.getParameterConstant(PdfDictionary.Type);
if(type!=PdfDictionary.Page){
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;
//System.out.println("test code called");
}
}
if (pdfObject != null) {
if(LogWriter.isOutput())
LogWriter.writeLog("Pages being read from "+pdfObject+ ' '+pdfObject.getObjectRefAsString());
pageNumber = 1; // reset page number for metadata
// reset lookup table
//pageLookup = new PageLookup();
//flush annots before we reread
if(formRenderer!=null)
formRenderer.resetAnnotData(insetW, insetH, pageData, 1, currentPdfFile,null);
//recursively read all pages
int tempPageCount=readAllPageReferences(ignoreRecursion, pdfObject, new HashMap(1000), new HashMap(1000),1);
//set PageCount if in Linearized data
if(linearPageCount>0){
pageCount=linearPageCount;
}else{
pageCount = tempPageCount - 1; // save page count
}
pageNumber =0; // reset page number for metadata;
if (this.pageCount == 0 && LogWriter.isOutput())
LogWriter.writeLog("No pages found");
}
// pass handle into renderer
if (formRenderer != null) {
formRenderer.openFile(pageCount);
formRenderer.resetFormData(insetW, insetH, pageData, currentPdfFile, res.getPdfObject(PdfResources.AcroFormObj));
}
}
currentOffset = null;
// reset so if continuous view mode set it will be recalculated for page
pages.disableScreen();
pages.stopGeneratingPage();
//force back if only 1 page
if (pageCount < 2)
displayView = Display.SINGLE_PAGE;
else
displayView = options.getPageMode();
//<start-adobe>
setDisplayView(this.displayView, alignment); //force reset and add back listener
/**
//<end-adobe>
if (currentOffset == null)
currentOffset = new PageOffsets(pageCount, pageData);
/**/
isOpen = true;
} catch (PdfException e) {
//ensure all data structures/handles flushed
isDecoding = false;
isOpen=true; //temporarily set to true as otherwise will not work
closePdfFile();
isOpen=false;
isDecoding = false;
throw new PdfException(e.getMessage() + " opening file");
}
isDecoding = false;
}
/**
* will return some dictionary values - if not a set value, will return null
* @return
*/
public Object getJPedalObject(int id){
switch(id){
case PdfDictionary.Layer:
return res.getPdfLayerList();
case PdfDictionary.Linearized:
return linearParser.getLinearObject(isOpen,currentPdfFile);
case PdfDictionary.LinearizedReader:
return linearParser.linearizedBackgroundReaderer;
case PdfDictionary.FileAccess:
return fileAcces;
default:
return null;
}
}
public void setPageMode(int mode){
options.setPageMode(mode);
}
/**
* read the data from pages lists and pages so we can open each page.
*
* object reference to first trailer
*/
private int readAllPageReferences(boolean ignoreRecursion, PdfObject pdfObject , Map rotations, Map parents, int tempPageCount) {
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)
type= PdfDictionary.Pages;
/**
* 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){
Object savedRotation=rotations.get(parent);
if(savedRotation!=null)
rawRotation= (Integer) savedRotation;
}
if(rawRotation==-1)
parent=(String) 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;
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)
pageData.setMediaBox(mediaBox);
if (cropBox != null)
pageData.setCropBox(cropBox);
/** process page to read next level down */
if (type==PdfDictionary.Pages) {
res.setPdfObject(PdfResources.GlobalResources, pdfObject.getDictionary(PdfDictionary.Resources));
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<kidCount;ii++){
nextObject=new PageObject(new String(kidList[ii]));
nextObject.ignoreRecursion(ignoreRecursion);
nextObject.ignoreStream(true);
currentPdfFile.readObject(nextObject);
tempPageCount=readAllPageReferences(ignoreRecursion, nextObject, rotations, parents,tempPageCount);
}
}
} else if (type==PdfDictionary.Page) {
if(debug)
System.out.println("PAGE---------------------currentPageOffset="+currentPageOffset);
// store ref for later
currentPdfFile.setLookup(currentPageOffset, tempPageCount);
pageData.checkSizeSet(tempPageCount); // make sure we have min values
/**
* add Annotations
*/
if (formRenderer != null) {
// read the annotations reference for the page we have found lots of issues with annotations so trap errors
byte[][] annotList = pdfObject.getKeyArray(PdfDictionary.Annots);
//allow for empty
if(annotList!=null && annotList.length==1 && annotList[0]==null)
annotList=null;
if (annotList != null) {
// pass handle into renderer
formRenderer.resetAnnotData(insetW, insetH, pageData, tempPageCount, currentPdfFile, annotList);
}
}
tempPageCount++;
}
return tempPageCount;
}
//<end-canoo><end-os>
/**
* shows if text extraction is XML or pure text
*/
public boolean isXMLExtraction() {
return options.isXMLExtraction();
}
/**
* XML extraction is the default - pure text extraction is much faster
*/
public void useTextExtraction() {
options.setXMLExtraction(false);
}
/**
* XML extraction is the default - pure text extraction is much faster
*/
public void useXMLExtraction() {
options.setXMLExtraction(true);
}
/**
* remove all displayed objects for JPanel display (wipes current page)
*/
public void clearScreen() {
currentDisplay.flush();
pages.refreshDisplay();
}
/**
* 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(int size) {
this.minimumCacheSize = size;
}
/**
* shows if embedded fonts present on page just decoded
*/
public boolean hasEmbeddedFonts() {
return resultsFromDecode.hasEmbeddedFonts();
}
/**
* shows if whole document contains embedded fonts and uses them
*/
final public boolean PDFContainsEmbeddedFonts() throws Exception {
boolean hasEmbeddedFonts = false;
PdfStreamDecoder current = new PdfStreamDecoder(currentPdfFile);
PdfObject pdfObject;
/**
* scan all pages
*/
for (int page = 1; page < pageCount + 1; page++) {
/** get pdf object id for page to decode */
String currentPageOffset =currentPdfFile.getReferenceforPage(page);
/**
* decode the file if not already decoded, there is a valid object id and it is unencrypted
*/
if (currentPageOffset != null) {
/** read page or next pages */
pdfObject=new PageObject(currentPageOffset);
pdfObject.ignoreStream(true);
currentPdfFile.readObject(pdfObject);
/** get information for the page */
byte[][] pageContents= pdfObject.getKeyArray(PdfDictionary.Contents);
if (pageContents != null) {
//current.setParameters(true, false, renderMode, extractionMode);
//externalHandlers.addHandlers(current);
//current.setObjectValue(ValueTypes.PDFPageData,pageData);
//current.setIntValue(ValueTypes.PageNum, page);
//current.setObjectValue(ValueTypes.DynamicVectorRenderer,currentDisplay);
/**read the resources for the page*/
res.setupResources(current, false, pdfObject.getDictionary(PdfDictionary.Resources), pageNumber,currentPdfFile);
hasEmbeddedFonts = current.getBooleanValue(ValueTypes.EmbeddedFonts);
// exit on first true
if (hasEmbeddedFonts)
page = this.pageCount;
}
}
}
return hasEmbeddedFonts;
}
/**
* given a ref, what is the page
* @param ref - PDF object reference
* @return - page number with being first page
*/
public int getPageFromObjectRef(String ref) {
return currentPdfFile.convertObjectToPageNumber(ref);
}
/**
* Returns list of the fonts used on the current page decoded or null
* type can be PdfDictionary.Font or PdfDictionary.Image
*/
public String getInfo(int type) {
String returnValue;
switch (type) {
case PdfDictionary.Font:
if (fontsInFile == null) {
returnValue = "No fonts defined";
} else {
returnValue = fontsInFile;
}
break;
case PdfDictionary.Image:
if (imagesInFile == null) {
returnValue = "No images defined as XObjects";
} else {
returnValue = imagesInFile;
}
break;
default:
returnValue = null;
}
return returnValue;
}
/**
* used to update statusBar object if exists
*/
private class ProgressListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {
statusBar.setProgress((int) (statusBar.percentageDone));
}
}
/**
* Allow user to access Forms renderer - returns null not available (should not generally be needed)
*/
public AcroRenderer getFormRenderer() {
return formRenderer;
}
/**
* shows if page reported any errors while printing. Log
* can be found with getPageFailureMessage()
*
* @return Returns the printingSuccessful.
*/
public boolean isPageSuccessful() {
return swingPrinter.isPageSuccessful();
}
/**
* return any errors or other messages while calling decodePage() - zero
* length is no problems
*/
public String getPageDecodeReport() {
return decodeStatus;
}
/**
* Return String with all error messages from last printed (useful for
* debugging)
*/
public String getPageFailureMessage() {
return swingPrinter.getPageFailureMessage();
}
/**
* If running in GUI mode, will extract a section of rendered page as
* BufferedImage -coordinates are PDF co-ordinates. If you wish to use hires
* image, you will need to enable hires image display with
* decode_pdf.useHiResScreenDisplay(true);
*
* @param t_x1
* @param t_y1
* @param t_x2
* @param t_y2
* @param scaling
* @return pageErrorMessages - Any printer errors
*/
public BufferedImage getSelectedRectangleOnscreen(float t_x1, float t_y1,
float t_x2, float t_y2, float scaling) {
BufferedImage img= swingPainter.getSelectedRectangleOnscreen(t_x1, t_y1,t_x2, t_y2, scaling, pageNumber, pageData,formRenderer, currentDisplay, currentPdfFile);
formRenderer.getCompData().resetScaledLocation(pages.getOldScaling(), displayRotation, 0);
return img;
}
/**
* 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)
*/
public void setObjectStore(ObjectStore newStore) {
objectStoreRef = newStore;
}
//<start-adobe>
/**
* returns object containing grouped text of last decoded page
* - if no page decoded, a Runtime exception is thrown to warn user
* Please see org.jpedal.examples.text for example code.
*
*/
public PdfGroupingAlgorithms getGroupingObject() throws PdfException {
return options.getGroupingObject(lastPageDecoded, getPdfData(), pageData);
}
/**
* returns object containing grouped text from background grouping - Please
* see org.jpedal.examples.text for example code
*/
public PdfGroupingAlgorithms getBackgroundGroupingObject() {
return options.getBackgroundGroupingObject(pdfBackgroundData,pageData);
}
//<end-adobe>
/**
* get PDF version in file
*/
final public String getPDFVersion() {
if(currentPdfFile==null)
return "";
else
return currentPdfFile.getObjectReader().getType();
}
//<start-adobe>
/**
* used for non-PDF files to reset page
*/
public void resetForNonPDFPage(int pageCount) {
displayScaling = null;
/** set hires mode or not for display */
currentDisplay.setHiResImageForDisplayMode(false);
fontsInFile = "";
this.pageCount = pageCount;
if (formRenderer != null)
formRenderer.removeDisplayComponentsFromScreen();
// reset page data
this.pageData = new PdfPageData();
}
/**
* set view mode used in panel and redraw in new mode
* SINGLE_PAGE,CONTINUOUS,FACING,CONTINUOUS_FACING delay is the time in
* milli-seconds which scrolling can stop before background page drawing
* starts Multipage views not in OS releases
*/
public void setDisplayView(int displayView, final int orientation) {
//
// //ensure method is correctly accessed
// if (SwingUtilities.isEventDispatchThread()) {
//
// setDisplayViewx(displayView, orientation);
//
// } else {
// final Runnable doPaintComponent = new Runnable() {
//
// public void run() {
// setDisplayViewx(displayView,orientation);
// }
// };
// SwingUtilities.invokeLater(doPaintComponent);
// }
// }
//
// private void setDisplayViewx(int displayView, int orientation) {
this.alignment = orientation;
if (pages != null)
pages.stopGeneratingPage();
boolean needsReset = (displayView != Display.SINGLE_PAGE || this.displayView != Display.SINGLE_PAGE);
if (needsReset && (this.displayView == Display.FACING || displayView == Display.FACING))
needsReset = false;
if (displayView != Display.SINGLE_PAGE)
needsReset = true;
boolean hasChanged = displayView != this.displayView;
//log what we are changing from
int lastDisplayView=this.displayView;
this.displayView = displayView;
//<start-thin>
switch (displayView) {
case Display.SINGLE_PAGE:
if(pages==null || hasChanged){
pages = new SingleDisplay(pageNumber, pageCount, currentDisplay);
}
break;
}
//<end-thin>
/***/
// remove listener if setup
if (viewListener!=null) {
removeComponentListener(viewListener);
viewListener.dispose();
viewListener=null;
}
/**
* setup once per page getting all page sizes and working out settings
* for views
*/
if (currentOffset == null)
currentOffset = new PageOffsets(pageCount, pageData);
pages.setup(useAcceleration, currentOffset, this);
pages.init(scaling, pageCount, displayRotation, pageNumber, currentDisplay, true, pageData, insetW, insetH);
// force redraw
swingPainter.forceRedraw();
pages.refreshDisplay();
// add listener if one not already there
if (viewListener==null) {
viewListener = new RefreshLayout();
addComponentListener(viewListener);
}
//move to correct page
if (pageNumber > 0) {
if (hasChanged && displayView == Display.SINGLE_PAGE) {
try {
unsetScaling();
setPageParameters(scaling, pageNumber, displayRotation);
invalidate();
updateUI();
decodePage(pageNumber);
} catch (Exception e) {
e.printStackTrace();
}
} else if (displayView != Display.SINGLE_PAGE && displayView != Display.PAGEFLOW3D) {
throw new RuntimeException("Only SINGLE_PAGE is available in LGPL release");
/**/
}
}
}
//<end-adobe>
/**
* flag to show if we suspect problem with some images
*/
public boolean hasAllImages() {
return resultsFromDecode.getImagesProcessedFully();
}
/**
* allow user to set certain paramters - only supports DecodeStatus.Timeout at present
* @param status
* @param value
*/
// public void setPageDecodeStatus(int status, Object value) {
//
// if(status==(DecodeStatus.Timeout)){
// if(value instanceof Boolean){
//
// boolean timeout=((Boolean)value).booleanValue();
// if(timeout && current!=null)
// current.reqestTimeout(null);
//
// }else if(value instanceof Integer){
//
// if(current!=null)
// current.reqestTimeout(value);
// }
// }else
// new RuntimeException("Unknown parameter");
// }
public boolean getPageDecodeStatus(int status) {
/**if(status.equals(DecodeStatus.PageDecodingSuccessful))
return pageSuccessful;
else*/
return resultsFromDecode.getPageDecodeStatus(status);
}
/**
* get page statuses
*/
public String getPageDecodeStatusReport(int status) {
return resultsFromDecode.getPageDecodeStatusReport(status);
}
/**
* set print mode (Matches Abodes Auto Print and rotate output
*/
public void setPrintAutoRotateAndCenter(boolean value) {
swingPrinter.isPrintAutoRotateAndCenter = value;
}
public void setPrintCurrentView(boolean value) {
swingPrinter.printOnlyVisible = value;
}
/**
* allows external helper classes to be added to JPedal to alter default functionality -
* not part of the API and should be used in conjunction with IDRsolutions only
* <br>if Options.FormsActionHandler is the type then the <b>newHandler</b> should be
* of the form <b>org.jpedal.objects.acroforms.ActionHandler</b>
*
* @param newHandler
* @param type
*/
public void addExternalHandler(Object newHandler, int type) {
switch (type) {
case Options.FormFactory:
formRenderer.setFormFactory((FormFactory) newHandler);
break;
case Options.MultiPageUpdate:
customSwingHandle = newHandler;
break;
// case Options.LinkHandler:
//
// if (formRenderer != null)
// formRenderer.resetHandler(newHandler, this,Options.LinkHandler);
//
// break;
case Options.FormsActionHandler:
if (formRenderer != null)
formRenderer.resetHandler(newHandler, this,Options.FormsActionHandler);
break;
//<start-thin><start-adobe>
case Options.SwingMouseHandler:
if(formRenderer != null){
formRenderer.getActionHandler().setMouseHandler((SwingMouseListener) newHandler);
}
break;
case Options.ThumbnailHandler:
pages.setThumbnailPanel((org.jpedal.examples.simpleviewer.gui.generic.GUIThumbnailPanel) newHandler);
break;
//<end-adobe><end-thin>
default:
externalHandlers.addExternalHandler(newHandler,type);
}
}
/**
* allows external helper classes to be accessed if needed - also allows user to access SwingGUI if running
* full Viewer package - not all Options available to get - please contact IDRsolutions if you are looking to
* use
*
* @param type
*/
public Object getExternalHandler(int type) {
switch (type) {
case Options.FormFactory:
return formRenderer.getFormFactory();
case Options.MultiPageUpdate:
return customSwingHandle;
case Options.Display:
return pages;
case Options.CurrentOffset:
return currentOffset;
default:
return externalHandlers.getExternalHandler(type);
}
}
public void unsetScaling() {
displayScaling = null;
}
public PdfObjectReader getIO() {
return currentPdfFile;
}
public String getFileName() {
return filename;
}
public boolean isForm() {
return res.isForm();
}
/**
* reference for Page object
* @param page
* @return String ref (ie 1 0 R)
* pdfObject=new PageObject(currentPageOffset);
* currentPdfFile.readObject(pdfObject);
*/
public String getReferenceforPage(int page){
return currentPdfFile.getReferenceforPage(page);
}
/**
* allow printing of different sizes pages
* (default is false as PrintJob does not support different page sizes whereas
* DocPrintJob does)
* @param allowDifferentPrintPageSizes
*/
public void setAllowDifferentPrintPageSizes(boolean allowDifferentPrintPageSizes) {
swingPrinter.allowDifferentPrintPageSizes = allowDifferentPrintPageSizes;
}
/**holds lines of text we create*/
private org.jpedal.text.TextLines textLines=new TextLines();
/**
*
* access textlines object
*/
public TextLines getTextLines() {
return textLines;
}
//<start-adobe>
/**
* set an inset display so that display will not touch edge of panel*/
final public void setInset(int width,int height) {
this.insetW=width;
this.insetH=height;
}
/**
* make screen scroll to ensure point is visible
*/
public void ensurePointIsVisible(Point p){
super.scrollRectToVisible(new Rectangle(p.x,y_size-p.y,scrollInterval,scrollInterval));
}
//<end-adobe>
/**
* set a left margin for printing pages (ie for duplex)
* @param oddPages
*/
public void setPrintIndent(int oddPages, int evenPages) {
swingPrinter.setPrintIndent(oddPages,evenPages);
}
/**
* allow user to 'move' display of PDF
*
* mode is a Constant in org.jpedal.external.OffsetOptions (ie OffsetOptions.SWING_DISPLAY,OffsetOptions.PRINTING)
*/
public void setUserOffsets(int x, int y, int mode){
swingPainter.getDisplayOffsets().setUserOffsets(x, y, mode, externalHandlers, this);
}
public Point getUserOffsets(int mode){
return swingPainter.getDisplayOffsets().getUserOffsets(mode);
}
/**
* get sizes of panel <BR>
* This is the PDF pagesize (as set in the PDF from pagesize) -
* It now includes any scaling factor you have set (ie a PDF size 800 * 600
* with a scaling factor of 2 will return 1600 *1200)
*/
final public Dimension getMaximumSize() {
Dimension pageSize=null;
if(displayView!=Display.SINGLE_PAGE)
pageSize = pages.getPageSize(displayView);
if(pageSize==null){
if(displayRotation==90 || displayRotation==270)
pageSize= new Dimension((int)(y_size+insetW+insetW),x_size+insetH+insetH);
else
pageSize= new Dimension((int)(x_size+insetW+insetW),y_size+insetH+insetH);
}
if(pageSize==null)
pageSize=getMinimumSize();
return pageSize;
}
/**
* get width*/
final public Dimension getMinimumSize() {
return new Dimension(100+insetW,100+insetH);
}
/**
* get sizes of panel <BR>
* This is the PDF pagesize (as set in the PDF from pagesize) -
* It now includes any scaling factor you have set (ie a PDF size 800 * 600
* with a scaling factor of 2 will return 1600 *1200)
*/
public Dimension getPreferredSize() {
return getMaximumSize();
}
//<start-adobe>
/**
* update rectangle we draw to highlight an area -
* See SimpleViewer example for example code showing current usage.
*/
final public void updateCursorBoxOnScreen(Rectangle newOutlineRectangle,Color outlineColor) {
if(displayView!=Display.SINGLE_PAGE)
return;
swingPainter.updateCursorBoxOnScreen(newOutlineRectangle, outlineColor, this, pageNumber);
}
//<end-adobe>
public void paint(Graphics g){
try{
super.paint(g);
if(!isDecoding){
swingPainter.drawCursor(g,alignment);
}
}catch(Exception e){
pages.flushPageCaches();
}catch(Error err){ //for tight memory
pages.flushPageCaches();
pages.stopGeneratingPage();
super.paint(g);
}
}
/**standard method to draw page and any highlights onto JPanel*/
public void paintComponent(Graphics g) {
final RenderChangeListener customRenderChangeListener=(RenderChangeListener)externalHandlers.getExternalHandler(Options.RenderChangeListener);
if(customRenderChangeListener!=null) //call custom class if present
customRenderChangeListener.renderingStarted(this.pageNumber);
super.paintComponent(g);
if (SwingUtilities.isEventDispatchThread()) {
threadSafePaint(g);
if(customRenderChangeListener!=null) //call custom class if present
customRenderChangeListener.renderingEnded(this.pageNumber);
} else {
final Graphics g2 = g;
final int page=pageNumber;
final Runnable doPaintComponent = new Runnable() {
public void run() {
threadSafePaint(g2);
if(customRenderChangeListener!=null) //call custom class if present
customRenderChangeListener.renderingEnded(page);
}
};
SwingUtilities.invokeLater(doPaintComponent);
}
}
/**
* update display
*/
synchronized void threadSafePaint(Graphics g) {
if(displayScaling==null)
return;
Graphics2D g2 = (Graphics2D) g;
if(!isDecoding && renderPage){
swingPainter.paintPage(g2,this,pages, pageData, pageNumber,currentDisplay,
displayView,displayRotation, insetW, insetH, textLines,myBorder,
currentOffset, externalHandlers);
}else{ //just fill the background
currentDisplay.setG2(g2);
currentDisplay.paintBackground(null);
}
}
/**
* get sizes of panel <BR>
* This is the PDF pagesize (as set in the PDF from pagesize) -
* It now includes any scaling factor you have set
*/
final public int getPDFWidth() {
if(displayRotation==90 || displayRotation==270)
return y_size+insetW+insetW;
else
return x_size+insetW+insetW;
}
/**
* get sizes of panel -
* This is the PDF pagesize
*/
final public int getPDFHeight() {
if((displayRotation==90 || displayRotation==270))
return x_size+insetH+insetH;
else
return y_size+insetH+insetH;
}
/**set border for screen and print which will be displayed<br>
* Setting a new value will enable screen and border painting - disable
* with disableBorderForPrinting() */
final public void setPDFBorder(Border newBorder){
this.myBorder=newBorder;
}
/**
* Enables/Disables hardware acceleration of screen rendering in 1.4 (default is on)
*/
public void setHardwareAccelerationforScreen(boolean useAcceleration) {
this.useAcceleration = useAcceleration;
}
//<start-adobe>
/**return amount to scroll window by when scrolling (default is 10)*/
public int getScrollInterval() {
return scrollInterval;
}
/**set amount to scroll window by when scrolling*/
public void setScrollInterval(int scrollInterval) {
this.scrollInterval = scrollInterval;
}
/**
* returns view mode used - ie SINGLE_PAGE,CONTINUOUS,FACING,CONTINUOUS_FACING (no effect in OS versions)
*/
public int getDisplayView() {
return displayView;
}
//<end-adobe>
/**
* set page scaling mode to use - default setting is PAGE_SCALING_REDUCE_TO_PRINTER_MARGINS
* All values start PAGE_SCALING
*/
public void setPrintPageScalingMode(int pageScalingMode) {
swingPrinter.setPrintPageScalingMode(pageScalingMode);
}
public void setUsePDFPaperSize(boolean usePDFPaperSize) {
swingPrinter.usePDFPaperSize = usePDFPaperSize;
}
public float getScaling() {
return scaling;
}
public int getInsetH() {
return insetH;
}
public int getInsetW() {
return insetW;
}
public Rectangle getCursorBoxOnScreen() {
return swingPainter.getCursorBoxOnScreen();
}
/**
* part of pageable interface - used only in printing
* Use getPageCount() for number of pages
*
* @see java.awt.print.Pageable#getNumberOfPages()
*/
public int getNumberOfPages() {
return swingPrinter.getNumberOfPages(pageCount);
}
/**
* part of pageable interface
*
* @see java.awt.print.Pageable#getPageFormat(int)
*/
public PageFormat getPageFormat(int p) throws IndexOutOfBoundsException {
return swingPrinter.getPageFormat(p,pageData,pageCount);
}
public void setCenterOnScaling(boolean center){
swingPrinter.setCenterOnScaling(center);
}
/**
* set pageformat for a specific page - if no pageFormat is set a default
* will be used. Recommended to use setPageFormat(PageFormat pf)
*/
public void setPageFormat(int p, PageFormat pf) {
swingPrinter.putPageFormat(p, pf);
}
/**
* set pageformat for a specific page - if no pageFormat is set a default
* will be used.
*/
public void setPageFormat(PageFormat pf) {
swingPrinter.putPageFormat("standard", pf);
}
/**
* set inclusive range to print (see SilentPrint.java and SimpleViewer.java
* for sample print code (invalid range will throw PdfException)
* can take values such as new PageRanges("3,5,7-9,15");
*/
public void setPagePrintRange(SetOfIntegerSyntax range) throws PdfException {
if (range == null)
throw new PdfException("[PDF] null page range entered");
swingPrinter.setPagePrintRange(range,pageCount);
}
/**
* allow user to select only odd or even pages to print
*/
public void setPrintPageMode(int mode) {
swingPrinter.setPrintPageMode(mode);
}
/**
* ask JPedal to stop printing a page
*/
final public void stopPrinting() {
swingPrinter.stopPrinting();
}
/**
* return page currently being printed or -1 if finished
*/
public int getCurrentPrintPage() {
return swingPrinter.getCurrentPrintPage();
}
public void resetCurrentPrintPage() {
swingPrinter.currentPrintPage = 0;
this.formRenderer.getCompData().resetAfterPrinting();
}
}