/**
   @version 1.01 2004-08-24
   @author Cay Horstmann
*/

import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.print.*;
import java.io.*;
import java.util.*;
import javax.print.*;
import javax.print.attribute.*;
import javax.swing.*;


/**
   Program demonstrujący wykorzystanie usług drukowania 
   za pośrednictwem strumieni.
   Umieszcza grafikę 2D w pliku w formacie PostScript.
*/
public class StreamPrintServiceTest
{
   public static void main(String[] args)
   {  
      JFrame frame = new StreamPrintServiceFrame();
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setVisible(true);
   }
}

/**
   Ramka zawierająca panel z grafiką 2D i przyciski
   umożliwiające zapis tej grafiki w pliku PostScript
   oraz ustalenie formatu strony.
*/
class StreamPrintServiceFrame extends JFrame
{  
   public StreamPrintServiceFrame()
   {  
      setTitle("StreamPrintServiceTest");
      setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

      canvas = new PrintPanel();
      add(canvas, BorderLayout.CENTER);

      attributes = new HashPrintRequestAttributeSet();

      JPanel buttonPanel = new JPanel();
      JButton printButton = new JButton("Print");
      buttonPanel.add(printButton);
      printButton.addActionListener(new
         ActionListener()
         {
            public void actionPerformed(ActionEvent event)
            {
               String fileName = getFile();
               if (fileName != null)
                  printPostScript(fileName);
            }
         });

      JButton pageSetupButton = new JButton("Page setup");
      buttonPanel.add(pageSetupButton);
      pageSetupButton.addActionListener(new
         ActionListener()
         {
            public void actionPerformed(ActionEvent event)
            {
               PrinterJob job = PrinterJob.getPrinterJob();
               job.pageDialog(attributes);
            }
         });

      add(buttonPanel, BorderLayout.NORTH);
   }

   /**
      Umożliwia użytkownikowi wybór pliku PostScript.
      @return nazwa pliku lub wartość null, jeśli użytkownik 
      nie wybrał pliku.
   */
   public String getFile()
   {
      // tworzy okno dialogowe wyboru pliku
      JFileChooser chooser = new JFileChooser();
      chooser.setCurrentDirectory(new File("."));
      
      // akceptujące pliki o rozszerzeniu .ps
      chooser.setFileFilter(new
         javax.swing.filechooser.FileFilter()
         {
            public boolean accept(File f)
            {
               return f.getName().toLowerCase().endsWith(".ps") || f.isDirectory();
            }

            public String getDescription() { return "PostScript Files"; }
         });

      // wyświetla okno dialogowe wyboru pliku
      int r = chooser.showSaveDialog(this);
      
      // jeśli plik został wybrany, to zwraca ścieżkę dostępu
      if(r == JFileChooser.APPROVE_OPTION)
         return chooser.getSelectedFile().getPath();
      else
         return null;
   }

   /**
      Zapisuje grafikę 2D w pliku PostScript.
      @param fileName nazwa pliku PostScript
   */
   public void printPostScript(String fileName)
   {
      try
      {
         DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
         String mimeType = "application/postscript";
         StreamPrintServiceFactory[] factories = 
            StreamPrintServiceFactory.lookupStreamPrintServiceFactories(flavor, mimeType);

         FileOutputStream out = new FileOutputStream(fileName);
         if (factories.length == 0) return;
         StreamPrintService service = factories[0].getPrintService(out);

         Doc doc = new SimpleDoc(canvas, flavor, null);
         DocPrintJob job = service.createPrintJob();
         job.print(doc, attributes);
      }
      catch (FileNotFoundException e)
      {
         JOptionPane.showMessageDialog(this, e);
      }
      catch (PrintException e)
      {  
         JOptionPane.showMessageDialog(this, e);
      }

   }

   private PrintPanel canvas;
   private PrintRequestAttributeSet attributes;

   private static final int DEFAULT_WIDTH = 300;
   private static final int DEFAULT_HEIGHT = 300;
}

/**
   Panel tworzący grafikę 2D wyświetlaną na ekranie i zapisywaną w pliku
*/
class PrintPanel extends JPanel implements Printable
{  
   public void paintComponent(Graphics g)
   {  
      super.paintComponent(g);
      Graphics2D g2 = (Graphics2D) g;
      drawPage(g2);
   }

   public int print(Graphics g, PageFormat pf, int page)
      throws PrinterException
   {  
      if (page >= 1) return Printable.NO_SUCH_PAGE;
      Graphics2D g2 = (Graphics2D) g;
      g2.translate(pf.getImageableX(), pf.getImageableY());
      g2.draw(new Rectangle2D.Double(0, 0, pf.getImageableWidth(), pf.getImageableHeight()));

      drawPage(g2);
      return Printable.PAGE_EXISTS;
   }

   /**
      Metoda tworząca stronę z wykorzystaniem kontekstu graficznego ekranu
      bądź drukarki.
      @param g2 kontekst graficzny
   */
   public void drawPage(Graphics2D g2)
   {  
      FontRenderContext context = g2.getFontRenderContext();
      Font f = new Font("Serif", Font.PLAIN, 72);
      GeneralPath clipShape = new GeneralPath();

      TextLayout layout = new TextLayout("Hello", f, context);
      AffineTransform transform = AffineTransform.getTranslateInstance(0, 72);
      Shape outline = layout.getOutline(transform);
      clipShape.append(outline, false);

      layout = new TextLayout("World", f, context);
      transform = AffineTransform.getTranslateInstance(0, 144);
      outline = layout.getOutline(transform);
      clipShape.append(outline, false);

      g2.draw(clipShape);
      g2.clip(clipShape);

      final int NLINES =50;
      Point2D p = new Point2D.Double(0, 0);
      for (int i = 0; i < NLINES; i++)
      {  
         double x = (2 * getWidth() * i) / NLINES;
         double y = (2 * getHeight() * (NLINES - 1 - i))
            / NLINES;
         Point2D q = new Point2D.Double(x, y);
         g2.draw(new Line2D.Double(p, q));
      }
   }
}
