org.jpedal.display.MultiPageDecoder 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
Open Source (LGPL) 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-2017 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
@LICENSE@
*
* ---------------
* MultiPageDecoder.java
* ---------------
*/
package org.jpedal.display;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.Semaphore;
import org.jpedal.FileAccess;
import org.jpedal.PdfDecoderInt;
import org.jpedal.constants.SpecialOptions;
import static org.jpedal.display.Display.*;
import org.jpedal.external.Options;
import org.jpedal.external.RenderChangeListener;
import org.jpedal.gui.GUIFactory;
import org.jpedal.io.PdfObjectReader;
import org.jpedal.objects.PdfPageData;
import org.jpedal.objects.acroforms.AcroRenderer;
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.parser.PdfStreamDecoder;
import org.jpedal.parser.ValueTypes;
import org.jpedal.render.DynamicVectorRenderer;
import org.jpedal.text.TextLines;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.repositories.Vector_Int;
import org.jpedal.utils.repositories.generic.Vector_Rectangle_Int;
/**
* @author markee
*/
public abstract class MultiPageDecoder {
private final Semaphore semaphore = new Semaphore(1);
final GUIFactory gui;
//used to redraw multiple pages
private Thread worker;
public final Map currentPageViews = new HashMap();
private final FileAccess fileAccess;
private final PdfObjectReader currentPdfFile;
private final AcroRenderer formRenderer;
private int displayView;
private Map cachedPageViews = new WeakHashMap();
private final MultiPagesDisplay display;
private RenderChangeListener customRenderChangeListener;
//facing mode drag pages
private final BufferedImage[] facingDragCachedImages = new BufferedImage[4];
private BufferedImage facingDragTempLeftImg, facingDragTempRightImg;
private int facingDragTempLeftNo, facingDragTempRightNo;
private final MultiDisplayOptions multiDisplayOptions;
public final PdfDecoderInt pdf;
private final PdfPageData pageData;
final DecoderOptions options;
final DisplayOffsets offsets;
public MultiPageDecoder(final PdfDecoderInt pdf, final PdfPageData pageData, final MultiPagesDisplay display, final MultiDisplayOptions multiDisplayOptions,
final DynamicVectorRenderer currentDisplay, final int pageNumber, final FileAccess fileAccess,
final PdfObjectReader io, final AcroRenderer formRenderer, final DecoderOptions options) {
//Precaution incase incorrect PdfDecoderInt implementation is used
if (pdf.getExternalHandler() == null) {
this.gui = null;
} else {
this.gui = (GUIFactory) pdf.getExternalHandler().getExternalHandler(Options.GUIContainer);
}
this.pdf = pdf;
this.pageData = pageData;
this.display = display;
this.multiDisplayOptions = multiDisplayOptions;
this.fileAccess = fileAccess;
this.currentPdfFile = io;
this.formRenderer = formRenderer;
this.options = options;
offsets = (DisplayOffsets) pdf.getExternalHandler(Options.DisplayOffsets);
/*cache current page*/
if (currentDisplay != null) {
currentPageViews.put(pageNumber, currentDisplay);
}
}
/**
* used to decode multiple pages on views
*/
public void decodeOtherPages(int pageNumber, final int pageCount, final int displayView) {
this.displayView = displayView;
if (debugLayout) {
System.out.println("start decodeOtherPages " + pageNumber + ' ' + pageCount);
}
//Ensure page range does not drop below one
if (pageNumber < 1) {
pageNumber = 1;
}
final int oldPN = multiDisplayOptions.getPageNumber();
multiDisplayOptions.setPageNumber(pageNumber);
//Store the image to be used instead of filling the borders with white
if (displayView == FACING && multiDisplayOptions.isTurnoverOn()) {
final int lp;
if (multiDisplayOptions.isSeparateCover()) {
lp = (oldPN / 2) * 2;
} else {
lp = oldPN - (1 - (oldPN & 1));
}
if (offsets.getDragLeft()) {
facingDragTempLeftImg = facingDragCachedImages[0];
facingDragTempLeftNo = lp - 2;
facingDragTempRightImg = facingDragCachedImages[1];
facingDragTempRightNo = lp - 1;
} else {
facingDragTempLeftImg = facingDragCachedImages[2];
facingDragTempLeftNo = lp + 2;
facingDragTempRightImg = facingDragCachedImages[3];
facingDragTempRightNo = lp + 3;
}
}
facingDragCachedImages[0] = null;
facingDragCachedImages[1] = null;
facingDragCachedImages[2] = null;
facingDragCachedImages[3] = null;
calcDisplayedRange();
while (multiDisplayOptions.isRunning()) {
try {
Thread.sleep(100);
} catch (final InterruptedException e) {
LogWriter.writeLog("Exception: " + e.getMessage());
}
}
//need in JavaFX to avoid illegalThreadState
if (worker != null && worker.getState() == Thread.State.TERMINATED) {
worker = null;
}
// restart if not display.running - uses pages to control loop so I hope will
// pick up change
if (worker != null && worker.getState() != Thread.State.NEW) {
//still running
//System.out.println(worker.getState()+" "+worker);
} else if ((worker == null || !multiDisplayOptions.isRunning())) {
multiDisplayOptions.setRunning(true);
worker = new Thread() {
@Override
public void run() {
try {
semaphore.acquire();
} catch (final InterruptedException ex) {
LogWriter.writeLog("Exception: " + ex.getMessage());
}
try {
if (debugLayout) {
System.out.println("START=========================Started decoding pages "
+ multiDisplayOptions.getStartViewPage() + ' ' + multiDisplayOptions.getEndViewPage());
}
decodeOtherPages();
if (debugLayout) {
System.out.println("END=========================Pages done");
}
multiDisplayOptions.setRunning(false);
//tell user object if exists that pages painted
if (customRenderChangeListener != null) {
customRenderChangeListener.renderingWorkerFinished();
}
} catch (final Exception e) {
multiDisplayOptions.setRunning(false);
LogWriter.writeLog("Exception: " + e.getMessage());
} catch (final Error err) {
multiDisplayOptions.setRunning(false);
LogWriter.writeLog("Error: " + err.getMessage());
} finally {
semaphore.release();
}
}
};
worker.setDaemon(true);
worker.start();
}
}
/**
* scans all pages in visible range and decodes
*/
void decodeOtherPages() {
final int pageCount = pageData.getPageCount();
if (debugLayout) {
System.out.println("decodeOtherPages called");
}
multiDisplayOptions.setIsGeneratingOtherPages(true);
int page = multiDisplayOptions.getStartViewPage(), originalStart = multiDisplayOptions.getStartViewPage(), originalEnd = multiDisplayOptions.getEndViewPage() + 1;
//increase decoded range for facing with turnover
int firstFacing = 1, lastFacing = 1;
if (multiDisplayOptions.isTurnoverOn() && displayView == FACING) {
firstFacing = originalStart - 2;
lastFacing = firstFacing + 6;
if (firstFacing < 1) {
firstFacing = 1;
}
if (lastFacing > pageCount + 1) {
lastFacing = pageCount + 1;
}
}
int facingCount = lastFacing - firstFacing;
resetPageCaches(multiDisplayOptions.getStartViewPage(), multiDisplayOptions.getEndViewPage() + 1);
if (debugLayout) {
System.out.println("decoding ------START " + originalStart + " END=" + originalEnd + " display.isGeneratingOtherPages=" + multiDisplayOptions.isIsGeneratingOtherPages());
System.out.println(multiDisplayOptions.getStartViewPage() + " " + multiDisplayOptions.getEndViewPage());
}
while (multiDisplayOptions.isIsGeneratingOtherPages()) {
// detect if restarted
if ((originalStart != multiDisplayOptions.getStartViewPage()) && (originalEnd != multiDisplayOptions.getEndViewPage())) {
if (debugLayout) {
System.out.println("Worker detected change to page range to " + multiDisplayOptions.getStartViewPage() + ' ' + multiDisplayOptions.getEndViewPage());
}
page = multiDisplayOptions.getStartViewPage();
originalEnd = multiDisplayOptions.getEndViewPage() + 1;
// can be zero in facing mode
if (page == 0) {
page++;
}
originalStart = page;
if (multiDisplayOptions.isTurnoverOn() && displayView == FACING) {
firstFacing = originalStart - 2;
lastFacing = firstFacing + 6;
if (firstFacing < 1) {
firstFacing = 1;
}
if (lastFacing > pageCount + 1) {
lastFacing = pageCount + 1;
}
facingCount = lastFacing - firstFacing;
}
resetPageCaches(originalStart, originalEnd);
}
// exit if finished
if (multiDisplayOptions.isTurnoverOn() && displayView == FACING && facingCount == 0) {
break;
}
if ((!multiDisplayOptions.isTurnoverOn() || displayView != FACING) && page == originalEnd) {
break;
}
if (originalStart > originalEnd) {
break;
}
if (page > 0 && page < pdf.getPageCount() + 1) {
decodeMorePages(page, originalStart, originalEnd);
}
//store thumbnail for turnover if in facing mode
if (displayView == FACING && multiDisplayOptions.isTurnoverOn()) {
int leftPage = multiDisplayOptions.getPageNumber();
if (multiDisplayOptions.isSeparateCover() && (leftPage & 1) == 1) {
leftPage--;
}
if (!multiDisplayOptions.isSeparateCover() && (leftPage & 1) == 0) {
leftPage--;
}
int ref = page - leftPage + 2;
if (!(ref > 1 && ref < 4)) {
if (ref > 1) {
ref -= 2;
}
final int[] pageW = multiDisplayOptions.getPageW();
final int[] pageH = multiDisplayOptions.getPageH();
// System.out.println("page + \" \" + ref = " + page + " " + ref);
if (ref < 4 && ref > -1 && facingDragCachedImages[ref] == null) {
final BufferedImage image = new BufferedImage(pageW[page], pageH[page], BufferedImage.TYPE_INT_ARGB);
final Graphics2D pg = (Graphics2D) image.getGraphics();
final int displayRotation = display.getDisplayRotation();
pg.rotate(displayRotation * Math.PI / 180);
try {
switch (displayRotation) {
case 90:
pg.translate(0, -pageW[page]);
pg.drawImage(pdf.getPageAsImage(page), 0, 0, pageH[page] + 1, pageW[page] + 1, null);
break;
case 180:
pg.translate(-pageW[page], -pageH[page]);
pg.drawImage(pdf.getPageAsImage(page), 0, 0, pageW[page] + 1, pageH[page] + 1, null);
break;
case 270:
pg.translate(-pageH[page], 0);
pg.drawImage(pdf.getPageAsImage(page), 0, 0, pageH[page] + 1, pageW[page] + 1, null);
break;
default:
pg.drawImage(pdf.getPageAsImage(page), 0, 0, pageW[page] + 1, pageH[page] + 1, null);
break;
}
} catch (final Exception e) {
LogWriter.writeLog("Exception: " + e.getMessage());
}
facingDragCachedImages[ref] = image;
}
}
}
facingCount--;
//change decode order if facing
page++;
if (multiDisplayOptions.isTurnoverOn() && displayView == FACING && page == lastFacing) {
page = firstFacing;
}
}
if (debugLayout) {
System.out.println("decodeOtherPageinins------ENDED");
}
}
public void decodeMorePages(final int page, final int originalStart, final int originalEnd) {
if (currentPageViews.get(page) == null) {
decodePage(page, originalStart, originalEnd);
}
repaint();
}
public void decodePage(final int page, final int originalStart, final int originalEnd) {
final AcroRenderer formRenderer = pdf.getFormRenderer();
//Create form objects
if (displayView == CONTINUOUS || displayView == CONTINUOUS_FACING) {
final PdfStreamDecoder current = (PdfStreamDecoder) currentPageViews.get(page);
//if(currentOffset!=null)
// formRenderer.getCompData().setPageValues(pdf.getScaling(), displayRotation,(int)getIndent(),0,0,pdf.getDisplayView(),currentOffset.widestPageNR,currentOffset.widestPageR);
formRenderer.createDisplayComponentsForPage(page, current);
if (pdf.getSpecialMode() != SpecialOptions.NONE &&
pdf.getSpecialMode() != SpecialOptions.SINGLE_PAGE &&
page != pdf.getPageCount()) {
formRenderer.createDisplayComponentsForPage(page + 1, current);
}
}
/* get pdf object id for page to decode */
final String currentPageOffset = pdf.getIO().getReferenceforPage(page);
if (debugLayout) {
System.out.println("Decoding page " + page + " currentPageOffset=" + currentPageOffset + " range=" + originalStart + ' ' + originalEnd);
}
/*
* decode the file if not already decoded and stored
*/
if (currentPageOffset != null || (formRenderer.isXFA() && formRenderer.useXFA())) {
final Integer key = page;
final Object currentView = currentPageViews.get(key);
if (currentView == null && multiDisplayOptions.isIsGeneratingOtherPages()) {
if (debugLayout) {
System.out.println("recreate page");
}
// force redraw
display.forceRedraw();
getPageView(currentPageOffset, page);
}
}
}
private void getPageView(final String currentPageOffset, final int pageNumber) {
final PdfObject pdfObject = new PageObject(currentPageOffset);
//ensure set (needed for XFA)
pdfObject.setPageNumber(pageNumber);
currentPdfFile.readObject(pdfObject);
final PdfObject Resources = pdfObject.getDictionary(PdfDictionary.Resources);
final DynamicVectorRenderer currentDisplay = getNewDisplay(pageNumber);
int val = 0;
if (pdf.getDisplayView() == Display.CONTINUOUS && pdf.getDisplayView() == Display.CONTINUOUS_FACING) {
val = 1;
}
final PdfStreamDecoder current = formRenderer.getStreamDecoder(currentPdfFile, fileAccess.getRes().getPdfLayerList(), true);
/*
* draw acroform data onto Panel
*/
if (formRenderer != null && pdf.isForm()) {
// formRenderer.getCompData().setPageValues(scaling, displayRotation,0,0,0,pdf.getDisplayView(),currentOffset.widestPageNR,currentOffset.widestPageR);
formRenderer.createDisplayComponentsForPage(pageNumber, current);
}
current.setParameters(true, true, 7, val, false, pdf.getExternalHandler().getMode().equals(GUIModes.JAVAFX));
current.setXMLExtraction(pdf.isXMLExtraction());
pdf.getExternalHandler().addHandlers(current);
current.setObjectValue(ValueTypes.Name, fileAccess.getFilename());
current.setObjectValue(ValueTypes.ObjectStore, pdf.getObjectStore());
current.setObjectValue(ValueTypes.PDFPageData, pageData);
current.setIntValue(ValueTypes.PageNum, pageNumber);
current.setRenderer(currentDisplay);
try {
currentDisplay.init(pageData.getMediaBoxWidth(pageNumber), pageData.getMediaBoxHeight(pageNumber), options.getPageColor());
currentDisplay.setValue(DynamicVectorRenderer.ALT_BACKGROUND_COLOR, options.getPageColor().getRGB());
if (options.getTextColor() != null) {
currentDisplay.setValue(DynamicVectorRenderer.ALT_FOREGROUND_COLOR, options.getTextColor().getRGB());
if (options.getChangeTextAndLine()) {
currentDisplay.setValue(DynamicVectorRenderer.FOREGROUND_INCLUDE_LINEART, 1);
} else {
currentDisplay.setValue(DynamicVectorRenderer.FOREGROUND_INCLUDE_LINEART, 0);
}
}
fileAccess.getRes().setupResources(current, false, Resources, pageNumber, currentPdfFile);
current.decodePageContent(pdfObject);
final TextLines textLines = pdf.getTextLines();
//All data loaded so now get all line areas for page
if (textLines != null) {
final Vector_Rectangle_Int vr = (Vector_Rectangle_Int) current.getObjectValue(ValueTypes.TextAreas);
vr.trim();
final int[][] pageTextAreas = vr.get();
final Vector_Int vi = (Vector_Int) current.getObjectValue(ValueTypes.TextDirections);
vi.trim();
final int[] pageTextDirections = vi.get();
for (int k = 0; k != pageTextAreas.length; k++) {
textLines.addToLineAreas(pageTextAreas[k], pageTextDirections[k], pageNumber);
}
}
} catch (final Exception ex) {
LogWriter.writeLog("Exception: " + ex.getMessage());
} catch (final Error err) {
LogWriter.writeLog("Exception: " + err.getMessage());
}
currentPageViews.put(pageNumber, currentDisplay);
display.setCurrentDisplay(currentDisplay);
}
/**
* copy pages between WeakMap and vice-versa
*/
void resetPageCaches(final int startPage, final int endPage) {
// copy any pages in existence into hashMap so not garbage collected
//synchronized (cachedPageViews) {
for (final Object o : this.cachedPageViews.keySet()) {
final Integer currentKey = (Integer) o;
final int keyValue = currentKey;
if ((keyValue >= startPage) && (keyValue <= endPage)) {
final DynamicVectorRenderer obj = cachedPageViews.get(currentKey);
if (obj != null) {
this.currentPageViews.put(currentKey, obj);
}
}
}
//}
// move any pages not visible into cache
{
//synchronized (currentPageViews) {
Iterator keys = this.currentPageViews.keySet().iterator();
final Map keysToTrash = new HashMap();
while (keys.hasNext()) {
final Integer currentKey = (Integer) keys.next();
final int keyValue = currentKey;
if ((keyValue < startPage) || (keyValue > endPage)) {
final DynamicVectorRenderer obj = currentPageViews.get(currentKey);
if (obj != null) {
this.cachedPageViews.put(currentKey, obj);
}
// track so we can delete outside loop
keysToTrash.put(currentKey, "x");
}
}
// now remove
keys = keysToTrash.keySet().iterator();
while (keys.hasNext()) {
final Integer currentKey = (Integer) keys.next();
final int keyValue = currentKey;
currentPageViews.remove(keyValue);
}
//}
}
//System.out.println("cache contains " + currentPageViews.keySet());
}
/**
* workout which pages are displayed
*/
private synchronized void calcDisplayedRange() {
final int pageCount = pageData.getPageCount();
if (debugLayout) {
System.out.println("calcDisplayedRange pageNumber=" + multiDisplayOptions.getPageNumber() + " mode=" + displayView);
}
if (displayView == SINGLE_PAGE) {
return;
}
display.getDisplayedRectangle();
if (displayView == FACING) {
multiDisplayOptions.calcDisplayRangeForFacing();
} else {
//// START SI'S PAGE COUNTER ////////////
final int newPage = updatePageDisplayed();
//System.out.println("newPage="+newPage);
fileAccess.setPageNumber(newPage);
//// END SI'S PAGE COUNTER ////////////
//update page number
if (newPage != -1 && gui != null)// && customSwingHandle!=null)
{
gui.setPage(newPage); // ( (org.jpedal.gui.GUIFactory) customSwingHandle).setPage(newPage);
}
}
//Ensure end page is not set beyond the total count of pages
if (multiDisplayOptions.getEndViewPage() > pageCount) {
multiDisplayOptions.setEndViewPage(pageCount);
}
if (displayView != FACING) {
display.refreshDisplay(); //refresh display to fix backbuffer background color issue
}
}
public void flushPageCaches() {
currentPageViews.clear();
cachedPageViews.clear();
}
public DynamicVectorRenderer getCurrentPageView(final int i) {
return currentPageViews.get(i);
}
public void dispose() {
this.cachedPageViews = null;
}
private int updatePageDisplayed() {
int newPage = -1;
final int pageCount = pageData.getPageCount();
final int[] yReached = multiDisplayOptions.getyReached();
final int[] pageH = multiDisplayOptions.getPageH();
final int ry = display.getRy();
final int rh = display.getRh();
//final int insetH=display.getInsetH();
final boolean debug = false;
int largestH = 0;
int firstVisiblePage = 0;
int lastVisiblePage = 0;
for (int i = 1; i <= pageCount; i += 1) {
final int pageTop = yReached[i];
final int pageBottom = yReached[i] + pageH[i];
final int viewBottom = ry + rh;
if (debug) {
System.out.println(display.getInsetH() + " " + i + ' ' + " pageTop=" + pageTop + " pageBottom=" + pageBottom + " viewTop=" + ry + " viewBottom=" + viewBottom);
}
if (pageTop <= viewBottom && pageBottom >= ry) {
//in view
if (debug) {
System.out.println("in view ");
}
} else {
continue;
}
//If not set yet, set to first page by default
if (newPage == -1) {
newPage = i;
firstVisiblePage = i;
}
lastVisiblePage = i;
final int midPt = ry + (rh / 2);
int gap = midPt - yReached[i];
if (debug) {
System.out.println("gap=" + gap);
}
if (gap < 0 || gap > pageH[i]) {
gap = 0;
}
int gap2 = yReached[i] + pageH[i] - midPt;
if (debug) {
System.out.println("gap2=" + gap2);
}
if (gap2 < 0 || gap2 > pageH[i]) {
gap2 = 0;
}
if (gap2 > gap) {
gap = gap2;
}
if (gap > 0 && gap > largestH) {
largestH = gap;
newPage = i;
if (debug) {
System.out.println(i + " gap now=" + largestH);
}
//break; //Stop if found first whole page
}
if (debug) {
System.out.println(i + " reached " + yReached[i] + ' ' + ry);
}
}
//If still not set set page 1 as a default
if (newPage == -1) {
newPage = 1;
firstVisiblePage = 1;
lastVisiblePage = 1;
}
multiDisplayOptions.setStartViewPage(firstVisiblePage);
//allow for 2 page doc with both pages onscreen
multiDisplayOptions.setEndViewPage(lastVisiblePage);
if (debugLayout) {
System.out.println("page range start=" + multiDisplayOptions.getStartViewPage() + " end=" + multiDisplayOptions.getEndViewPage());
}
//Ensure end page is not set beyond the total count of pages
if (multiDisplayOptions.getEndViewPage() > pageCount) {
multiDisplayOptions.setEndViewPage(pageCount);
}
return newPage;
}
public void resetCachedFacingImages() {
for (int i = 0; i < 4; i++) {
facingDragCachedImages[i] = null;
}
}
public BufferedImage[] getFacingDragImages() {
return facingDragCachedImages;
}
public BufferedImage getfacingDragTempLeftImg() {
return facingDragTempLeftImg;
}
public BufferedImage getfacingDragTempRightImg() {
return facingDragTempRightImg;
}
public int getFacingDragTempLeftNo() {
return facingDragTempLeftNo;
}
public int getFacingDragTempRightNo() {
return facingDragTempRightNo;
}
public void setCustomRenderChangeListener(final RenderChangeListener customRenderChangeListener) {
this.customRenderChangeListener = customRenderChangeListener;
}
public void repaint() {
throw new UnsupportedOperationException("repaint Not supported yet.");
}
public DynamicVectorRenderer getNewDisplay(final int pageNumber) {
throw new UnsupportedOperationException(this + "Not supported yet.");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy