com.sun.pdfview.ThumbPanel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pdf-renderer Show documentation
Show all versions of pdf-renderer Show documentation
PDF renderer implementation supporting the subset of PDF 1.4 specification.
The newest version!
/*
* $Id: ThumbPanel.java,v 1.5 2009-01-26 05:09:01 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import javax.swing.JPanel;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;
/**
* A panel of thumbnails, one for each page of a PDFFile. You can add
* a PageChangeListener to be informed of when the user clicks one of the
* pages.
*/
public class ThumbPanel extends JPanel
implements Runnable, Scrollable, ImageObserver {
/** The PDFFile being displayed */
PDFFile file;
/** Array of images, one per page in the file */
Image images[];
/** Size of the border between images*/
int border = 2;
/**
* Height of each line. Thumbnails will be scaled to this height
* (minus the border).
*/
int lineheight = 96 + border;
/**
* Guesstimate of the width of a thumbnail that hasn't been processed
* yet.
*/
int defaultWidth = (lineheight - border) * 4 / 3;
/**
* Array of the x locations of each of the thumbnails. Every 0 stored
* in this array indicates the start of a new line of thumbnails.
*/
int xloc[];
/** Thread that renders each thumbnail in turn */
Thread anim;
/** Which thumbnail is selected, or -1 if no thumbnail selected. */
int showing = -1;
/**
* Which thumbnail needs to be drawn next, or -1 if the previous
* needy thumbnail is being processed.
*/
int needdrawn = -1;
/**
* Whether the default width has been guesstimated for this PDFFile
* yet.
*/
boolean defaultNotSet = true;
/** The PageChangeListener that is listening for page changes */
PageChangeListener listener;
// Flag flag= new Flag();
/**
* Creates a new ThumbPanel based on a PDFFile. The file may be null.
* Automatically starts rendering thumbnails for that file.
*/
public ThumbPanel(PDFFile file) {
super();
this.file = file;
if (file != null) {
int count = file.getNumPages();
images = new Image[count];
xloc = new int[count];
setPreferredSize(new Dimension(defaultWidth, 200));
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent evt) {
handleClick(evt.getX(), evt.getY());
}
});
anim = new Thread(this);
anim.setName(getClass().getName());
anim.start();
} else {
images = new Image[0];
setPreferredSize(new Dimension(defaultWidth, 200));
}
}
/**
* Renders each of the pages in the PDFFile into a thumbnail.
* Preferentially works on the needdrawn thumbnail, otherwise, go in
* order.
*/
public void run() {
int workingon = 0; // the thumbnail we'll be rendering next.
while (anim == Thread.currentThread()) {
if (needdrawn >= 0) {
workingon = needdrawn;
needdrawn = -1;
}
// find an unfinished page
int loop;
for (loop = images.length; loop > 0; loop--) {
if (images[workingon] == null) {
break;
}
workingon++;
if (workingon >= images.length) {
workingon = 0;
}
}
if (loop == 0) {
// done all pages.
break;
}
// build the page
try {
int pagetoread = workingon + 1;
// int pagetoread= 1;
// System.out.println("Read page: " + pagetoread);
PDFPage p = file.getPage(pagetoread, true);
int wid = (int) Math.ceil((lineheight - border) * p.getAspectRatio());
// if (!p.isFinished()) {
// System.out.println("Page not finished!");
// p.waitForFinish();
// }
// flag.clear();
// int pagetowrite= 0;
int pagetowrite = workingon;
Image i = p.getImage(wid, (lineheight - border), null, this, true, true);
// images[0] = i;
images[pagetowrite] = i;
// flag.waitForFlag();
if (defaultNotSet) {
defaultNotSet = false;
setDefaultWidth(wid);
}
repaint();
} catch (Exception e) {
e.printStackTrace();
int size = lineheight - border;
images[workingon] = new BufferedImage(size, size,
BufferedImage.TYPE_BYTE_BINARY);
}
}
}
/**
* Adds a PageChangeListener to receive notification of page clicks.
*/
public void addPageChangeListener(PageChangeListener pl) {
// [[MW: should be an array list instead of only one]]
listener = pl;
}
/**
* Removes a PageChangeListener from the notification list.
*/
public void removePageChangeListener(PageChangeListener pl) {
// [[MW: should be an array list instead of only one]]
listener = null;
}
/**
* Stops the render thread. Be sure to call this before dropping
* a ThumbPanel.
*/
public void stop() {
anim = null;
}
/**
* Sets the default width of an un-processed thumbnail.
* @param width the width of an unknown thumbnail, in pixels.
*/
public void setDefaultWidth(int width) {
defaultWidth = width;
// setPreferredSize(new Dimension(width, lineheight));
}
/**
* Handles a mouse click in the panel. Figures out which page was
* clicked, and calls showPage.
* @param x the x coordinate of the mouse click
* @param y the y coordinate of the mouse click
*/
public void handleClick(int x, int y) {
int linecount = -1;
int line = y / lineheight;
// run through the thumbnail locations, counting new lines
// until the appropriate line is reached.
for (int i = 0; i < xloc.length; i++) {
if (xloc[i] == 0) {
linecount++;
}
if (line == linecount && xloc[i] + (images[i] != null ? images[i].getWidth(null) : defaultWidth) > x) {
showPage(i);
break;
}
}
}
/**
* Sets the currently viewed page, indicates it with a highlight
* border, and makes sure the thumbnail is visible.
*/
public void pageShown(int pagenum) {
if (showing != pagenum) {
// FIND THE SELECTION RECTANGLE
// getViewPort.scrollRectToVisible(r);
if (pagenum >= 0 && getParent() instanceof JViewport) {
int y = -lineheight;
for (int i = 0; i <= pagenum; i++) {
if (xloc[i] == 0) {
y += lineheight;
}
}
Rectangle r = new Rectangle(xloc[pagenum], y,
(images[pagenum] == null
? defaultWidth : images[pagenum].getWidth(null)),
lineheight);
scrollRectToVisible(r);
}
showing = pagenum;
repaint();
}
}
/**
* Notifies the listeners that a page has been selected. Performs
* the notification in the AWT thread.
* Also highlights the selected page. Does this first so that feedback
* is immediate.
*/
public void showPage(int pagenum) {
pageShown(pagenum);
SwingUtilities.invokeLater(new GotoLater(pagenum));
}
/**
* Simple runnable to tell listeners that the page has changed.
*/
class GotoLater implements Runnable {
int page;
public GotoLater(int pagenum) {
page = pagenum;
}
public void run() {
if (listener != null) {
listener.gotoPage(page);
}
}
}
/**
* Updates the positions of the thumbnails, and draws them to the
* screen.
*/
public void paint(Graphics g) {
int x = 0;
int y = 0;
int maxwidth = 0;
Rectangle clip = g.getClipBounds();
g.setColor(Color.gray);
int width = getWidth();
g.fillRect(0, 0, width, getHeight());
for (int i = 0; i < images.length; i++) {
// calculate the x location of the thumbnail, based on its width
int w = defaultWidth + 2;
if (images[i] != null) {
w = (int) images[i].getWidth(null) + 2;
}
// need a new line?
if (x + w > width && x != 0) {
x = 0;
y += lineheight;
}
// if the thumbnail is visible, draw it.
if (clip.intersects(new Rectangle(x, y, w, lineheight))) {
if (images[i] != null) {
// thumbnail is ready.
g.drawImage(images[i], x + 1, y + 1, this);
} else {
// thumbnail isn't ready. Remember that we need it...
if (needdrawn == -1) {
needdrawn = i;
}
// ... and draw a blank thumbnail.
g.setColor(Color.lightGray);
g.fillRect(x + 1, y + 1, w - border, lineheight - border);
g.setColor(Color.darkGray);
g.drawRect(x + 1, y + 1, w - border - 1, lineheight - border - 1);
}
// draw the selection highlight if needed.
if (i == showing) {
g.setColor(Color.red);
g.drawRect(x, y, w - 1, lineheight - 1);
g.drawRect(x + 1, y + 1, w - 3, lineheight - 3);
}
}
// save the x location of this thumbnail.
xloc[i] = x;
x += w;
// remember the longest line
if (x > maxwidth) {
maxwidth = x;
}
}
// if there weren't any thumbnails, make a default line width
if (maxwidth == 0) {
maxwidth = defaultWidth;
}
Dimension d = getPreferredSize();
if (d.height != y + lineheight || d.width != maxwidth) {
setPreferredSize(new Dimension(maxwidth, y + lineheight));
revalidate();
}
}
/**
* Handles notification of any image updates. Not used any more.
*/
public boolean imageUpdate(Image img, int infoflags, int x, int y,
int width, int height) {
// if ((infoflags & ALLBITS)!=0) {
// flag.set();
// }
return ((infoflags & (ALLBITS | ERROR | ABORT)) == 0);
}
public Dimension getPreferredScrollableViewportSize() {
return getPreferredSize();
}
public int getScrollableBlockIncrement(Rectangle visrect, int orientation,
int direction) {
return Math.max(lineheight, (visrect.height / lineheight) * lineheight);
}
public boolean getScrollableTracksViewportHeight() {
return false;
}
public boolean getScrollableTracksViewportWidth() {
return true;
}
public int getScrollableUnitIncrement(Rectangle visrect, int orientation,
int direction) {
return lineheight;
}
}