/**
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.jpedal.org
 * (C) Copyright 1997-2008, IDRsolutions and Contributors.
 *
 *   This file is part of JPedal
 *
     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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


 *
 * ---------------
 * SilentPrint.java
 * ---------------
 */
package org.jpedal.examples.printing;

import java.awt.print.*;
import java.io.File;

import java.lang.reflect.Array;
import java.util.Hashtable;

import javax.print.*;
import javax.print.attribute.Attribute;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.Chromaticity;
import javax.print.attribute.standard.Copies;
import javax.print.attribute.standard.JobName;
import javax.print.attribute.standard.Media;
import javax.print.attribute.standard.MediaSize;
import javax.print.attribute.standard.OrientationRequested;
import javax.print.attribute.standard.PageRanges;
import javax.print.attribute.standard.PrintQuality;
import javax.print.attribute.standard.SheetCollate;
import javax.print.event.PrintJobEvent;
import javax.print.event.PrintJobListener;

import org.jpedal.PdfDecoder;
import org.jpedal.examples.simpleviewer.paper.PaperSizes;
import org.jpedal.exception.PdfException;
import org.jpedal.fonts.FontMappings;
import org.jpedal.objects.PrinterOptions;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.PdfBook;
import org.jpedal.utils.PdfPageFormat;

/**
 * This example prints a pdf file or directory of files using JPS
 */
public class SilentPrint {

    String printerName;

  /** flag to output information for debugging the code */
  private static boolean debugCode = false;

  /** correct separator for OS */
  private final String separator = System.getProperty("file.separator");

  /** the decoder object which decodes the pdf and returns a data object */
  private PdfDecoder pdfDecoder = null;

  /** used to enable printing a page at a spec index */
  public static boolean customSetting = false;

  /** number of page to be printed */
  public static int pageMark = 1;

  /** needs to be global for the printer selection to work */
  public static DocPrintJob printJob = null;
  
  /**
   * So it can be used for testing without any bigger changes in the actual
   * code
   */
  public SilentPrint() {

  }

