summaryrefslogtreecommitdiff
path: root/platform/java/example/Viewer.java
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2017-01-04 13:19:33 +0100
committerTor Andersson <tor.andersson@artifex.com>2017-01-09 17:45:35 +0100
commit18d95a4ee602435899eae941ed0215fa1f71a01b (patch)
tree19f087faf7e24c62bc343a3cfea06dfb4c5ff87a /platform/java/example/Viewer.java
parent5d92615f37303041a473e8a25860860601a8dcfe (diff)
downloadmupdf-18d95a4ee602435899eae941ed0215fa1f71a01b.tar.xz
java: Clean up and simplify example viewer.
Don't pull in swing classes for simple desktop AWT viewer. Use inner classes for helper classes. Add list of zoom levels. Add table of content list. Make page canvas flicker free.
Diffstat (limited to 'platform/java/example/Viewer.java')
-rw-r--r--platform/java/example/Viewer.java507
1 files changed, 316 insertions, 191 deletions
diff --git a/platform/java/example/Viewer.java b/platform/java/example/Viewer.java
index abd13067..f5a1ad2b 100644
--- a/platform/java/example/Viewer.java
+++ b/platform/java/example/Viewer.java
@@ -2,112 +2,253 @@ package example;
import com.artifex.mupdf.fitz.*;
-import java.io.File;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.*;
-import java.awt.Frame;
-import java.awt.Label;
-import java.awt.Button;
-import java.awt.Panel;
-import java.awt.BorderLayout;
-import java.awt.FlowLayout;
-import java.awt.event.ActionListener;
-import java.awt.event.WindowListener;
-import java.awt.event.WindowEvent;
-import java.awt.event.ActionEvent;
-import java.awt.GraphicsEnvironment;
-import java.awt.GraphicsDevice;
-import java.awt.Toolkit;
-
-import javax.swing.JFileChooser;
-import javax.swing.filechooser.FileFilter;
-import javax.swing.JOptionPane;
+import java.io.File;
+import java.io.FilenameFilter;
import java.lang.reflect.Field;
+import java.util.Vector;
-public class Viewer extends Frame implements WindowListener, ActionListener
+public class Viewer extends Frame implements WindowListener, ActionListener, ItemListener
{
protected Document doc;
- protected Panel toolbar;
- protected PageCanvas pageCanvas;
+
+ protected ScrollPane pageScroll;
+ protected Panel pageHolder;
+ protected ImageCanvas pageCanvas;
+
+ protected Button firstButton, prevButton, nextButton, lastButton;
+ protected TextField pageField;
protected Label pageLabel;
- protected Button firstButton, prevButton, nextButton, lastButton, zoomInButton, zoomOutButton, fontIncButton, fontDecButton, toggleAnnotsButton;
+ protected Button zoomInButton, zoomOutButton;
+ protected Choice zoomChoice;
+ protected Button fontIncButton, fontDecButton;
+ protected Label fontSizeLabel;
+
+ protected List outlineList;
+ protected Vector flatOutline;
+
protected int pageCount;
- protected int pageNumber;
- protected int layoutWidth;
- protected int layoutHeight;
- protected int layoutEm;
+ protected int pageNumber = 0;
+ protected int zoomLevel = 5;
+ protected int layoutWidth = 450;
+ protected int layoutHeight = 600;
+ protected int layoutEm = 12;
+ protected float pixelScale;
+
+ protected static final int zoomList[] = {
+ 18, 24, 36, 54, 72, 96, 120, 144, 180, 216, 288
+ };
+
+ protected static BufferedImage imageFromPixmap(Pixmap pixmap) {
+ int w = pixmap.getWidth();
+ int h = pixmap.getHeight();
+ BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);
+ image.setRGB(0, 0, w, h, pixmap.getPixels(), 0, w);
+ return image;
+ }
- private float retinaScale;
+ protected static BufferedImage imageFromPage(Page page, Matrix ctm) {
+ Rect bbox = page.getBounds().transform(ctm);
+ Pixmap pixmap = new Pixmap(ColorSpace.DeviceBGR, bbox, true);
+ pixmap.clear(255);
- public Viewer(Document doc_) {
- super("MuPDF");
+ DrawDevice dev = new DrawDevice(pixmap);
+ page.run(dev, ctm, null);
+ dev.close();
+ dev.destroy();
+
+ BufferedImage image = imageFromPixmap(pixmap);
+ pixmap.destroy();
+ return image;
+ }
+
+ protected static void messageBox(Frame owner, String title, String message) {
+ final Dialog box = new Dialog(owner, title, true);
+ box.add(new Label(message), BorderLayout.CENTER);
+ Panel buttonPane = new Panel(new FlowLayout());
+ Button okayButton = new Button("Okay");
+ okayButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ box.setVisible(false);
+ }
+ });
+ buttonPane.add(okayButton);
+ box.add(buttonPane, BorderLayout.SOUTH);
+ box.pack();
+ box.setVisible(true);
+ box.dispose();
+ }
+
+ protected class ImageCanvas extends Canvas
+ {
+ protected BufferedImage image;
+
+ public void setImage(BufferedImage image_) {
+ image = image_;
+ repaint();
+ }
- retinaScale = getRetinaScale();
+ public Dimension getPreferredSize() {
+ return new Dimension(image.getWidth(), image.getHeight());
+ }
+
+ public void paint(Graphics g) {
+ float imageScale = 1 / pixelScale;
+ final Graphics2D g2d = (Graphics2D)g.create(0, 0, image.getWidth(), image.getHeight());
+ g2d.scale(imageScale, imageScale);
+ g2d.drawImage(image, 0, 0, null);
+ g2d.dispose();
+ }
+ public Dimension getMinimumSize() { return getPreferredSize(); }
+ public Dimension getMaximumSize() { return getPreferredSize(); }
+ public void update(Graphics g) { paint(g); }
+ }
+
+ public Viewer(Document doc_) {
this.doc = doc_;
+ pixelScale = getRetinaScale();
+ setTitle("MuPDF: " + doc.getMetaData(Document.META_INFO_TITLE));
+ doc.layout(layoutWidth, layoutHeight, layoutEm);
pageCount = doc.countPages();
- pageNumber = 0;
- layoutWidth = 1200;
- layoutHeight = 800;
- layoutEm = 12;
+ Panel rightPanel = new Panel(new BorderLayout());
+ {
+ Panel toolpane = new Panel(new GridBagLayout());
+ {
+ GridBagConstraints c = new GridBagConstraints();
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.WEST;
- setSize(layoutWidth, layoutHeight + 100);
- setTitle("MuPDF: " + doc.getMetaData(Document.META_INFO_TITLE));
+ Panel toolbar = new Panel(new FlowLayout(FlowLayout.LEFT));
+ {
+ firstButton = new Button("|<");
+ firstButton.addActionListener(this);
+ prevButton = new Button("<");
+ prevButton.addActionListener(this);
+ nextButton = new Button(">");
+ nextButton.addActionListener(this);
+ lastButton = new Button(">|");
+ lastButton.addActionListener(this);
+ pageField = new TextField(4);
+ pageField.addActionListener(this);
+ pageLabel = new Label("/ " + pageCount);
+
+ toolbar.add(firstButton);
+ toolbar.add(prevButton);
+ toolbar.add(pageField);
+ toolbar.add(pageLabel);
+ toolbar.add(nextButton);
+ toolbar.add(lastButton);
+ }
+ c.gridy = 0;
+ toolpane.add(toolbar, c);
- toolbar = new Panel();
- toolbar.setLayout(new FlowLayout(FlowLayout.LEFT));
- firstButton = new Button("|<");
- firstButton.addActionListener(this);
- prevButton = new Button("<");
- prevButton.addActionListener(this);
- nextButton = new Button(">");
- nextButton.addActionListener(this);
- lastButton = new Button(">|");
- lastButton.addActionListener(this);
- zoomInButton = new Button("+");
- zoomInButton.addActionListener(this);
- zoomOutButton = new Button("-");
- zoomOutButton.addActionListener(this);
-
- toggleAnnotsButton = new Button("toggle annots");
- toggleAnnotsButton.addActionListener(this);
-
- if (doc.isReflowable()) {
- fontIncButton = new Button("FONT");
- fontIncButton.addActionListener(this);
- fontDecButton = new Button("font");
- fontDecButton.addActionListener(this);
- }
+ toolbar = new Panel(new FlowLayout(FlowLayout.LEFT));
+ {
+ zoomOutButton = new Button("Zoom-");
+ zoomOutButton.addActionListener(this);
+ zoomInButton = new Button("Zoom+");
+ zoomInButton.addActionListener(this);
+
+ zoomChoice = new Choice();
+ for (int i = 0; i < zoomList.length; ++i)
+ zoomChoice.add(String.valueOf(zoomList[i]));
+ zoomChoice.select(zoomLevel);
+ zoomChoice.addItemListener(this);
+
+ toolbar.add(zoomOutButton);
+ toolbar.add(zoomChoice);
+ toolbar.add(zoomInButton);
+ }
+ c.gridy += 1;
+ toolpane.add(toolbar, c);
+
+ if (doc.isReflowable()) {
+ toolbar = new Panel(new FlowLayout(FlowLayout.LEFT));
+ {
+ fontDecButton = new Button("Font-");
+ fontDecButton.addActionListener(this);
+ fontIncButton = new Button("Font+");
+ fontIncButton.addActionListener(this);
+ fontSizeLabel = new Label(String.valueOf(layoutEm));
+
+ toolbar.add(fontDecButton);
+ toolbar.add(fontSizeLabel);
+ toolbar.add(fontIncButton);
+ }
+ c.gridy += 1;
+ toolpane.add(toolbar, c);
+ }
+ }
+ rightPanel.add(toolpane, BorderLayout.NORTH);
- pageLabel = new Label();
-
- toolbar.add(firstButton);
- toolbar.add(prevButton);
- toolbar.add(nextButton);
- toolbar.add(lastButton);
- toolbar.add(zoomInButton);
- toolbar.add(zoomOutButton);
- toolbar.add(toggleAnnotsButton);
- if (doc.isReflowable()) {
- toolbar.add(fontIncButton);
- toolbar.add(fontDecButton);
+ outlineList = new List();
+ outlineList.addItemListener(this);
+ rightPanel.add(outlineList, BorderLayout.CENTER);
}
- toolbar.add(pageLabel);
+ this.add(rightPanel, BorderLayout.EAST);
- add(toolbar, BorderLayout.NORTH);
+ pageScroll = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
+ {
+ pageHolder = new Panel(new GridBagLayout());
+ {
+ pageHolder.setBackground(Color.gray);
+ pageCanvas = new ImageCanvas();
+ pageHolder.add(pageCanvas);
+ }
+ pageScroll.add(pageHolder);
+ }
+ this.add(pageScroll, BorderLayout.CENTER);
addWindowListener(this);
- stuff();
+ updateOutline();
+ updatePageCanvas();
+
+ pack();
+ }
+
+ protected void addOutline(Outline[] outline, String indent) {
+ for (int i = 0; i < outline.length; ++i) {
+ Outline node = outline[i];
+ if (node.title != null) {
+ flatOutline.add(node);
+ outlineList.add(indent + node.title);
+ }
+ if (node.down != null)
+ addOutline(node.down, indent + " ");
+ }
+ }
+
+ protected void updateOutline() {
+ Outline[] outline = doc.loadOutline();
+ outlineList.removeAll();
+ if (outline != null) {
+ flatOutline = new Vector();
+ addOutline(outline, "");
+ outlineList.setVisible(true);
+ } else {
+ outlineList.setVisible(false);
+ }
}
- public void stuff() {
- pageLabel.setText("Page " + (pageNumber + 1) + " / " + pageCount);
- if (pageCanvas != null)
- remove(pageCanvas);
- pageCanvas = new PageCanvas(doc.loadPage(pageNumber), retinaScale);
- add(pageCanvas, BorderLayout.CENTER);
+ protected void updatePageCanvas() {
+ pageField.setText(String.valueOf(pageNumber + 1));
+
+ Matrix ctm = new Matrix().scale(zoomList[zoomLevel] / 72.0f * pixelScale);
+ BufferedImage image = imageFromPage(doc.loadPage(pageNumber), ctm);
+ pageCanvas.setImage(image);
+
+ Dimension size = pageHolder.getPreferredSize();
+ size.width += 40;
+ size.height += 40;
+ pageScroll.setPreferredSize(size);
+ pageCanvas.invalidate();
validate();
}
@@ -115,6 +256,7 @@ public class Viewer extends Frame implements WindowListener, ActionListener
Object source = event.getSource();
int oldPageNumber = pageNumber;
int oldLayoutEm = layoutEm;
+ int oldZoomLevel = zoomLevel;
if (source == firstButton)
pageNumber = 0;
@@ -133,36 +275,78 @@ public class Viewer extends Frame implements WindowListener, ActionListener
if (pageNumber >= pageCount)
pageNumber = pageCount - 1;
}
- if (doc.isReflowable() && source == fontIncButton) {
- layoutEm = layoutEm + 1;
+
+ if (source == pageField) {
+ pageNumber = Integer.parseInt(pageField.getText()) - 1;
+ if (pageNumber < 0)
+ pageNumber = 0;
+ if (pageNumber >= pageCount)
+ pageNumber = pageCount - 1;
+ pageField.setText(String.valueOf(pageNumber));
}
- if (doc.isReflowable() && source == fontDecButton) {
- layoutEm = layoutEm - 1;
+
+ if (source == fontIncButton && doc.isReflowable()) {
+ layoutEm += 1;
+ if (layoutEm > 36)
+ layoutEm = 36;
+ fontSizeLabel.setText(String.valueOf(layoutEm));
}
- if (source == zoomInButton) {
- pageCanvas.zoomIn();
+ if (source == fontDecButton && doc.isReflowable()) {
+ layoutEm -= 1;
+ if (layoutEm < 6)
+ layoutEm = 6;
+ fontSizeLabel.setText(String.valueOf(layoutEm));
}
if (source == zoomOutButton) {
- pageCanvas.zoomOut();
+ zoomLevel -= 1;
+ if (zoomLevel < 0)
+ zoomLevel = 0;
+ zoomChoice.select(zoomLevel);
}
- if (layoutEm != oldLayoutEm)
- doc.layout(layoutWidth, layoutHeight, layoutEm);
+ if (source == zoomInButton) {
+ zoomLevel += 1;
+ if (zoomLevel >= zoomList.length)
+ zoomLevel = zoomList.length - 1;
+ zoomChoice.select(zoomLevel);
+ }
- if (source == toggleAnnotsButton) {
- pageCanvas.toggleAnnots();
+ if (layoutEm != oldLayoutEm) {
+ float oldPos = (pageNumber + 0.5f) / (float)pageCount;
+ doc.layout(layoutWidth, layoutHeight, layoutEm);
+ updateOutline();
+ pageCount = doc.countPages();
+ pageLabel.setText("/ " + pageCount);
+ pageNumber = (int)(oldPos * pageCount);
}
- if (pageNumber != oldPageNumber || layoutEm != oldLayoutEm)
- stuff();
+ if (zoomLevel != oldZoomLevel || pageNumber != oldPageNumber || layoutEm != oldLayoutEm)
+ updatePageCanvas();
}
- public void windowClosing(WindowEvent event) {
- System.exit(0);
+ public void itemStateChanged(ItemEvent event) {
+ Object source = event.getSource();
+ if (source == zoomChoice) {
+ int oldZoomLevel = zoomLevel;
+ zoomLevel = zoomChoice.getSelectedIndex();
+ if (zoomLevel != oldZoomLevel)
+ updatePageCanvas();
+ }
+ if (source == outlineList) {
+ int i = outlineList.getSelectedIndex();
+ Outline node = (Outline)flatOutline.elementAt(i);
+ if (node.page >= 0) {
+ if (node.page != pageNumber) {
+ pageNumber = node.page;
+ updatePageCanvas();
+ }
+ }
+ }
}
+ public void windowClosing(WindowEvent event) { System.exit(0); }
public void windowActivated(WindowEvent event) { }
public void windowDeactivated(WindowEvent event) { }
public void windowIconified(WindowEvent event) { }
@@ -170,108 +354,50 @@ public class Viewer extends Frame implements WindowListener, ActionListener
public void windowOpened(WindowEvent event) { }
public void windowClosed(WindowEvent event) { }
- public static void main(String[] args)
- {
- JFileChooser fileChooser = new JFileChooser();
- fileChooser.setDialogTitle("Choose a file to open");
- fileChooser.setFileFilter(new FileFilter()
- {
- public String getDescription()
- {
- return "Supported files (*.pdf, *,xps, *.jpg, *.jpeg, *.png, *.epub, *.cbz, *.cbr, *.epub)";
- }
-
- public boolean accept(File f)
- {
- if (f.isDirectory())
- return true;
-
- String filename = f.getName().toLowerCase();
- if (filename.endsWith(".pdf"))
- return true;
- if (filename.endsWith(".xps"))
- return true;
- if (filename.endsWith(".jpg"))
- return true;
- if (filename.endsWith(".jpeg"))
- return true;
- if (filename.endsWith(".png"))
- return true;
- if (filename.endsWith(".epub"))
- return true;
- if (filename.endsWith(".cbz"))
- return true;
- if (filename.endsWith(".cbr"))
- return true;
- if (filename.endsWith(".epub"))
- return true;
-
- return false;
- }
- });
-
- while (true)
- {
- try
- {
- // get a file to open
- int result = fileChooser.showOpenDialog(null);
- if (result == JFileChooser.APPROVE_OPTION)
- {
- // user selects a file
- File selectedFile = fileChooser.getSelectedFile();
- if (selectedFile != null)
- {
- Document doc = new Document(selectedFile.getAbsolutePath());
- if (doc != null)
- {
- Viewer app = new Viewer(doc);
- if (app != null)
- {
- app.setVisible(true);
- return;
- }
- else
- {
- infoBox("Cannot create Viewer for "+selectedFile.getAbsolutePath(),"Error");
- }
- }
- else
- {
- infoBox("Cannot open "+selectedFile.getAbsolutePath(),"Error");
- }
- }
- else
- {
- infoBox("Selected file not found.","Error");
- }
+ public static void main(String[] args) {
+ File selectedFile;
+
+ if (args.length <= 0) {
+ FileDialog fileDialog = new FileDialog((Frame)null, "MuPDF Open File", FileDialog.LOAD);
+ fileDialog.setDirectory(System.getProperty("user.dir"));
+ fileDialog.setFilenameFilter(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ if (name.endsWith(".pdf")) return true;
+ if (name.endsWith(".xps")) return true;
+ if (name.endsWith(".oxps")) return true;
+ if (name.endsWith(".cbt")) return true;
+ if (name.endsWith(".cbz")) return true;
+ if (name.endsWith(".epub")) return true;
+ if (name.endsWith(".fb2")) return true;
+ if (name.endsWith(".xhtml")) return true;
+ return false;
}
- else
- {
- infoBox("File selection cancelled.","Error");
- return;
- }
-
- }
- catch (Exception e)
- {
- infoBox("Exception: "+e.getMessage(),"Error");
- }
+ });
+ fileDialog.setVisible(true);
+ if (fileDialog.getFile() == null)
+ System.exit(0);
+ selectedFile = new File(fileDialog.getDirectory(), fileDialog.getFile());
+ fileDialog.dispose();
+ } else {
+ selectedFile = new File(args[0]);
}
- }
- private static void infoBox(String infoMessage, String titleBar)
- {
- JOptionPane.showMessageDialog(null, infoMessage, "InfoBox: " + titleBar, JOptionPane.INFORMATION_MESSAGE);
+ try {
+ Document doc = new Document(selectedFile.getAbsolutePath());
+ Viewer app = new Viewer(doc);
+ app.setVisible(true);
+ return;
+ } catch (Exception e) {
+ messageBox(null, "MuPDF Error", "Cannot open \"" + selectedFile + "\": " + e.getMessage() + ".");
+ System.exit(1);
+ }
}
- public float getRetinaScale()
- {
+ public float getRetinaScale() {
// first try Oracle's VM (we should also test for 1.7.0_40 or higher)
final String vendor = System.getProperty("java.vm.vendor");
boolean isOracle = vendor != null && vendor.toLowerCase().contains("Oracle".toLowerCase());
- if (isOracle)
- {
+ if (isOracle) {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
final GraphicsDevice device = env.getDefaultScreenDevice();
try {
@@ -297,5 +423,4 @@ public class Viewer extends Frame implements WindowListener, ActionListener
return 1.0f;
}
-
}