/* ******************************************************************************
*
* Copyright 2008-2010 Hans Dijkema
*
* JRichTextEditor 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 3 of
* the License, or (at your option) any later version.
*
* JRichTextEditor 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 JRichTextEditor. If not, see <http://www.gnu.org/licenses/>.
*
* ******************************************************************************/
package nl.dykema.jxmlnote.report.viewers;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.geom.AffineTransform;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter;
import nl.dykema.jxmlnote.exceptions.DefaultXMLNoteErrorHandler;
import nl.dykema.jxmlnote.interfaces.XMLNotePreferences;
import nl.dykema.jxmlnote.internationalization.DefaultXMLNoteTranslator;
import nl.dykema.jxmlnote.internationalization.XMLNoteTranslator;
import nl.dykema.jxmlnote.report.viewers.PdfPagePanel.ZoomListener;
import nl.dykema.jxmlnote.toolbar.JXMLNoteIcon;
import nl.dykema.jxmlnote.toolbar.JXMLNoteToolBar;
import nl.dykema.jxmlnote.widgets.JPrintPreferences;
import nl.dykema.jxmlnote.widgets.JRecentlyUsedMenu;
import nl.dykema.jxmlnote.widgets.JXMLNoteSwingUtils;
import com.sun.pdfview.PDFFile;
import com.sun.pdfview.PDFPage;
import com.sun.pdfview.PDFRenderer;
/**
* With this class it is possible to view- and print a PDF File. This can be used to view the output
* of XMLNoteToPdf. This class
*
* @author Hans Dijkema
*
*/
public class PdfViewer extends JDialog implements Printable, ActionListener {
private static final long serialVersionUID = 1L;
private XMLNoteTranslator _tr;
private JXMLNoteToolBar _toolbar;
private JMenuBar _bar;
private RandomAccessFile _raf;
private FileChannel _channel;
private PDFFile _pdffile;
private PdfPagePanel _pdfpanel;
private int _pageNr;
private JLabel _pagesLabel;
private JLabel _pageNrLabel;
private JLabel _zoomLabel;
private PageFormat _pageFormat;
private double _zoom=1.0;
private int _mode;
private XMLNotePreferences _preferences;
private File _fileToView;
private File _tmpFileToView;
private boolean _closed;
private static int _scrollUnitIncrement=16;
private class StatusBar extends JPanel {
private static final long serialVersionUID = 1L;
public StatusBar() {
super.setLayout(new FlowLayout(FlowLayout.RIGHT));
super.setBorder(BorderFactory.createEtchedBorder());
}
}
private void setZoom(double z) {
_zoom=z;
_zoomLabel.setText(String.format("%.1f%%", _zoom*100.0));
}
private double getZoom() {
return _zoom;
}
public void actionPerformed(ActionEvent e) {
String c=e.getActionCommand();
if (c.equals(JXMLNoteToolBar.ACTION_NEXT)) {
_pageNr+=1;
if (_pageNr>_pdffile.getNumPages()) {
_pageNr-=1;
}
} else if (c.equals(JXMLNoteToolBar.ACTION_PREVIOUS)) {
_pageNr-=1;
if (_pageNr<1) {
_pageNr+=1;
}
} else if (c.equals(JXMLNoteToolBar.ACTION_FIRST)) {
_pageNr=1;
} else if (c.equals(JXMLNoteToolBar.ACTION_LAST)) {
_pageNr=_pdffile.getNumPages();
} else if (c.equals(JXMLNoteToolBar.ACTION_PRINT)) {
doPrint();
} else if (c.equals(JXMLNoteToolBar.ACTION_PRINT_PREFS)) {
pageSettings();
} else if (c.equals(JXMLNoteToolBar.ACTION_ZOOM_FIT_WIDTH)) {
_pdfpanel.zoomFitWidth(new ZoomListener() { public void zoomed(double z) { setZoom(z); } });
_mode=PdfPagePanel.PAGE_FIT_WIDTH;
} else if (c.equals(JXMLNoteToolBar.ACTION_ZOOM_FIT_HEIGHT)) {
_pdfpanel.zoomFitHeight(new ZoomListener() { public void zoomed(double z) { setZoom(z); } });
_mode=PdfPagePanel.PAGE_FIT_HEIGHT;
} else if (c.equals(JXMLNoteToolBar.ACTION_ZOOM_100)) {
setZoom(1.0);
_pdfpanel.zoomFitNone(getZoom());
_mode=PdfPagePanel.PAGE_FIT_NONE;
} else if (c.equals(JXMLNoteToolBar.ACTION_ZOOM_MORE)) {
setZoom(getZoom()*1.1);
_pdfpanel.zoomFitNone(getZoom());
_mode=PdfPagePanel.PAGE_FIT_NONE;
} else if (c.equals(JXMLNoteToolBar.ACTION_ZOOM_LESS)) {
setZoom(getZoom()/1.1);
_pdfpanel.zoomFitNone(getZoom());
_mode=PdfPagePanel.PAGE_FIT_NONE;
} else if (c.equals(JXMLNoteToolBar.ACTION_CLOSE)) {
storePrefs();
try {
closePdf();
} catch (IOException e1) {
DefaultXMLNoteErrorHandler.exception(e1);
}
super.setVisible(false);
} else if (c.equals(JXMLNoteToolBar.ACTION_SAVE)) {
save();
}
if (_pdfpanel.getPage().getPageNumber()!=(_pageNr)) {
_pdfpanel.showPage(_pdffile.getPage(_pageNr));
}
_pageNrLabel.setText(Integer.toString(_pageNr));
_pagesLabel.setText(Integer.toString(_pdffile.getNumPages()));
}
private void storePrefs() {
Point p=this.getLocation();
Dimension s=this.getSize();
_preferences.put("pdfviewer.x", p.x);
_preferences.put("pdfviewer.y", p.y);
_preferences.put("pdfviewer.w", s.width);
_preferences.put("pdfviewer.h", s.height);
_preferences.put("pdfviewer.mode", _mode);
_preferences.put("pdfviewer.zoom", (int) (getZoom()*10000.0));
}
private void applyPreferences() {
Point p=new Point(_preferences.getInt("pdfviewer.x", -1),_preferences.getInt("pdfviewer.y", -1));
Dimension s=new Dimension(_preferences.getInt("pdfviewer.w", 500),_preferences.getInt("pdfviewer.h", 700));
if (p.x<0 || p.y<0) {
// do nothing
} else {
this.setLocation(p);
}
this.setPreferredSize(s);
_mode=_preferences.getInt("pdfviewer.mode", PdfPagePanel.PAGE_FIT_NONE);
_zoom=(double) (_preferences.getInt("pdfviewer.zoom", 10000)/10000.0);
if (_zoom<0.2) { _zoom=0.2; }
if (_mode==PdfPagePanel.PAGE_FIT_WIDTH) {
_pdfpanel.zoomFitWidth(new ZoomListener() { public void zoomed(double z) { setZoom(z); } });
} else if (_mode==PdfPagePanel.PAGE_FIT_HEIGHT) {
_pdfpanel.zoomFitHeight(new ZoomListener() { public void zoomed(double z) { setZoom(z); } });
} else if (_mode==PdfPagePanel.PAGE_FIT_NONE) {
_pdfpanel.zoomFitNone(_zoom);
}
}
public JXMLNoteToolBar toolbar() {
return _toolbar;
}
public void save() {
class MyFileChooser extends JFileChooser {
private static final long serialVersionUID = 1L;
public MyFileChooser(String dir) {
super(dir);
}
protected JDialog createDialog(Component parent) throws HeadlessException {
JDialog dlg=super.createDialog(parent);
if (parent instanceof Window) {
JXMLNoteSwingUtils.centerOnParent((Window) parent, dlg);
} else {
JXMLNoteSwingUtils.centerOnParent(SwingUtilities.getWindowAncestor(parent), dlg);
}
return dlg;
}
}
String prevDir=_preferences.getString("previousSaveDir", System.getProperty("user.dir"));
JFileChooser dlg=new MyFileChooser(prevDir);
dlg.setFileFilter(new FileFilter() {
public boolean accept(File f) {
return f.getName().toLowerCase().endsWith(".pdf");
}
public String getDescription() {
return _tr._(".pdf - PDF files");
}
});
int result=dlg.showSaveDialog(this);
if (result==JFileChooser.APPROVE_OPTION) {
File copyTo=dlg.getSelectedFile();
if (!copyTo.getName().toLowerCase().endsWith(".pdf")) {
String f=copyTo.getAbsolutePath()+".pdf";
copyTo=new File(f);
}
try {
byte[] buf=new byte[8192];
//InputStream in=new FileInputStream(_fileToView);
long seek=_raf.getFilePointer(); // we already have the pdf open
OutputStream out=new FileOutputStream(copyTo);
int len;
_raf.seek(0);
while ((len=_raf.read(buf))>0) {
out.write(buf,0, len);
}
out.close();
_raf.seek(seek);
String parent=copyTo.getParent();
if (parent!=null) { _preferences.put("previousSaveDir",parent); }
} catch (Exception e) {
DefaultXMLNoteErrorHandler.exception(e);
}
}
}
public int print (Graphics g, PageFormat format, int index) throws PrinterException {
int pagenum = index+1;
if (pagenum < 1 || pagenum > _pdffile.getNumPages ()) {
return NO_SUCH_PAGE;
}
Graphics2D g2d = (Graphics2D) g;
AffineTransform at = g2d.getTransform ();
PDFPage pdfPage = _pdffile.getPage (pagenum);
Dimension dim;
dim = pdfPage.getUnstretchedSize ((int) format.getImageableWidth (),
(int) format.getImageableHeight (),
pdfPage.getBBox ());
Rectangle bounds = new Rectangle ((int) format.getImageableX (),
(int) format.getImageableY (),
dim.width,
dim.height);
PDFRenderer rend = new PDFRenderer (pdfPage, (Graphics2D) g, bounds,null, null);
try {
pdfPage.waitForFinish ();
rend.run ();
} catch (InterruptedException ie) {
JOptionPane.showMessageDialog (this, ie.getMessage ());
}
/*g2d.setTransform (at);
g2d.draw (new Rectangle2D.Double (format.getImageableX (),
format.getImageableY (),
format.getImageableWidth (),
format.getImageableHeight ()));*/
return PAGE_EXISTS;
}
private void pageSettings() {
JPrintPreferences prefs=new JPrintPreferences(this,_preferences);
prefs.pageDialog();
}
private void doPrint() {
// TODO: Hier iets met landscape/rotation doen.
PrinterJob job = PrinterJob.getPrinterJob ();
JPrintPreferences prefs=new JPrintPreferences(this,_preferences);
HashPrintRequestAttributeSet set=new HashPrintRequestAttributeSet();
prefs.fromPrefs(set);
job.setPrintable (this);
Date dt=new Date();
SimpleDateFormat format=new SimpleDateFormat("yy-mm-dd HH:MM:SS");
job.setJobName(format.format(dt)+" - "+_fileToView.getName());
try
{
/*PrintService service=job.getPrintService();
DocFlavor flavor=DocFlavor.SERVICE_FORMATTED.PAGEABLE;
final GraphicsConfiguration gc =GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
JDialog dlg=new ServiceDialog(gc,10,10,service,flavor,set,this);
dlg.setModal(true);
dlg.setVisible(true);*/
if (job.printDialog (set)) {
prefs.toPrefs(set);
job.print(set);
}
}
catch (PrinterException pe) {
JOptionPane.showMessageDialog (this, pe.getMessage ());
}
}
static public void setScrollUnitIncrement(int pixels) {
_scrollUnitIncrement=pixels;
}
static public int getScrollUnitIncrement() {
return _scrollUnitIncrement;
}
static public void showPdfViewer(JComponent parent,String title,File pdfFile,XMLNotePreferences prefs) throws IOException {
showPdfViewer(SwingUtilities.getWindowAncestor(parent),parent,title,pdfFile,prefs);
}
static public void showPdfViewer(Window parent,String title,File pdfFile,XMLNotePreferences prefs) throws IOException {
showPdfViewer(parent,null,title,pdfFile,prefs);
}
public void closePdf() throws IOException {
if (!_closed) {
this._channel.close();
this._raf.close();
_closed=true;
}
}
static public void showPdfViewer(Window parent,Component relativeTo,String title,File pdfFile,XMLNotePreferences prefs) throws IOException {
PdfViewer viewer=createPdfViewer(parent,relativeTo,title,pdfFile,prefs);
viewer.setVisible(true);
viewer.dispose();
}
public static PdfViewer createPdfViewer(Window parent,Component relativeTo,String title,File pdfFile,XMLNotePreferences prefs) throws IOException {
final PdfViewer viewer=new PdfViewer(parent,title,true,pdfFile,prefs);
viewer.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setScrollUnitIncrement(getScrollUnitIncrement());
if (relativeTo!=null) {
viewer.setLocationRelativeTo(relativeTo);
}
viewer.pack();
// Sizes are known now.
PDFPage page = viewer._pdffile.getPage(0);
viewer._pdfpanel.showPage(page);
return viewer;
}
private void copyFile(File from,File to) throws IOException {
InputStream in = new FileInputStream(from);
OutputStream out = new FileOutputStream(to);
// Transfer bytes from in to out
byte[] buf = new byte[8192];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
}
protected PdfViewer(Window frame,String title,boolean modal,File fileToView,XMLNotePreferences prefs) throws IOException {
super(frame,title);
super.setModal(true);
_fileToView=fileToView;
_tmpFileToView=File.createTempFile("HOD",".pdf");
_tmpFileToView.deleteOnExit();
copyFile(_fileToView,_tmpFileToView);
//int format=PageFormat.PORTRAIT;
//format=PageFormat.LANDSCAPE;
//PrinterJob.getPrinterJob().defaultPage().setOrientation(PageFormat.LANDSCAPE);
_pageFormat = PrinterJob.getPrinterJob().defaultPage();
//_pageFormat.setOrientation(format);
//_pageFormat.setOrientation(PageFormat.LANDSCAPE);
_tr=new DefaultXMLNoteTranslator();
_toolbar=new JXMLNoteToolBar(this,null);
_toolbar.setButtonSize(24);
_toolbar.removeAllSections();
_toolbar.addSection("file",_tr._("Save PDF document"));
_toolbar.add("file",JXMLNoteToolBar.ACTION_SAVE,_tr._("save PDF document"),this);
_toolbar.addSection("navigating", _tr._("Navigate the PDF document"));
_toolbar.add("navigating", JXMLNoteToolBar.ACTION_FIRST, _tr._("First page"), this);
_toolbar.add("navigating", JXMLNoteToolBar.ACTION_PREVIOUS, _tr._("Previous page"), this);
_toolbar.add("navigating", JXMLNoteToolBar.ACTION_NEXT, _tr._("Next page"), this);
_toolbar.add("navigating", JXMLNoteToolBar.ACTION_LAST, _tr._("Last page"), this);
_toolbar.addSection("zooming", _tr._("Zooming"));
_toolbar.add("zooming", JXMLNoteToolBar.ACTION_ZOOM_FIT_WIDTH, _tr._("Fit Width"), this);
_toolbar.add("zooming", JXMLNoteToolBar.ACTION_ZOOM_FIT_HEIGHT, _tr._("Fit Height"), this);
_toolbar.add("zooming", JXMLNoteToolBar.ACTION_ZOOM_100, _tr._("100%"), this);
_toolbar.add("zooming", JXMLNoteToolBar.ACTION_ZOOM_LESS, _tr._("10% less"), this);
_toolbar.add("zooming", JXMLNoteToolBar.ACTION_ZOOM_MORE, _tr._("10% more"), this);
_toolbar.addSection("printing", _tr._("Printing"));
_toolbar.add("printing",JXMLNoteToolBar.ACTION_PRINT,_tr._("Print this document"),this);
_toolbar.add("printing",JXMLNoteToolBar.ACTION_PRINT_PREFS,_tr._("Printer preferences"),this);
_toolbar.initToolBar();
_bar=new JMenuBar();
JMenu window=JRecentlyUsedMenu.makeMenu(_tr._("_Window"));
JXMLNoteIcon icn=new JXMLNoteIcon(JXMLNoteToolBar.ACTION_CLOSE);
JMenuItem close=JRecentlyUsedMenu.makeMenuItem(_tr._("_Close"), icn, JXMLNoteToolBar.ACTION_CLOSE, this);
_bar.add(window);
window.add(close);
JPanel mb=new JPanel(new BorderLayout());
mb.add(_bar,BorderLayout.NORTH);
JPanel view=new JPanel(new BorderLayout());
view.add(_toolbar,BorderLayout.NORTH);
_pdfpanel=new PdfPagePanel(_pageFormat);
//_scrollpane=new JScrollPane(_pdfpanel,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
view.add(_pdfpanel,BorderLayout.CENTER);
mb.add(view,BorderLayout.CENTER);
StatusBar sbar=new StatusBar();
_zoomLabel=new JLabel();
_pageNrLabel=new JLabel();
_pagesLabel=new JLabel();
sbar.add(_zoomLabel);
sbar.add(new JLabel("|"));
sbar.add(_pageNrLabel);
//sbar.add(new JSeparator(JSeparator.VERTICAL));
sbar.add(new JLabel("/"));
sbar.add(_pagesLabel);
mb.add(sbar,BorderLayout.SOUTH);
this.add(mb);
_raf = new RandomAccessFile(_tmpFileToView, "r");
_channel = _raf.getChannel();
ByteBuffer buf = _channel.map(FileChannel.MapMode.READ_ONLY,0, _channel.size());
_pdffile = new PDFFile(buf);
_closed=false;
_pageNr=1;
_pageNrLabel.setText(Integer.toString(_pageNr));
_pagesLabel.setText(Integer.toString(_pdffile.getNumPages()));
_preferences=prefs;
applyPreferences();
this.addWindowListener(new WindowListener() {
public void windowActivated(WindowEvent arg0) { }
public void windowClosed(WindowEvent arg0) { }
public void windowDeactivated(WindowEvent arg0) { }
public void windowDeiconified(WindowEvent arg0) {}
public void windowIconified(WindowEvent arg0) {}
public void windowOpened(WindowEvent arg0) {}
public void windowClosing(WindowEvent arg0) {
storePrefs();
try {
closePdf();
} catch (IOException e) {
DefaultXMLNoteErrorHandler.exception(e);
}
}
});
}
}