1 /* PrintACT.java
2  *
3  *
4  * Copyright(C) 2004  Genome Research Limited
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or(at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  */
21 
22 package uk.ac.sanger.artemis.components;
23 
24 import java.awt.*;
25 import java.awt.event.ActionEvent;
26 import java.awt.event.ActionListener;
27 import java.awt.event.KeyEvent;
28 import java.awt.image.BufferedImage;
29 import java.awt.image.RenderedImage;
30 import java.awt.print.PageFormat;
31 import java.awt.print.Printable;
32 import java.awt.print.PrinterException;
33 import java.awt.print.PrinterJob;
34 import java.io.File;
35 import java.io.FileNotFoundException;
36 import java.io.FileOutputStream;
37 import java.io.IOException;
38 import java.io.OutputStreamWriter;
39 import java.io.UnsupportedEncodingException;
40 import java.io.Writer;
41 
42 import javax.swing.Box;
43 import javax.swing.JCheckBox;
44 import javax.swing.JCheckBoxMenuItem;
45 import javax.swing.JComboBox;
46 import javax.swing.JFileChooser;
47 import javax.swing.JFrame;
48 import javax.swing.JLabel;
49 import javax.swing.JMenu;
50 import javax.swing.JMenuBar;
51 import javax.swing.JMenuItem;
52 import javax.swing.JOptionPane;
53 import javax.swing.JPanel;
54 import javax.swing.JScrollPane;
55 import javax.swing.JSeparator;
56 import javax.swing.KeyStroke;
57 
58 import org.apache.batik.dom.GenericDOMImplementation;
59 import org.apache.batik.svggen.SVGGeneratorContext;
60 import org.apache.batik.svggen.SVGGraphics2D;
61 import org.apache.batik.svggen.SVGGraphics2DIOException;
62 import org.w3c.dom.DOMImplementation;
63 import org.w3c.dom.Document;
64 
65 import uk.ac.sanger.artemis.Options;
66 import uk.ac.sanger.artemis.editor.ScrollPanel;
67 
68 /**
69 * Use to print images from ACT
70 */
71 public class PrintACT extends ScrollPanel implements Printable
72 {
73   private static final long serialVersionUID = 1L;
74   /** act display to create image from */
75   private MultiComparator mc;
76   private JCheckBox drawLabels = new JCheckBox("Show labels on alignment");
77 
PrintACT(MultiComparator mc)78   public PrintACT(MultiComparator mc)
79   {
80     this.mc = mc;
81   }
82 
83 
paintComponent(Graphics g)84   public void paintComponent(Graphics g)
85   {
86 // let UI delegate paint first (incl. background filling)
87     super.paintComponent(g);
88     Graphics2D g2d = (Graphics2D)g.create();
89 
90     for(int i = 0; i < mc.getFeatureDisplayArray().length; ++i)
91     {
92       if(!(i == mc.getEntryGroupArray().length - 1 &&
93                 mc.getEntryGroupArray().length == 2))
94       {
95         Component c[] = mc.getBasePlotGroupArray()[i].getComponents();
96         for(int j=0; j<c.length; j++)
97         {
98           if(c[j] instanceof BasePlot && c[j].isVisible())
99           {
100             ((BasePlot)c[j]).paintComponent(g2d);
101             g2d.translate(0,((BasePlot)c[j]).getHeight());
102           }
103         }
104 
105         if(mc.getBamPanelArray()[i].isVisible())
106         {
107           mc.getBamPanelArray()[i].paintComponents(g2d);
108           g2d.translate(0,mc.getBamPanelArray()[i].getHeight());
109         }
110 
111         if(mc.getVcfPanelArray()[i].isVisible())
112         {
113           mc.getVcfPanelArray()[i].paintComponents(g2d);
114           g2d.translate(0,mc.getVcfPanelArray()[i].getHeight());
115         }
116       }
117 
118       mc.getFeatureDisplayArray()[i].paintComponent(g2d);
119       g2d.translate(0,mc.getFeatureDisplayArray()[i].getHeight());
120 
121       if(i == mc.getEntryGroupArray().length - 1 &&
122               mc.getEntryGroupArray().length == 2)
123       {
124         Component c[] = mc.getBasePlotGroupArray()[i].getComponents();
125         for(int j=0; j<c.length; j++)
126         {
127           if(c[j] instanceof BasePlot && c[j].isVisible())
128           {
129             ((BasePlot)c[j]).paintComponent(g2d);
130             g2d.translate(0,((BasePlot)c[j]).getHeight());
131           }
132         }
133 
134         if(mc.getBamPanelArray()[i].isVisible())
135         {
136           mc.getBamPanelArray()[i].paintComponents(g2d);
137           g2d.translate(0,mc.getBamPanelArray()[i].getHeight());
138         }
139 
140         if(mc.getVcfPanelArray()[i].isVisible())
141         {
142           mc.getVcfPanelArray()[i].paintComponents(g2d);
143           g2d.translate(0,mc.getVcfPanelArray()[i].getHeight());
144         }
145       }
146 
147       if(i < mc.getAlignmentViewerArray().length)
148       {
149         mc.getAlignmentViewerArray()[i].paintComponentForPrint(g2d,drawLabels.isSelected());
150         g2d.translate(0,mc.getAlignmentViewerArray()[i].getHeight());
151       }
152     }
153 
154   }
155 
156   /**
157   *
158   * Display a print preview page
159   *
160   */
printPreview()161   protected void printPreview()
162   {
163     int width  = 999999;
164     int height = 0;
165     for(int i = 0; i < mc.getFeatureDisplayArray().length; ++i)
166     {
167       if(!(i == mc.getEntryGroupArray().length - 1 &&
168                 mc.getEntryGroupArray().length == 2))
169       {
170         Component c[] = mc.getBasePlotGroupArray()[i].getComponents();
171         for(int j=0; j<c.length; j++)
172           if(c[j] instanceof BasePlot && c[j].isVisible())
173           {
174             height += ((BasePlot)c[j]).getHeight();
175             if(((BasePlot)c[j]).getSize().width < width &&
176                ((BasePlot)c[j]).getSize().width  > 0)
177               width = ((BasePlot)c[j]).getSize().width;
178           }
179       }
180 
181       if(mc.getBamPanelArray()[i].isVisible())
182         height += mc.getBamPanelArray()[i].getHeight();
183 
184       if(mc.getVcfPanelArray()[i].isVisible())
185         height += mc.getVcfPanelArray()[i].getHeight();
186 
187       height += mc.getFeatureDisplayArray()[i].getHeight();
188       if(mc.getFeatureDisplayArray()[i].getWidth() < width)
189         width = mc.getFeatureDisplayArray()[i].getWidth();
190 
191       if(i == mc.getEntryGroupArray().length - 1 &&
192               mc.getEntryGroupArray().length == 2)
193       {
194         Component c[] = mc.getBasePlotGroupArray()[i].getComponents();
195         for(int j=0; j<c.length; j++)
196           if(c[j] instanceof BasePlot && c[j].isVisible())
197           {
198             height += ((BasePlot)c[j]).getHeight();
199             if(((BasePlot)c[j]).getSize().width < width &&
200                ((BasePlot)c[j]).getSize().width  > 0)
201               width = ((BasePlot)c[j]).getSize().width;
202           }
203       }
204 
205       if(i < mc.getAlignmentViewerArray().length)
206       {
207         height += mc.getAlignmentViewerArray()[i].getHeight();
208         if(mc.getAlignmentViewerArray()[i].getWidth() < width)
209           width = mc.getAlignmentViewerArray()[i].getWidth();
210       }
211     }
212 
213     final JFrame f = new JFrame("Print Preview");
214     JPanel jpane = (JPanel)f.getContentPane();
215     JScrollPane scrollPane = new JScrollPane(this);
216     jpane.setLayout(new BorderLayout());
217     jpane.add(scrollPane,BorderLayout.CENTER);
218 
219     final Dimension dScreen = f.getToolkit().getScreenSize();
220     Dimension d = new Dimension((int)(dScreen.getWidth()*3/4),
221                                 (int)(dScreen.getHeight()/2));
222     f.setSize(d);
223 
224     setPreferredSize(new Dimension(width,height));
225 
226     JMenuBar menuBar = new JMenuBar();
227     JMenu filemenu = new JMenu("File");
228     menuBar.add(filemenu);
229 
230 // print png/jpeg
231     JMenuItem printImage = new JMenuItem("Save As Image Files (png/jpeg)...");
232     printImage.addActionListener(new ActionListener()
233     {
234       public void actionPerformed(ActionEvent e)
235       {
236         print();
237       }
238     });
239     filemenu.add(printImage);
240 
241 //  print PostScript
242     JMenuItem printPS = new JMenuItem("Print...");
243     printPS.addActionListener(new ActionListener()
244     {
245       public void actionPerformed(ActionEvent e)
246       {
247         doPrintActions();
248       }
249     });
250     filemenu.add(printPS);
251 
252 // close
253     filemenu.add(new JSeparator());
254     JMenuItem menuClose = new JMenuItem("Close");
255     menuClose.setAccelerator(KeyStroke.getKeyStroke(
256               KeyEvent.VK_E, ActionEvent.CTRL_MASK));
257 
258     filemenu.add(menuClose);
259     menuClose.addActionListener(new ActionListener()
260     {
261       public void actionPerformed(ActionEvent e)
262       {
263         f.dispose();
264       }
265     });
266 
267     JMenu optionsmenu = new JMenu("Options");
268     menuBar.add(optionsmenu);
269 
270 // draw labels
271     JCheckBoxMenuItem showLabels = new JCheckBoxMenuItem("Display Labels",
272                                                          drawLabels.isSelected());
273     showLabels.addActionListener(new ActionListener()
274     {
275       public void actionPerformed(ActionEvent e)
276       {
277         drawLabels.setSelected(!drawLabels.isSelected());
278         repaint();
279       }
280     });
281     optionsmenu.add(showLabels);
282 
283     f.setJMenuBar(menuBar);
284     f.setVisible(true);
285   }
286 
287   /**
288   * Print to a jpeg or png file
289   */
print()290   public void print()
291   {
292     // file chooser
293     final StickyFileChooser fc = new StickyFileChooser();
294     File fselect = new File(fc.getCurrentDirectory()+
295         System.getProperty("file.separator")+
296         "act.png");
297     fc.setSelectedFile(fselect);
298 
299     // file name prefix
300     Box YBox = Box.createVerticalBox();
301     JLabel labFormat = new JLabel("Select Format:");
302     Font font = labFormat.getFont();
303     labFormat.setFont(font.deriveFont(Font.BOLD));
304     YBox.add(labFormat);
305 
306     Box bacross = Box.createHorizontalBox();
307     final JComboBox formatSelect = new JComboBox(PrintArtemis.getImageFormats());
308     formatSelect.setSelectedItem("png");
309     formatSelect.addActionListener(new ActionListener()
310     {
311       public void actionPerformed(ActionEvent arg0)
312       {
313         String selected;
314         if(fc.getSelectedFile() != null)
315         {
316           selected = fc.getSelectedFile().getAbsolutePath();
317           String fmts[] = PrintArtemis.getImageFormats();
318           for(int i=0; i<fmts.length; i++)
319             selected = selected.replaceAll("."+fmts[i]+"$", "");
320         }
321         else
322           selected = "act";
323 
324         fc.setSelectedFile(new File(selected+"."+
325                formatSelect.getSelectedItem()));
326       }
327     });
328     formatSelect.setSelectedItem("png");
329 
330     Dimension d = formatSelect.getPreferredSize();
331     formatSelect.setMaximumSize(d);
332     bacross.add(Box.createHorizontalGlue());
333     bacross.add(formatSelect);
334     YBox.add(bacross);
335 
336     bacross = Box.createHorizontalBox();
337     bacross.add(Box.createHorizontalGlue());
338     bacross.add(drawLabels);
339     YBox.add(bacross);
340 
341     // file prefix & format options
342     fc.setAccessory(YBox);
343     int n = fc.showSaveDialog(null);
344     if(n == JFileChooser.CANCEL_OPTION)
345       return;
346 
347     // remove file extension
348     String fsave = fc.getSelectedFile().getAbsolutePath().toLowerCase();
349     if(fsave.endsWith(".svg"))
350     {
351       createSVG(fc.getSelectedFile());
352       return;
353     }
354 
355     if(fsave.endsWith(".png") ||
356        fsave.endsWith(".jpg") ||
357        fsave.endsWith(".jpeg") )
358     {
359       int ind = fsave.lastIndexOf(".");
360       fsave = fc.getSelectedFile().getAbsolutePath();
361       fsave = fsave.substring(0,ind);
362     }
363     else
364       fsave = fc.getSelectedFile().getAbsolutePath();
365 
366     // image type
367     String ftype = (String)formatSelect.getSelectedItem();
368     try
369     {
370       RenderedImage rendImage = createImage();
371       writeImageToFile(rendImage, new File(fsave+"."+ftype),
372                        ftype);
373     }
374     catch(NoClassDefFoundError ex)
375     {
376       JOptionPane.showMessageDialog(this,
377             "This option requires Java 1.8 or higher.");
378     }
379   }
380 
createSVG(final File fout)381   private void createSVG(final File fout)
382   {
383     final DOMImplementation domImpl =
384         GenericDOMImplementation.getDOMImplementation();
385     final Document doc = domImpl.createDocument(
386         "http://www.w3.org/2000/svg", "svg", null);
387 
388     SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(doc);
389     ctx.setComment("Generated by ACT with Batik SVG Generator");
390     final SVGGraphics2D svgG = new SVGGraphics2D(ctx, true);
391     svgG.setFont(Options.getOptions().getFont());
392     svgG.setSVGCanvasSize( getImageSize() );
393     paintComponent(svgG);
394 
395     try
396     {
397       final Writer out = new OutputStreamWriter(
398           new FileOutputStream(fout), "UTF-8");
399       svgG.stream(out, true);
400     }
401     catch (UnsupportedEncodingException e)
402     {
403       e.printStackTrace();
404     }
405     catch (SVGGraphics2DIOException e)
406     {
407       e.printStackTrace();
408     }
409     catch (FileNotFoundException e)
410     {
411       e.printStackTrace();
412     }
413 
414     return;
415   }
416 
417   /**
418   *  Returns a generated image
419   *  @param pageIndex   page number
420   *  @return            image
421   */
createImage()422   private RenderedImage createImage()
423   {
424     Dimension d = getImageSize();
425 
426     // Create a buffered image in which to draw
427     BufferedImage bufferedImage = new BufferedImage(
428                                   d.width,d.height,
429                                   BufferedImage.TYPE_INT_RGB);
430     // Create a graphics contents on the buffered image
431     Graphics2D g2d = bufferedImage.createGraphics();
432     paintComponent(g2d);
433 
434     return bufferedImage;
435   }
436 
437   /**
438    * Get the size of the image
439    * @return
440    */
getImageSize()441   private Dimension getImageSize()
442   {
443     int width  = 999999;
444     int height = 0;
445     for(int i = 0; i < mc.getFeatureDisplayArray().length; ++i)
446     {
447       if(!(i == mc.getEntryGroupArray().length - 1 &&
448                 mc.getEntryGroupArray().length == 2))
449       {
450         Component c[] = mc.getBasePlotGroupArray()[i].getComponents();
451         for(int j=0; j<c.length; j++)
452           if(c[j] instanceof BasePlot)
453           {
454             height += ((BasePlot)c[j]).getHeight();
455             if(((BasePlot)c[j]).getSize().width < width &&
456                ((BasePlot)c[j]).getSize().width  > 0)
457               width = ((BasePlot)c[j]).getSize().width;
458           }
459       }
460 
461       if(mc.getBamPanelArray()[i].isVisible())
462         height += mc.getBamPanelArray()[i].getHeight();
463 
464       if(mc.getVcfPanelArray()[i].isVisible())
465         height += mc.getVcfPanelArray()[i].getHeight();
466 
467       height += mc.getFeatureDisplayArray()[i].getHeight();
468       if(mc.getFeatureDisplayArray()[i].getWidth() < width)
469         width = mc.getFeatureDisplayArray()[i].getWidth();
470 
471       if(i == mc.getEntryGroupArray().length - 1 &&
472               mc.getEntryGroupArray().length == 2)
473       {
474         Component c[] = mc.getBasePlotGroupArray()[i].getComponents();
475         for(int j=0; j<c.length; j++)
476           if(c[j] instanceof BasePlot)
477           {
478             height += ((BasePlot)c[j]).getHeight();
479             if(((BasePlot)c[j]).getSize().width < width &&
480                ((BasePlot)c[j]).getSize().width  > 0)
481               width = ((BasePlot)c[j]).getSize().width;
482           }
483       }
484 
485       if(i < mc.getAlignmentViewerArray().length)
486       {
487         height += mc.getAlignmentViewerArray()[i].getHeight();
488         if(mc.getAlignmentViewerArray()[i].getWidth() < width)
489           width = mc.getAlignmentViewerArray()[i].getWidth();
490       }
491     }
492     return new Dimension(width, height);
493   }
494 
495   /**
496   *
497   * Write out the image
498   * @param image        image
499   * @param file         file to write image to
500   * @param type         type of image
501   *
502   */
writeImageToFile(RenderedImage image, File file, String type)503   private void writeImageToFile(RenderedImage image,
504                                File file, String type)
505   {
506     try
507     {
508       javax.imageio.ImageIO.write(image,type,file);
509     }
510     catch ( IOException e )
511     {
512       System.out.println("Java 1.8+ is required");
513       e.printStackTrace();
514     }
515   }
516 
doPrintActions()517   protected void doPrintActions()
518   {
519     final PrinterJob pj=PrinterJob.getPrinterJob();
520     pj.setPrintable(PrintACT.this);
521     pj.printDialog();
522     try
523     {
524       pj.print();
525     }
526     catch (Exception PrintException) {}
527   }
528 
529   /**
530   *
531   * The method @print@ must be implemented for @Printable@ interface.
532   * Parameters are supplied by system.
533   *
534   */
print(Graphics g, PageFormat pf, int pageIndex)535   public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException
536   {
537     Graphics2D g2 = (Graphics2D) g;
538 
539 //  RepaintManager.currentManager(this).setDoubleBufferingEnabled(false);
540     Dimension d = getImageSize();    //get size of document
541     double panelWidth  = d.width;    //width in pixels
542     double panelHeight = d.height;   //height in pixels
543 
544     double pageHeight = pf.getImageableHeight();   //height of printer page
545     double pageWidth  = pf.getImageableWidth();    //width of printer page
546     double scale = pageWidth/panelWidth;
547     int totalNumPages = (int)Math.ceil(scale * panelHeight / pageHeight);
548     // Make sure not print empty pages
549     if(pageIndex >= totalNumPages)
550      return Printable.NO_SUCH_PAGE;
551 
552     // Shift Graphic to line up with beginning of print-imageable region
553     g2.translate(pf.getImageableX(), pf.getImageableY());
554     // Shift Graphic to line up with beginning of next page to print
555     g2.translate(0f, -pageIndex*pageHeight);
556     // Scale the page so the width fits...
557     g2.scale(scale, scale);
558     paintComponent(g2);
559     return Printable.PAGE_EXISTS;
560   }
561 
562 }
563