  /**
   * example method to open a file and print the pages
   */
  public SilentPrint(String file_name, String printerName) {

        this.printerName=printerName;

    /**
     * if file name ends pdf, do the file otherwise do every pdf file in the
     * directory. We already know file or directory exists so no need to
     * check that, but we do need to check its a directory
     */
    if (file_name.toLowerCase().endsWith(".pdf")) {
      decodeAndPrintFile(file_name);
    }
    else {

      /**
       * get list of files and check directory
       */
      String[] files = null;
      File inputFiles = null;

      /** make sure name ends with a deliminator for correct path later */
      if (!file_name.endsWith(separator))
        file_name = file_name + separator;

      try {
        inputFiles = new File(file_name);

        if (!inputFiles.isDirectory()) {
          System.err.println(file_name + " is not a directory. Exiting program");
        else
          files = inputFiles.list();
      }
      catch (Exception ee) {
        LogWriter.writeLog("Exception trying to access file " + ee.getMessage());
      }

      /** now work through all pdf files */
      long fileCount = files.length;

      for (int i = 0; i < fileCount; i++) {

        if (files[i].toLowerCase().endsWith(".pdf")) {
          logMessage(file_name + files[i]);

          decodeAndPrintFile(file_name + files[i]);

                    // reset printer in case of lots of pages (cannot be reused)
                    try {
                        validatePrinter(printerName);
                    }
                    catch (PrinterException e) {
                        e.printStackTrace()// To change body of catch statement use File | Settings | File Templates.
                    }
        }
      }
    }
  }

  /**
   * routine to decode a file and print it
   */
  private void decodeAndPrintFile(String file_name) {

    /**
     * open the file and get page count
     */
    try {

      logMessage("Opening file :" + file_name + " to print.");

      pdfDecoder = new PdfDecoder(true);
      // decode_pdf.setExtractionMode(0, 72,1);
      pdfDecoder.openPdfFile(file_name);

    catch (Exception e) {
      reportError("Exception " + e + " in pdf code");
    }

    /**
     * code to handle passwords - some PDFs have an empty password set
     */
    if (pdfDecoder.isEncrypted() && !pdfDecoder.isFileViewable()) {

      String password = null// set your password here

      try {
        /** try and reopen with new password */
        if (password == null)
          pdfDecoder.setEncryptionPassword(password);
        else
          pdfDecoder.setEncryptionPassword("");

      catch (Exception e) {
        e.printStackTrace();
      }
    }

    /**
     * print if allowed and values found
     */
    if (pdfDecoder.isEncrypted() && !pdfDecoder.isFileViewable()) {
      logMessage("Encrypted settings");
    else {

      // mappings for non-embedded fonts to use
      FontMappings.setFontReplacements();

      printPages();
    }

    /** close the pdf file */
    pdfDecoder.closePdfFile();

  }

  /**
   * PRINTING CODE EXAMPLE.  If you put this into a thread you will need to synchronize
   * and ensure terminated if program exits.
   
   * Uses pageable interface so does not work under 1.3
   */
  private void printPages()
  {
    //Create set of attributes
    PrintRequestAttributeSet attributes = getPrintRequestAttributeSet();
    
    /**
     * workaround to improve performance on PCL printing by printing
     * using drawString or Java's glyph if font available in Java
     */
    // decode_pdf.setTextPrint(PdfDecoder.NOTEXTPRINT); //normal mode - only needed to reset
    // decode_pdf.setTextPrint(PdfDecoder.STANDARDTEXTSTRINGPRINT);  //print all standard fonts with Java regardless of if embedded
    // decode_pdf.setTextPrint(PdfDecoder.TEXTGLYPHPRINT); //intermediate mode - let Java create Glyphs if font matches
    // decode_pdf.setTextPrint(PdfDecoder.TEXTSTRINGPRINT); //try and get Java to do all the work
    
    //Automatically rotate and center the Pdf pages for best fit.  Default is true.
        pdfDecoder.setPrintAutoRotateAndCenter(true);
    
        //Pdf page scaling options
//    pdfDecoder.setPrintPageScalingMode(PrinterOptions.PAGE_SCALING_FIT_TO_PRINTER_MARGINS);
//    pdfDecoder.setPrintPageScalingMode(PrinterOptions.PAGE_SCALING_NONE);
    pdfDecoder.setPrintPageScalingMode(PrinterOptions.PAGE_SCALING_REDUCE_TO_PRINTER_MARGINS);
        
        PdfBook pdfBook = new PdfBook(pdfDecoder, printJob.getPrintService(), attributes);
        pdfBook.setChooseSourceByPdfPageSize(false);
        
        SimpleDoc doc = new SimpleDoc(pdfBook, DocFlavor.SERVICE_FORMATTED.PAGEABLE, null);
        
        // used to track print activity
    printJob.addPrintJobListener(new PDFPrintJobListener());   

        try {
      printJob.print(doc, attributes);
    }
        catch (Exception e) {
      LogWriter.writeLog("Exception " + e + " printing");
    }
        
    // any errors reported in JPedal
    if (pdfDecoder.getPageFailureMessage().length() 0)
      System.out.println("Errors reported from JPedal=" + pdfDecoder.getPageFailureMessage());
        
  }
  
  /**
   * This example method sets up a few attributes for a PrintService. 
   
   * See http://download.oracle.com/javase/1.4.2/docs/api/javax/print/attribute/PrintRequestAttribute.html
   * http://download.oracle.com/javase/tutorial/2d/printing/services.html and
   * http://download.oracle.com/javase/7/docs/technotes/guides/jps/spec/attributes.fm.html
   * for further information regarding attributes.
   */
  private PrintRequestAttributeSet getPrintRequestAttributeSet()
  {
    PrintRequestAttributeSet attributeSet = new HashPrintRequestAttributeSet();
    
    // Name the print job
        String[] jobString = pdfDecoder.getFileName().split("/");
        JobName jobName = new JobName(jobString[jobString.length-1]null);
        
        if(validateAttribute(jobName, attributeSet)) {
          attributeSet.add(jobName);
        }
        
        //Print multiple copies
        if(pageMark > 1) {
          Copies copies = new Copies(pageMark);
          SheetCollate collate = SheetCollate.COLLATED;
          if(validateAttribute(copies, attributeSet&& validateAttribute(collate, attributeSet)) {
            attributeSet.add(copies);
            attributeSet.add(collate);
          }
        }    
        
        //Print out a page range
        PageRanges range = new PageRanges("1-10");
        if(validateAttribute(range, attributeSet)) {
            attributeSet.add(range);
        
        
        // Black and white print
//        if(validateAttribute(Chromaticity.MONOCHROME, attributeSet)) {
//          attributeSet.add(Chromaticity.MONOCHROME);
//        }
        
        // Draft print quality
//        if(validateAttribute(PrintQuality.DRAFT, attributeSet)) {
//          attributeSet.add(PrintQuality.DRAFT);
//        }
        
        return attributeSet;
  }
  
  /**
   @return true if the attribute is supported on the current PrintService
   */
  private static boolean validateAttribute(Attribute att, PrintRequestAttributeSet attributeSet)
  {
        return printJob.getPrintService().isAttributeValueSupported(att, DocFlavor.SERVICE_FORMATTED.PAGEABLE, attributeSet);
  }
  
  /**
   * Checks that the desired PrintService exists.
   
   @param newPrinter The name of the PrintService
   @return true if PrintService is available
   @throws PrinterException
   */
  private static boolean validatePrinter(String newPrinterthrows PrinterException {

    boolean matchFound = false;

    PrintService[] service = PrinterJob.lookupPrintServices()// list of printers

    int count = service.length;

    for (int i = 0; i < count; i++) {
      if (service[i].getName().indexOf(newPrinter!= -1) {
        printJob = service[i].createPrintJob();
        i = count;
        matchFound = true;
      }
    }

    if (!matchFound) {
      String list = "";
      for (int i = 0; i < count; i++)
        list = list + '\"' + service[i].getName() "\",";

      reportError("Printer " + newPrinter + " not supported. Options=" + list);
    }

    return matchFound;
  }

  /** single routine to log activity for easy debugging */
  private static void logMessage(String message) {

    // change to suit your needs
    if (debugCode) {
      System.out.println(message);
      LogWriter.writeLog(message);
    }
  }

  /** single routine so error handling can be easily setup */
  private static void reportError(String message)  {
    // change to suit your needs
    System.err.println(message);
    LogWriter.writeLog(message);
  }

  /**
   * main routine which checks for any files passed and runs the demo
   */
  public static void main(String[] args) {

    logMessage("Simple demo to print pages");

    // check user has passed us a filename and use default if none
    if (args.length != 2) {

      System.out.println("Printing needs 2 parameters");
      System.out.println("Parameter 1 - File name or directory (put in quotes if it contains spaces");
      System.out.println("Parameter 2- a printer name");

      System.out.println("---Available printers are---");
      
      try {
        PrintService[] service = PrinterJob.lookupPrintServices()// list of printers

        int count = service.length;

        for (int i = 0; i < count; i++)
          System.out.println("\"" + service[i].getName() "\"");

      }
      catch (Exception e) {
        e.printStackTrace()// To change body of catch statement use File | Settings | File Templates.
      }
    }
    else {

      String file_name = args[0];
      String printerName = args[1];

      logMessage("File :" + file_name);
      logMessage("Printer :" + printerName);

      boolean validPrinter = false;

      // check printer exists
      try {
        validPrinter = validatePrinter(printerName);
      }
      catch (PrinterException e) {
        e.printStackTrace()// To change body of catch statement use File | Settings | File Templates.
      }

      // check file exists
      File pdf_file = new File(file_name);

      // if file exists, open and get number of pages
      if (pdf_file.exists() == false) {
        logMessage("File " + file_name + " not found");

      }
      else if (!validPrinter) {
        logMessage("Printer " + printerName + " not found");

      }
      else {
        new SilentPrint(file_name,printerName);
      }
    }
  }

  /**
   * listener code - just an example
   */
  private static class PDFPrintJobListener implements PrintJobListener {

    static final private boolean showMessages = false;

    public void printDataTransferCompleted(PrintJobEvent printJobEvent) {
      if (showMessages)
        System.out.println("printDataTransferCompleted=" + printJobEvent.toString());
    }

    public void printJobCompleted(PrintJobEvent printJobEvent) {
      if (showMessages)
        System.out.println("printJobCompleted="  + printJobEvent.toString());
    }

    public void printJobFailed(PrintJobEvent printJobEvent) {
      if (showMessages)
        System.out.println("printJobEvent=" + printJobEvent.toString());
    }

    public void printJobCanceled(PrintJobEvent printJobEvent) {
      if (showMessages)
        System.out.println("printJobFailed=" + printJobEvent.toString());
    }

    public void printJobNoMoreEvents(PrintJobEvent printJobEvent) {
      if (showMessages)
        System.out.println("printJobNoMoreEvents=" + printJobEvent.toString());
    }

    public void printJobRequiresAttention(PrintJobEvent printJobEvent) {
      if (showMessages)
        System.out.println("printJobRequiresAttention="  + printJobEvent.toString());
    }
  }
}