summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorevrhel <ethanvrhel@gmail.com>2020-07-23 18:57:52 -0700
committerevrhel <ethanvrhel@gmail.com>2020-09-24 15:23:37 -0700
commitf1441ed36302f75d94cf4aa4a8047150bf5b7585 (patch)
tree740520b5368000f5e828aeb8177b87e296879a75
parentea3eecbc30b1a4a4747dfd2818262c2f6eea112c (diff)
downloadghostpdl-f1441ed36302f75d94cf4aa4a8047150bf5b7585.tar.gz
Basic viewer working
The viewer has basic capabilities, but still lacks loading high resolution images, spacing components, and resizing correctly.
-rw-r--r--demos/java/gsviewer/src/com/artifex/gsviewer/Document.java196
-rw-r--r--demos/java/gsviewer/src/com/artifex/gsviewer/GSFileFilter.java12
-rw-r--r--demos/java/gsviewer/src/com/artifex/gsviewer/Main.java30
-rw-r--r--demos/java/gsviewer/src/com/artifex/gsviewer/Page.java46
-rw-r--r--demos/java/gsviewer/src/com/artifex/gsviewer/StdIO.java7
-rw-r--r--demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java93
-rw-r--r--demos/java/gsviewer/src/com/artifex/gsviewer/ViewerGUIListenerImpl.java67
-rw-r--r--demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerGUIListener.java16
-rw-r--r--demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerWindow.java167
9 files changed, 504 insertions, 130 deletions
diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java b/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java
index 2d5035e19..7b6f2c041 100644
--- a/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java
+++ b/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java
@@ -17,20 +17,34 @@ import com.artifex.gsjava.util.BytePointer;
import com.artifex.gsjava.util.LongReference;
import com.artifex.gsviewer.ImageUtil.ImageParams;
+/**
+ * A Document stores an ordered list of Pages.
+ *
+ * @author Ethan Vrhel
+ *
+ */
public class Document implements List<Page> {
private static final Object slock = new Object();
private static final DocumentLoader documentLoader = new DocumentLoader();
private static final int format = GS_COLORS_RGB | GS_DISPLAY_DEPTH_8 | GS_DISPLAY_BIGENDIAN;
- public static void loadDocumentAsync(final File file, final DocAsyncLoadCallback cb)
+ /**
+ * Loads a document on a separate thread.
+ *
+ * @param file The file to load.
+ * @param cb The callback when loading the document has finished.
+ * @param dlb The callback signifying when progress has occurred while loading.
+ * @throws NullPointerException When <code>cb</code> is <code>null</code>.
+ */
+ public static void loadDocumentAsync(final File file, final DocAsyncLoadCallback cb, final DocLoadProgressCallback dlb)
throws NullPointerException {
Objects.requireNonNull(cb, "DocAsyncLoadCallback");
final Thread t = new Thread(() -> {
Document doc = null;
Exception exception = null;
try {
- doc = new Document(file);
+ doc = new Document(file, dlb);
} catch (FileNotFoundException | IllegalStateException | NullPointerException e) {
exception = e;
}
@@ -41,17 +55,69 @@ public class Document implements List<Page> {
t.start();
}
+ /**
+ * Loads a document on a separate thread.
+ *
+ * @param file The file to load.
+ * @param cb The callback when loading the document has finished.
+ * @throws NullPointerException When <code>cb</code> is <code>null</code>.
+ */
+ public static void loadDocumentAsync(final File file, final DocAsyncLoadCallback cb)
+ throws NullPointerException {
+ loadDocumentAsync(file, cb, null);
+ }
+
+ /**
+ * Loads a document on a separate thread.
+ *
+ * @param filename The name of the file to load.
+ * @param cb The callback when loading the document has finished.
+ * @param dlb The callback signifying when progress has occurred while loading.
+ * @throws NullPointerException When <code>cb</code> is <code>null</code>.
+ */
+ public static void loadDocumentAsync(final String filename, final DocAsyncLoadCallback cb, final DocLoadProgressCallback dlb)
+ throws NullPointerException {
+ loadDocumentAsync(new File(filename), cb, dlb);
+ }
+
+ /**
+ * Loads a document on a separate thread.
+ *
+ * @param filename The name of the file to load.
+ * @param cb The callback when loading the document has finished.
+ * @throws NullPointerException When <code>cb</code> is <code>null</code>.
+ */
public static void loadDocumentAsync(final String filename, final DocAsyncLoadCallback cb)
throws NullPointerException {
- loadDocumentAsync(new File(filename), cb);
+ loadDocumentAsync(filename, cb, null);
}
@FunctionalInterface
public static interface DocAsyncLoadCallback {
+ /**
+ * Called when a document has finished loading on a separate
+ * thread.
+ *
+ * @param doc The document which was loaded. <code>null</code> if the document
+ * failed to load.
+ * @param exception An exception that ocurred while loading, <code>null</code>
+ * if no exception was thrown.
+ */
public void onDocumentLoad(Document doc, Exception exception);
}
+ @FunctionalInterface
+ public static interface DocLoadProgressCallback {
+
+ /**
+ * Called when the document has made progress while loading.
+ *
+ * @param progress The amount of progress (from 0 to 100).
+ */
+ public void onDocumentProgress(int progress);
+ }
+
private static void initDocInstance(LongReference instanceRef) throws IllegalStateException {
int code = gsapi_new_instance(instanceRef, GS_NULL);
if (code != GS_ERROR_OK) {
@@ -76,6 +142,8 @@ public class Document implements List<Page> {
private int pageWidth, pageHeight, pageRaster;
private BytePointer pimage;
private List<BufferedImage> images;
+ private int progress;
+ private DocLoadProgressCallback callback;
private DocumentLoader() {
reset();
@@ -87,6 +155,8 @@ public class Document implements List<Page> {
this.pageRaster = 0;
this.pimage = null;
this.images = new LinkedList<>();
+ this.progress = 0;
+ this.callback = null;
return this;
}
@@ -104,6 +174,11 @@ public class Document implements List<Page> {
public int onDisplayPage(long handle, long device, int copies, boolean flush) {
byte[] data = (byte[]) pimage.toArrayNoConvert();
images.add(ImageUtil.createImage(data, new ImageParams(pageWidth, pageHeight, pageRaster, BufferedImage.TYPE_3BYTE_BGR)));
+
+ progress += (100 - progress) / 2;
+ if (callback != null)
+ callback.onDocumentProgress(progress);
+
return 0;
}
}
@@ -111,7 +186,16 @@ public class Document implements List<Page> {
private File file;
private List<Page> pages;
- public Document(final File file)
+ /**
+ * Creates and loads a new document.
+ *
+ * @param file The file to load.
+ * @param loadCallback The callback to indicate when progress has occurred while loading.
+ * @throws FileNotFoundException When <code>file</code> does not exist.
+ * @throws IllegalStateException When Ghostscript fails to intialize or load the document.
+ * @throws NullPointerException When <code>file</code> is <code>null</code>.
+ */
+ public Document(final File file, final DocLoadProgressCallback loadCallback)
throws FileNotFoundException, IllegalStateException, NullPointerException {
this.file = Objects.requireNonNull(file, "file");
if (!file.exists())
@@ -124,6 +208,8 @@ public class Document implements List<Page> {
LongReference instanceRef = new LongReference();
initDocInstance(instanceRef);
+ documentLoader.callback = loadCallback;
+
int code = gsapi_init_with_args(instanceRef.value, gargs);
gsapi_exit(instanceRef.value);
gsapi_delete_instance(instanceRef.value);
@@ -135,13 +221,58 @@ public class Document implements List<Page> {
for (BufferedImage img : documentLoader.images) {
pages.add(new Page(img));
}
- }
- }
- public Document(final String filename) throws FileNotFoundException, NullPointerException {
- this(new File(Objects.requireNonNull(filename, "filename")));
+ if (documentLoader.callback != null)
+ documentLoader.callback.onDocumentProgress(100);
+ }
}
+ /**
+ * Creates and loads a document from a filename.
+ *
+ * @param file The file to load.
+ * @throws FileNotFoundException If the given file does not exist.
+ * @throws NullPointerException If <code>file</code> is <code>null</code>.
+ */
+ public Document(final File file)
+ throws FileNotFoundException, NullPointerException {
+ this(file, null);
+ }
+
+ /**
+ * Creates and loads a document from a filename.
+ *
+ * @param filename The name of the file to load.
+ * @param loadCallback The callback to indicate when progress has occurred while loading.
+ * @throws FileNotFoundException If the given filename does not exist.
+ * @throws NullPointerException If <code>filename</code> is <code>null</code>.
+ */
+ public Document(final String filename, final DocLoadProgressCallback loadCallback)
+ throws FileNotFoundException, NullPointerException {
+ this(new File(Objects.requireNonNull(filename, "filename")), loadCallback);
+ }
+
+ /**
+ * Creates and loads a document from a filename.
+ *
+ * @param filename The name of the file to load.
+ * @throws FileNotFoundException If the given filename does not exist.
+ * @throws NullPointerException If <code>filename</code> is <code>null</code>.
+ */
+ public Document(final String filename)
+ throws FileNotFoundException, NullPointerException {
+ this(filename, null);
+ }
+
+ /**
+ * Loads the high resolution images in a range of images.
+ *
+ * @param startPage The first page to load.
+ * @param endPage The end page to load.
+ * @throws IndexOutOfBoundsException When <code>firstPage</code> or <code>endPage</code>
+ * are not in the document or <code>endPage</code> is less than <code>firstPage</code>.
+ * @throws IllegalStateException When Ghostscript fails to initialize or load the document.
+ */
public void loadHighRes(int startPage, int endPage) throws IndexOutOfBoundsException, IllegalStateException {
checkBounds(startPage, endPage);
final String[] gargs = { "gs", "-dNOPAUSE", "-dSAFER", "-I%rom%Resource%/Init/", "-dBATCH", "-r" + Page.PAGE_HIGH_DPI,
@@ -166,10 +297,26 @@ public class Document implements List<Page> {
}
}
+ /**
+ * Loads the high resolution image of a singular page.
+ *
+ * @param page The page to load.
+ * @throws IndexOutOfBoundsException When <code>page</code> is not in the document.
+ * @throws IllegalStateException When Ghostscript fails to initialize or load the
+ * images.
+ */
public void loadHighRes(int page) throws IndexOutOfBoundsException, IllegalStateException {
loadHighRes(page, page);
}
+ /**
+ * Loads the high resolution images of a list of pages.
+ *
+ * @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
+ * images.
+ */
public void loadHighResList(final int... pages) throws IndexOutOfBoundsException, IllegalStateException {
if (pages.length > 0) {
final StringBuilder builder = new StringBuilder();
@@ -208,6 +355,14 @@ public class Document implements List<Page> {
}
}
+ /**
+ * Unloads the high resolution images in a range of pages.
+ *
+ * @param startPage The start page to unload the high resolution image from.
+ * @param endPage The end page to unload the high resolution image from.
+ * @throws IndexOutOfBoundsException When <code>startPage</code> and <code>endPage</code>
+ * are outside document or <code>endPage</code> is less than <code>startPage</code>.
+ */
public void unloadHighRes(int startPage, int endPage) throws IndexOutOfBoundsException {
checkBounds(startPage, endPage);
for (int i = startPage - 1; i < endPage; i++) {
@@ -215,12 +370,30 @@ public class Document implements List<Page> {
}
}
+ /**
+ * Unloads a high resolution image at the given page.
+ *
+ * @param page The page to unload the high resolution image.
+ * @throws IndexOutOfBoundsException When page is not a page in the document.
+ */
public void unloadHighRes(int page) throws IndexOutOfBoundsException {
unloadHighRes(page, page);
}
+ /**
+ * Unloads the document's resources. The document will be unusable after this
+ * call.
+ */
+ public void unload() {
+ for (Page p : pages) {
+ p.unloadLowRes();
+ p.unloadHighRes();
+ }
+ pages.clear();
+ }
+
private void checkBounds(int start, int end) throws IndexOutOfBoundsException {
- if (start < 1 || start >= pages.size())
+ if (start < 1 || start > pages.size())
throw new IndexOutOfBoundsException("start=" + start);
if (end < 1 || end > pages.size())
throw new IndexOutOfBoundsException("end=" + end);
@@ -362,4 +535,9 @@ public class Document implements List<Page> {
public int hashCode() {
return pages.hashCode();
}
+
+ @Override
+ public void finalize() {
+ unload();
+ }
} \ No newline at end of file
diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/GSFileFilter.java b/demos/java/gsviewer/src/com/artifex/gsviewer/GSFileFilter.java
index 6136e973b..acdab56ac 100644
--- a/demos/java/gsviewer/src/com/artifex/gsviewer/GSFileFilter.java
+++ b/demos/java/gsviewer/src/com/artifex/gsviewer/GSFileFilter.java
@@ -16,12 +16,12 @@ public class GSFileFilter extends FileFilter {
final int ind = name.lastIndexOf('.');
final String ext = name.substring(ind);
return
- equals(ext, "pdf") ||
- equals(ext, "eps") ||
- equals(ext, "ps") ||
- equals(ext, "xps") ||
- equals(ext, "oxps") ||
- equals(ext, "bin");
+ equals(ext, ".pdf") ||
+ equals(ext, ".eps") ||
+ equals(ext, ".ps") ||
+ equals(ext, ".xps") ||
+ equals(ext, ".oxps") ||
+ equals(ext, ".bin");
}
@Override
diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/Main.java b/demos/java/gsviewer/src/com/artifex/gsviewer/Main.java
index b1c3014e9..e8c63b7af 100644
--- a/demos/java/gsviewer/src/com/artifex/gsviewer/Main.java
+++ b/demos/java/gsviewer/src/com/artifex/gsviewer/Main.java
@@ -1,10 +1,5 @@
package com.artifex.gsviewer;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-import javax.imageio.ImageIO;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
@@ -13,34 +8,17 @@ import com.artifex.gsviewer.gui.ViewerWindow;
public class Main {
- public static void main(String[] args) throws FileNotFoundException {
+ public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
- | UnsupportedLookAndFeelException e1) {
- e1.printStackTrace();
+ | UnsupportedLookAndFeelException e) {
+ System.err.println("Failed to set Look and Feel: " + e);
}
- ViewerWindow win = new ViewerWindow(new ViewerGUIListenerImpl());
+ ViewerWindow win = new ViewerWindow(new ViewerController());
SwingUtilities.invokeLater(() -> {
win.setVisible(true);
});
-
-
- //Document doc = new Document("010104_momentum_concepts.pdf");
- //doc.loadHighRes(1, 3);
- //doc.unloadHighRes(2);
- //doc.loadHighResList(1, 2, 3);
-
- //int pageNum = 1;
- //for (Page page : doc) {
- // try {
- // ImageIO.write(page.getDisplayableImage(), "PNG", new File("page-" + pageNum + ".png"));
- // } catch (IOException e) {
- // System.err.println("Failed to write page " + pageNum);
- // }
- // pageNum++;
- //}
-
}
}
diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java b/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java
index 3bb0be37e..63b716cad 100644
--- a/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java
+++ b/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java
@@ -1,11 +1,26 @@
package com.artifex.gsviewer;
+import java.awt.Dimension;
import java.awt.image.BufferedImage;
import com.artifex.gsviewer.ImageUtil.ImageParams;
+/**
+ * A Page represents an individual page within a Document. It stores a high resolution image
+ * and a low resolution preview image.
+ *
+ * @author Ethan Vrhel
+ *
+ */
public class Page {
+ /**
+ * The high-resolution DPI to use.
+ */
public static final int PAGE_HIGH_DPI = 72;
+
+ /**
+ * The low-resolution DPI to use.
+ */
public static final int PAGE_LOW_DPI = 10;
private BufferedImage lowRes;
@@ -56,6 +71,11 @@ public class Page {
}
}
+ public void unloadAll() {
+ unloadLowRes();
+ unloadHighRes();
+ }
+
public BufferedImage getLowResImage() {
return lowRes;
}
@@ -68,6 +88,29 @@ public class Page {
return highRes == null ? lowRes : highRes;
}
+ public Dimension getLowResSize() {
+ return lowRes == null ? null : new Dimension(lowRes.getWidth(), lowRes.getHeight());
+ }
+
+ public Dimension getHighResSize() {
+ return highRes == null ? null : new Dimension(highRes.getWidth(), highRes.getHeight());
+ }
+
+ public Dimension getDisplayableSize() {
+ return highRes == null ? getLowResSize() : getHighResSize();
+ }
+
+ public Dimension getSize() {
+ Dimension size = getHighResSize();
+ if (size != null)
+ return size;
+ size = getLowResSize();
+ if (size == null)
+ return new Dimension(0, 0);
+ return new Dimension(size.width * PAGE_HIGH_DPI / PAGE_LOW_DPI,
+ size.height * PAGE_HIGH_DPI / PAGE_LOW_DPI);
+ }
+
@Override
public String toString() {
return "Page[lowResLoaded=" + (lowRes != null) + ",highResLoaded=" + (highRes != null) + "]";
@@ -93,7 +136,6 @@ public class Page {
@Override
public void finalize() {
- unloadLowRes();
- unloadHighRes();
+ unloadAll();
}
} \ No newline at end of file
diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/StdIO.java b/demos/java/gsviewer/src/com/artifex/gsviewer/StdIO.java
index 68912954e..4a3951074 100644
--- a/demos/java/gsviewer/src/com/artifex/gsviewer/StdIO.java
+++ b/demos/java/gsviewer/src/com/artifex/gsviewer/StdIO.java
@@ -4,6 +4,13 @@ import com.artifex.gsjava.callbacks.IStdErrFunction;
import com.artifex.gsjava.callbacks.IStdInFunction;
import com.artifex.gsjava.callbacks.IStdOutFunction;
+/**
+ * Implementation of <code>IStdOutFunction</code>, <code>IStdErrFunction</code>, and
+ * <code>IStdInFunction</code> to get IO data from Ghostscript.
+ *
+ * @author Ethan Vrhel
+ *
+ */
public class StdIO implements IStdOutFunction, IStdErrFunction, IStdInFunction {
@Override
diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java b/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java
new file mode 100644
index 000000000..6353d5849
--- /dev/null
+++ b/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerController.java
@@ -0,0 +1,93 @@
+package com.artifex.gsviewer;
+
+import java.awt.Point;
+import java.io.File;
+
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+
+import com.artifex.gsviewer.gui.ViewerGUIListener;
+import com.artifex.gsviewer.gui.ViewerWindow;
+
+public class ViewerController implements ViewerGUIListener {
+
+ private ViewerWindow source;
+ private Document currentDocument;
+
+ public void open(final File file) {
+ if (currentDocument != null)
+ close();
+ Document.loadDocumentAsync(file, (final Document doc, final Exception exception) -> {
+ source.setLoadProgress(0);
+ if (exception != null) {
+ JOptionPane.showMessageDialog(source, exception.toString(),
+ "Failed to load", JOptionPane.ERROR_MESSAGE);
+ } else {
+ System.out.println("Loaded document");
+ this.currentDocument = doc;
+ source.loadDocumentToViewer(doc);
+ }
+ }, (int progress) -> {
+ source.setLoadProgress(progress);
+ });
+ }
+
+ public void close() {
+ if (currentDocument != null) {
+ source.loadDocumentToViewer(null);
+ currentDocument.unload();
+ currentDocument = null;
+ }
+ }
+
+ public Document getCurrentDocument() {
+ return currentDocument;
+ }
+
+ @Override
+ public void onViewerAdd(ViewerWindow source) {
+ this.source = source;
+ }
+
+ @Override
+ public void onPageChange(int oldPage, int newPage) {
+ System.out.println("Page change: " + oldPage + " to " + newPage);
+ }
+
+ @Override
+ public void onZoomChange(double oldZoom, double newZoom) {
+ System.out.println("Zoom change: " + oldZoom + " to " + newZoom);
+ }
+
+ @Override
+ public void onScrollChange(Point oldScroll, Point newScroll) {
+ System.out.println("Scroll change: " + oldScroll + " to " + newScroll);
+ }
+
+ @Override
+ public void onOpenFile() {
+ JFileChooser chooser = new JFileChooser();
+ chooser.setFileFilter(GSFileFilter.INSTANCE);
+ chooser.setCurrentDirectory(new File("").getAbsoluteFile());
+ int status = chooser.showOpenDialog(source);
+ if (status == JFileChooser.APPROVE_OPTION)
+ open(chooser.getSelectedFile());
+ }
+
+ @Override
+ public void onCloseFile() {
+ close();
+ }
+
+ @Override
+ public void onClosing() {
+ source.dispose();
+ System.exit(0);
+ }
+
+ @Override
+ public void onSettingsOpen() {
+ System.out.println("Settings open");
+ }
+
+}
diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerGUIListenerImpl.java b/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerGUIListenerImpl.java
deleted file mode 100644
index 108be1974..000000000
--- a/demos/java/gsviewer/src/com/artifex/gsviewer/ViewerGUIListenerImpl.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.artifex.gsviewer;
-
-import java.awt.Point;
-import java.io.File;
-
-import javax.swing.JFileChooser;
-import javax.swing.JOptionPane;
-
-import com.artifex.gsviewer.gui.ViewerGUIListener;
-import com.artifex.gsviewer.gui.ViewerWindow;
-
-public class ViewerGUIListenerImpl implements ViewerGUIListener {
-
- @Override
- public void onPageChange(ViewerWindow source, int oldPage, int newPage) {
- System.out.println("Page change: " + oldPage + " to " + newPage);
- }
-
- @Override
- public void onZoomChange(ViewerWindow source, double oldZoom, double newZoom) {
- System.out.println("Zoom change: " + oldZoom + " to " + newZoom);
- }
-
- @Override
- public void onScrollChange(ViewerWindow source, Point oldScroll, Point newScroll) {
- System.out.println("Scroll change: " + oldScroll + " to " + newScroll);
- }
-
- @Override
- public void onOpenFile(ViewerWindow source) {
- System.out.println("Open file");
- JFileChooser chooser = new JFileChooser();
- chooser.setFileFilter(GSFileFilter.INSTANCE);
- chooser.setCurrentDirectory(new File("").getAbsoluteFile());
- int status = chooser.showOpenDialog(source);
- if (status == JFileChooser.APPROVE_OPTION) {
- File toOpen = chooser.getSelectedFile();
- System.out.println("To open: " + toOpen.getAbsolutePath());
- Document.loadDocumentAsync(toOpen, (final Document doc, final Exception exception) -> {
- if (exception != null) {
- JOptionPane.showMessageDialog(source, exception.toString(),
- "Failed to load", JOptionPane.ERROR_MESSAGE);
- } else {
- System.out.println("Loaded document");
- }
- });
- }
- }
-
- @Override
- public void onCloseFile(ViewerWindow source) {
- System.out.println("Close file");
- }
-
- @Override
- public void onClosing(ViewerWindow source) {
- System.out.println("Closing");
- source.dispose();
- System.exit(0);
- }
-
- @Override
- public void onSettingsOpen(ViewerWindow source) {
- System.out.println("Settings open");
- }
-
-}
diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerGUIListener.java b/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerGUIListener.java
index f56bbc5cb..7acaa587b 100644
--- a/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerGUIListener.java
+++ b/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerGUIListener.java
@@ -4,17 +4,19 @@ import java.awt.Point;
public interface ViewerGUIListener {
- public void onPageChange(ViewerWindow source, int oldPage, int newPage);
+ public void onViewerAdd(ViewerWindow source);
- public void onZoomChange(ViewerWindow source, double oldZoom, double newZoom);
+ public void onPageChange(int oldPage, int newPage);
- public void onScrollChange(ViewerWindow source, Point oldScroll, Point newScroll);
+ public void onZoomChange(double oldZoom, double newZoom);
- public void onOpenFile(ViewerWindow source);
+ public void onScrollChange(Point oldScroll, Point newScroll);
- public void onCloseFile(ViewerWindow source);
+ public void onOpenFile();
- public void onClosing(ViewerWindow source);
+ public void onCloseFile();
- public void onSettingsOpen(ViewerWindow source);
+ public void onClosing();
+
+ public void onSettingsOpen();
}
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 ca144d683..49932244c 100644
--- a/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerWindow.java
+++ b/demos/java/gsviewer/src/com/artifex/gsviewer/gui/ViewerWindow.java
@@ -1,5 +1,23 @@
package com.artifex.gsviewer.gui;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JPanel;
+import javax.swing.RepaintManager;
+
+import com.artifex.gsviewer.Document;
+import com.artifex.gsviewer.Page;
+
/**
* Auto-generated form using NetBeans.
*/
@@ -11,6 +29,10 @@ public class ViewerWindow extends javax.swing.JFrame {
private int currentPage, maxPage;
private double currentZoom;
+ private Document loadedDocument;
+ private List<PagePanel> viewerPagePanels;
+ private List<PagePanel> miniViewerPagePanels;
+
/**
* Creates new form ViewerWindow
*/
@@ -20,10 +42,13 @@ public class ViewerWindow extends javax.swing.JFrame {
public ViewerWindow(final ViewerGUIListener listener) {
initComponents();
- this.guiListener = listener;
this.currentPage = 0;
this.maxPage = 0;
this.currentZoom = 1.0;
+ this.viewerPagePanels = new ArrayList<>();
+ this.miniViewerPagePanels = new ArrayList<>();
+
+ setGUIListener(listener);
}
/**
@@ -267,7 +292,7 @@ public class ViewerWindow extends javax.swing.JFrame {
currentPage = Math.max(currentPage - 1, 1);
pageNumberField.setText(new StringBuilder().append(currentPage).toString());
if (guiListener != null)
- guiListener.onPageChange(this, oldPage, currentPage);
+ guiListener.onPageChange(oldPage, currentPage);
} else {
pageNumberField.setText(new StringBuilder().append(0).toString());
}
@@ -279,7 +304,7 @@ public class ViewerWindow extends javax.swing.JFrame {
currentPage = Math.min(currentPage + 1, maxPage);
pageNumberField.setText(new StringBuilder().append(currentPage).toString());
if (guiListener != null)
- guiListener.onPageChange(this, oldPage, currentPage);
+ guiListener.onPageChange(oldPage, currentPage);
} else {
pageNumberField.setText(new StringBuilder().append(0).toString());
}
@@ -287,7 +312,7 @@ public class ViewerWindow extends javax.swing.JFrame {
private void formWindowClosing(java.awt.event.WindowEvent evt) {// GEN-FIRST:event_formWindowClosing
if (guiListener != null)
- guiListener.onClosing(this);
+ guiListener.onClosing();
}// GEN-LAST:event_formWindowClosing
private void pageNumberFieldKeyPressed(java.awt.event.KeyEvent evt) {// GEN-FIRST:event_pageNumberFieldKeyPressed
@@ -300,7 +325,7 @@ public class ViewerWindow extends javax.swing.JFrame {
final int oldPage = currentPage;
this.currentPage = newPage;
if (guiListener != null)
- guiListener.onPageChange(this, oldPage, currentPage);
+ guiListener.onPageChange(oldPage, currentPage);
} catch (IllegalArgumentException e) {
System.err.println(new StringBuilder().append("Invalid page number \"").
append(text).append('\"').toString());
@@ -315,7 +340,7 @@ public class ViewerWindow extends javax.swing.JFrame {
final double oldZoom = currentZoom;
this.currentZoom = sliderValue;
if (guiListener != null && oldZoom != currentZoom)
- guiListener.onZoomChange(this, oldZoom, currentZoom);
+ guiListener.onZoomChange(oldZoom, currentZoom);
}// GEN-LAST:event_zoomSliderMouseReleased
private void increaseZoomButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_increaseZoomButtonActionPerformed
@@ -323,7 +348,7 @@ public class ViewerWindow extends javax.swing.JFrame {
this.currentZoom = Math.min(Math.ceil((oldZoom * 10) + 1) / 10, 2.0);
zoomSlider.setValue((int)(currentZoom * 50));
if (guiListener != null && oldZoom != currentZoom)
- guiListener.onZoomChange(this, oldZoom, currentZoom);
+ guiListener.onZoomChange(oldZoom, currentZoom);
}// GEN-LAST:event_increaseZoomButtonActionPerformed
private void decreaseZoomButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_decreaseZoomButtonActionPerformed
@@ -331,27 +356,27 @@ public class ViewerWindow extends javax.swing.JFrame {
this.currentZoom = Math.max(Math.floor((oldZoom * 10) - 1) / 10, 0.0);
zoomSlider.setValue((int)(currentZoom * 50));
if (guiListener != null && oldZoom != currentZoom)
- guiListener.onZoomChange(this, oldZoom, currentZoom);
+ guiListener.onZoomChange(oldZoom, currentZoom);
}// GEN-LAST:event_decreaseZoomButtonActionPerformed
private void openMenuActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_openMenuActionPerformed
if (guiListener != null)
- guiListener.onOpenFile(this);
+ guiListener.onOpenFile();
}// GEN-LAST:event_openMenuActionPerformed
private void closeMenuItemActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_closeMenuItemActionPerformed
if (guiListener != null)
- guiListener.onCloseFile(this);
+ guiListener.onCloseFile();
}// GEN-LAST:event_closeMenuItemActionPerformed
private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_exitMenuItemActionPerformed
if (guiListener != null)
- guiListener.onClosing(this);
+ guiListener.onClosing();
}// GEN-LAST:event_exitMenuItemActionPerformed
private void settingsMenuItemActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_settingsMenuItemActionPerformed
if (guiListener != null)
- guiListener.onSettingsOpen(this);
+ guiListener.onSettingsOpen();
}// GEN-LAST:event_settingsMenuItemActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
@@ -378,8 +403,63 @@ public class ViewerWindow extends javax.swing.JFrame {
private javax.swing.JSlider zoomSlider;
// End of variables declaration//GEN-END:variables
+ private class PagePanel extends JPanel {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final int VIEWER_MODE = 0,
+ MINI_VIEWER_MODE = 1;
+
+ private Object lock;
+ private Page page;
+ private int mode;
+
+ private PagePanel(final Page page, final Dimension size, final int mode) {
+ this.lock = new Object();
+ this.page = page;
+ this.mode = mode;
+ setPreferredSize(size);
+
+ setBackground(Color.WHITE);
+
+ pack();
+ }
+
+ @Override
+ public void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ synchronized (lock) {
+ if (page != null) {
+ Dimension size = getSize();
+ BufferedImage img;
+ switch (mode) {
+ case VIEWER_MODE:
+ img = page.getDisplayableImage();
+ if (img != null)
+ g.drawImage(img.getScaledInstance(size.width, size.height, Image.SCALE_FAST), 0, 0, this);
+ break;
+ case MINI_VIEWER_MODE:
+ img = page.getLowResImage();
+ if (img != null)
+ g.drawImage(img, 0, 0, this);
+ break;
+ default:
+ throw new IllegalStateException("Illegal mode");
+ }
+ }
+ }
+ }
+
+ @Override
+ public void invalidate() {
+ super.invalidate();
+ System.out.println("Invalidated!");
+ }
+ }
+
public void setGUIListener(final ViewerGUIListener listener) {
this.guiListener = listener;
+ listener.onViewerAdd(this);
}
public void setPage(final int newPage) {
@@ -389,7 +469,7 @@ public class ViewerWindow extends javax.swing.JFrame {
final int oldPage = currentPage;
currentPage = newPage;
if (guiListener != null)
- guiListener.onPageChange(this, oldPage, currentPage);
+ guiListener.onPageChange(oldPage, currentPage);
} catch (IllegalArgumentException e) {
System.err.println(new StringBuilder().append("Invalid page number \"").
append(newPage).append('\"').toString());
@@ -401,4 +481,65 @@ public class ViewerWindow extends javax.swing.JFrame {
public void setMaxPage(final int max) {
this.maxPage = max;
}
+
+ public void setLoadProgress(final int progress) {
+ progressBar.setValue(progress);
+ }
+
+ public void loadDocumentToViewer(final Document document) {
+ if (document == null) {
+ unloadViewerDocument();
+ return;
+ }
+
+ for (final PagePanel panel : viewerPagePanels) {
+ synchronized (panel.lock) {
+ panel.page = null;
+ }
+ }
+ viewerContentPane.removeAll();
+ viewerPagePanels.clear();
+
+ for (final PagePanel panel : miniViewerPagePanels) {
+ synchronized (panel.lock) {
+ panel.page = null;
+ }
+ }
+ miniViewerContentPane.removeAll();
+ viewerPagePanels.clear();
+
+ this.loadedDocument = document;
+
+ setResizable(false);
+
+ viewerContentPane.setLayout(new BoxLayout(viewerContentPane, BoxLayout.Y_AXIS));
+
+ for (final Page page : document) {
+ final PagePanel panel = new PagePanel(page, page.getSize(), PagePanel.VIEWER_MODE);
+ viewerContentPane.add(panel);
+ viewerContentPane.add(Box.createVerticalStrut(10));
+
+ viewerPagePanels.add(panel);
+ }
+
+ miniViewerContentPane.setLayout(new BoxLayout(miniViewerContentPane, BoxLayout.Y_AXIS));
+
+ for (final Page page : document) {
+ final PagePanel panel = new PagePanel(page, page.getLowResSize(), PagePanel.MINI_VIEWER_MODE);
+ miniViewerContentPane.add(panel);
+ miniViewerContentPane.add(Box.createVerticalStrut(10));
+
+ miniViewerPagePanels.add(panel);
+ }
+
+ setResizable(true);
+ }
+
+ public void unloadViewerDocument() {
+ this.loadedDocument = null;
+ }
+
+ public Document getLoadedDocument() {
+ return loadedDocument;
+ }
}