summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEthan Vrhel <ethanvrhel@gmail.com>2020-08-20 14:36:48 -0700
committerevrhel <ethanvrhel@gmail.com>2020-09-24 15:23:44 -0700
commiteda31b70496337caefd0dc912c5a0850de769147 (patch)
tree6a10dec0787d35e3b7f3af954b54c108468ff0ec
parent5b08d9eadfe64d87cf26869c47a2a5f78755dd4b (diff)
downloadghostpdl-eda31b70496337caefd0dc912c5a0850de769147.tar.gz
Updated how zooming is handled
Zooming is now done through the SmartLoader and is no longer a separate operation. Fixed an issue when the SmartLoader could become out of date due to the condition variable being signaled before the SmartLoader had finished.
-rw-r--r--demos/java/gsviewer/src/com/artifex/gsviewer/DefaultUnhandledExceptionHandler.java36
-rw-r--r--demos/java/gsviewer/src/com/artifex/gsviewer/Document.java104
-rw-r--r--demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java111
-rw-r--r--demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerWindow.java8
4 files changed, 137 insertions, 122 deletions
diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/DefaultUnhandledExceptionHandler.java b/demos/java/gsviewer/src/com/artifex/gsviewer/DefaultUnhandledExceptionHandler.java
index 9e31a2e03..6ae6389fe 100644
--- a/demos/java/gsviewer/src/com/artifex/gsviewer/DefaultUnhandledExceptionHandler.java
+++ b/demos/java/gsviewer/src/com/artifex/gsviewer/DefaultUnhandledExceptionHandler.java
@@ -4,6 +4,10 @@ import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Enumeration;
+import java.util.Properties;
public class DefaultUnhandledExceptionHandler implements Thread.UncaughtExceptionHandler {
@@ -22,7 +26,38 @@ public class DefaultUnhandledExceptionHandler implements Thread.UncaughtExceptio
public void uncaughtException(Thread t, Throwable e) {
StringBuilder builder = new StringBuilder();
+ DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
builder.append("Unhandled exception!\n");
+
+ //builder.append("USER\n");
+ builder.append("Time: " + dtf.format(LocalDateTime.now()) + "\n\n");
+
+ builder.append("PROPERTIES\n");
+ Properties props = System.getProperties();
+ for (Enumeration<Object> keys = props.keys(); keys.hasMoreElements();) {
+ String key = keys.nextElement().toString();
+ String prop = props.getProperty(key);
+ String friendly = "\"";
+ for (int i = 0; i < prop.length(); i++) {
+ char c = prop.charAt(i);
+ switch (c) {
+ case '\n':
+ friendly += "\\n";
+ break;
+ case '\r':
+ friendly += "\\r";
+ break;
+ default:
+ friendly += c;
+ break;
+ }
+ }
+ friendly += "\"";
+ builder.append(key + " = " + friendly + "\n");
+ }
+ builder.append('\n');
+
+ builder.append("EXCEPTION\n");
builder.append("Exception: " + e.getClass().getName() + "\n");
builder.append("Message: " + e.getMessage() + "\n");
builder.append("Cause: " + (e.getCause() == null ? "[Unknown]" :
@@ -30,6 +65,7 @@ public class DefaultUnhandledExceptionHandler implements Thread.UncaughtExceptio
builder.append("Thread: " + t.getName() + " (id=" + t.getId() + ")\n");
builder.append("Stack trace:" + "\n");
builder.append(stackTraceToString(e));
+
String fullMessage = builder.toString();
System.err.println(fullMessage);
try {
diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java b/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java
index 5e2c4af91..4b07c5b8d 100644
--- a/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java
+++ b/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java
@@ -231,11 +231,6 @@ public class Document implements List<Page> {
if (code != GS_ERROR_OK)
throw new IllegalStateException("Failed to distill document (code = " + code + ")");
-
- /*Reference<Integer> pExitCode = new Reference<>();
- int code = gsInstance.run_file(out.getAbsolutePath(), 0, pExitCode);
- if (code != GS_ERROR_OK)
- throw new IllegalStateException("failed to run file (code = " + code + ")");*/
} finally {
deleteGSInstance();
}
@@ -682,77 +677,6 @@ public class Document implements List<Page> {
}
/**
- * Loads the high resolution images of a list of pages.
- *
- * @param operationMode How the loader should behave if an operation is already in
- * progress. Can be <code>OPERATION_WAIT</code>, meaning the loader should
- * wait until a running operation has finished, <code>OPERATION_RETURN</code> meaning
- * the loader should immediately return, or <code>OPERATION_THROW</code> meaning the loader
- * should throw an <code>OperationInProgressException</code>.
- * @param pages The pages to load.
- *
- * @throws IndexOutOfBoundsException When any page is not a page in the document.
- * @throws IllegalStateException When Ghostscript fails to initialize or load the document.
- * @throws OperationInProgressException When an operation is already in progress
- * and <code>operationMode</code> is <code>OPERATION_THROW</code>.
- * @throws IllegalArgumentException When <code>operationMode</code> is not
- * <code>OPERATION_WAIT</coded>, <code>OPERATION_RETURN</code>, or <code>OPERATION_THROW</code>.
- */
- public void loadHighResList(int operationMode, final int[] pages)
- throws IndexOutOfBoundsException, IllegalStateException, OperationInProgressException {
- throw new UnsupportedOperationException("loadHighResList");
- /*if (pages.length > 0) {
- if (!handleOperationMode(operationMode))
- return;
-
- try {
- startOperation();
-
- final StringBuilder builder = new StringBuilder();
- if (pages[0] < 1 || pages[0] > this.pages.size())
- throw new IndexOutOfBoundsException("page=" + pages[0]);
- builder.append(pages[0]);
-
- for (int i = 1; i < pages.length; i++) {
- if (pages[i] < 1 || pages[i] > this.pages.size())
- throw new IndexOutOfBoundsException("page=" + pages[i]);
- builder.append(',').append(pages[i]);
- }
-
-
- final String[] gargs = { "gs", "-dNOPAUSE", "-dSAFER", "-I%rom%Resource%/Init/", "-dBATCH", "-r" + Page.PAGE_HIGH_DPI,
- "-sDEVICE=display", "-sPageList=" + builder.toString(), "-dDisplayFormat=" + format,
- "-dTextAlphaBits=4", "-dGraphicsAlphaBits=4",
- "-f", file.getAbsolutePath() };
-
- GSInstance instance = null;
- try {
- instance = initDocInstance();
- } catch (IllegalStateException e) {
- operationDone();
- }
-
- if (instance == null)
- throw new IllegalStateException("Failed to initialize Ghoscript");
-
- int code = instance.init_with_args(gargs);
- instance.exit();
- instance.delete_instance();
- if (code != GS_ERROR_OK) {
- throw new IllegalStateException("Failed to gsapi_init_with_args code=" + code);
- }
-
- int ind = 0;
- for (final BufferedImage img : documentLoader.images) {
- this.pages.get(pages[ind] - 1).setHighRes(img);
- }
- } finally {
- operationDone();
- }
- }*/
- }
-
- /**
* Unloads the high resolution images in a range of pages.
*
* @param startPage The start page to unload the high resolution image from.
@@ -885,6 +809,34 @@ public class Document implements List<Page> {
}
}
+ public void zoomPage(final int operationMode, final int page, final double zoom)
+ throws IndexOutOfBoundsException {
+ checkBounds(page, page);
+
+ if (!handleOperationMode(operationMode))
+ return;
+
+ try {
+ startOperation();
+
+ setParams((int)(Page.PAGE_HIGH_DPI * zoom), page, page);
+
+ Reference<Integer> exitCode = new Reference<>();
+ int code = gsInstance.run_file(file.getAbsolutePath(), 0, exitCode);
+
+ if (code != GS_ERROR_OK)
+ throw new IllegalStateException("Failed to run file (code = " + code + ")");
+
+ int ind = page - 1;
+ for (final BufferedImage img : documentLoader.images) {
+ this.pages.get(ind++).setZoomed(img);
+ }
+
+ } finally {
+ operationDone();
+ }
+ }
+
/**
* Checks to make sure the pages <code>start</code> through
* <code>end</code> are in the document, and throws an
diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java b/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java
index 39b3aae7f..f062ab949 100644
--- a/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java
+++ b/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java
@@ -24,6 +24,7 @@ public class ViewerController implements ViewerGUIListener {
private static final Lock lock = new ReentrantLock();
private static final Condition cv = lock.newCondition();
+ private static volatile boolean outOfDate = true;
private ViewerWindow source;
private Document currentDocument;
@@ -38,10 +39,14 @@ public class ViewerController implements ViewerGUIListener {
try {
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
+ chooser.setFileFilter(PDFFileFilter.INSTANCE);
ret = chooser.showSaveDialog(source);
if (ret != JFileChooser.APPROVE_OPTION)
return;
File out = chooser.getSelectedFile();
+ String filepath = out.getAbsolutePath();
+ if (filepath.lastIndexOf('.') == -1)
+ out = new File(filepath + ".pdf");
if (out.exists()) {
ret = source.showConfirmDialog("Overwrite?", out.getName() + " already exists. Overwrite?");
if (ret != ViewerWindow.YES)
@@ -98,46 +103,21 @@ public class ViewerController implements ViewerGUIListener {
@Override
public void onPageChange(int oldPage, int newPage) {
- lock.lock();
- try {
- cv.signalAll();
- } catch (IllegalMonitorStateException e) {
- System.err.println("Exception on signaling: " + e);
- } finally {
- lock.unlock();
- }
+ smartLoader.signalOutOfDate();
}
@Override
public void onZoomChange(double oldZoom, double newZoom) {
if (newZoom > 1.0) {
- int currentPage = source.getCurrentPage();
- Runnable r = () -> {
- //source.showWarningDialog("Error", "An operation is already in progress");
- //return;
-
- try {
- currentDocument.zoomArea(Document.OPERATION_THROW, currentPage, newZoom);
- } catch (Document.OperationInProgressException e) {
- source.showWarningDialog("Error", "An operation is already in progress");
- }
- };
- Thread t = new Thread(r);
- t.setName("Zoom-Thread");
- t.start();
+ smartLoader.resetZoom();
+ smartLoader.signalOutOfDate();
}
}
@Override
public void onScrollChange(int newScroll) {
- lock.lock();
- try {
- cv.signalAll();
- } catch (IllegalMonitorStateException e) {
- System.err.println("Exception on signaling: " + e);
- } finally {
- lock.unlock();
- }
+ if (smartLoader != null)
+ smartLoader.signalOutOfDate();
}
@Override
@@ -184,7 +164,6 @@ public class ViewerController implements ViewerGUIListener {
source.showErrorDialog("Unhandled Exception", errorMessage);
}
DefaultUnhandledExceptionHandler.INSTANCE.uncaughtException(t, e);
-
}
}
@@ -192,15 +171,17 @@ public class ViewerController implements ViewerGUIListener {
private class SmartLoader implements Runnable {
private volatile boolean[] loaded;
+ private volatile boolean[] zoomLoaded;
private volatile boolean shouldRun;
private Thread thread;
- private SmartLoader(Document doc) {
+ SmartLoader(Document doc) {
loaded = new boolean[doc.size()];
+ zoomLoaded = new boolean[doc.size()];
shouldRun = true;
}
- private void start() {
+ void start() {
if (thread != null)
stop();
shouldRun = true;
@@ -210,11 +191,12 @@ public class ViewerController implements ViewerGUIListener {
thread.start();
}
- private void stop() {
- shouldRun = false;
+ void stop() {
lock.lock();
cv.signalAll();
lock.unlock();
+ outOfDate = false;
+ shouldRun = false;
try {
thread.join();
} catch (InterruptedException e) {
@@ -223,6 +205,24 @@ public class ViewerController implements ViewerGUIListener {
thread = null;
}
+ void resetZoom() {
+ for (int i = 0; i < zoomLoaded.length; i++) {
+ zoomLoaded[i] = false;
+ }
+ }
+
+ void signalOutOfDate() {
+ outOfDate = true;
+ lock.lock();
+ try {
+ cv.signalAll();
+ } catch (IllegalMonitorStateException e) {
+ System.err.println("Exception on signaling: " + e);
+ } finally {
+ lock.unlock();
+ }
+ }
+
@Override
public void run() {
System.out.println("Smart loader dispatched.");
@@ -238,9 +238,23 @@ public class ViewerController implements ViewerGUIListener {
int ind = 0;
for (int page : toLoad) {
if (page >= 1 && page <= currentDocument.size()) {
- if (!loaded[page - 1]) {
- currentDocument.loadHighRes(Document.OPERATION_WAIT, page);
- loaded[page - 1] = true;
+ if (source.getZoom() > 1.0) {
+
+ // Load the zoomed page view only if it has not already been loaded.
+ if (!zoomLoaded[page - 1]) {
+ currentDocument.zoomArea(Document.OPERATION_WAIT, page, source.getZoom());
+ zoomLoaded[page - 1] = true;
+ }
+ } else {
+
+ // Unload any zoomed image to save memory consumption
+ currentDocument.unloadZoomed(page);
+
+ // Load the high-resolution page view only if it has not already been loaded
+ if (!loaded[page - 1]) {
+ currentDocument.loadHighRes(Document.OPERATION_WAIT, page);
+ loaded[page - 1] = true;
+ }
}
}
ind++;
@@ -248,13 +262,20 @@ public class ViewerController implements ViewerGUIListener {
}
source.setLoadProgress(0);
- lock.lock();
- try {
- cv.await();
- } catch (InterruptedException e) {
- System.err.println("Interrupted in smart loader: " + e);
- } finally {
- lock.unlock();
+ // First check if the current view is out of date and if so, just immediately continue
+ // so the thread does not get stuck in the lock until another event has occurred.
+ if (!outOfDate) {
+ outOfDate = false;
+ lock.lock();
+ try {
+ cv.await();
+ } catch (InterruptedException e) {
+ System.err.println("Interrupted in smart loader: " + e);
+ } finally {
+ lock.unlock();
+ }
+ } else {
+ outOfDate = false;
}
}
}
diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerWindow.java b/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerWindow.java
index 0b701ddc3..9ada11a62 100644
--- a/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerWindow.java
+++ b/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerWindow.java
@@ -496,8 +496,10 @@ public class ViewerWindow extends javax.swing.JFrame {
img = page.getZoomedImage();
Image result = img;
- if (img == page.getLowResImage() || currentZoom < 1.0)
+ if (img == page.getLowResImage() || currentZoom < 1.0 ||
+ (img == page.getHighResImage() && currentZoom > 1.0 && page.getZoomedImage() == null)) {
result = img.getScaledInstance(actualSize.width, actualSize.height, Image.SCALE_FAST);
+ }
return result;
} else {
return null;
@@ -790,6 +792,10 @@ public class ViewerWindow extends javax.swing.JFrame {
return JOptionPane.showConfirmDialog(this, message, title, JOptionPane.YES_NO_CANCEL_OPTION);
}
+ public double getZoom() {
+ return currentZoom;
+ }
+
/**
* Refresh each button's state.
*/