001/*
002 *  TwoUpPDF
003 *  a simple tool to format a PDF document's pages as 2Up pages that have been
004 *  scaled/maximized for readablity when 2Up.
005 *
006 *  $URL: svn://svn.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/tools/pdf/CropPDF.java $
007 *  $REVISION: $
008 *  Copyright (C) 2003 Tom Gutwin
009 *  tgutwin@webarts.bc.ca
010 *
011 *
012 *  This program is free software; you can redistribute it and/or
013 *  modify it under the terms of the GNU General Public License
014 *  as published by the Free Software Foundation; either version 2
015 *  of the License, or any later version.
016 *
017 *  This program is distributed in the hope that it will be useful,
018 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
019 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
020 *  GNU General Public License for more details.
021 *
022 *  You should have received a copy of the GNU General Public License
023 *  along with the jEdit program; if not, write to the Free Software
024 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
025 */
026
027package ca.bc.webarts.tools.pdf;
028
029import java.io.File;
030import java.io.FileOutputStream;
031
032import com.itextpdf.text.Document;
033import com.itextpdf.text.PageSize;
034import com.itextpdf.text.pdf.PdfContentByte;
035//import com.itextpdf.text.pdf.PdfDocument;
036import com.itextpdf.text.pdf.PdfImportedPage;
037import com.itextpdf.text.pdf.PdfReader;
038import com.itextpdf.text.pdf.PdfWriter;
039import com.itextpdf.text.Rectangle;
040
041
042/**
043 *  A tool to manipulate PDF files by cropping each page by the specified
044 *  amounts.
045 *  <br>
046 *  This class can be called from the commandline or instantiated and used from
047 *  other Java Objects.<br><br>
048 *  <b><u>Commandline Syntax:</u></b><br>
049 *  java CropPDF [leftPageCrop] [rightPageCrop] [topPageCrop] [bottomPageCrop] PDFFilename<br />
050 *  where pageCrop is in inches<br>
051 *
052 * @author    tgutwin
053 */
054public class CropPDF
055{
056  /** Default input filename. **/
057  public static final String DEFAULT_INPUT_FILENAME = "infile.pdf";
058
059  /**  The input filename that will be cropped and saved in output filename.
060   *   DEFAULT = infile.pdf**/
061  private static String inFileName_ = DEFAULT_INPUT_FILENAME;
062
063  /**  The output filename string. DEFAULT = infile-Crop.pdf**/
064  private static String outFileName_ = "infile-Crop.pdf";
065
066  /**  The left Cropped amount in inches. Default = 0.0**/
067  private static float leftCropBorder_ = 0.0f;
068
069  /**  The right Cropped amount in inches. Default = 0.0 **/
070  private static float rightCropBorder_ = 0.0f;
071
072  /**  The top Cropped amount in inches. Default = 0.0 **/
073  private static float topCropBorder_ = 0.0f;
074
075  /**  The bottom Cropped amount in inches. Default = 0.0 **/
076  private static float bottomCropBorder_ = 0.0f;
077
078  /**  EVEN Page left Cropped amount in inches. Default = 0.0**/
079  private static float evenLeftCropBorder_ = 0.0f;
080
081  /**  EVEN Page right Cropped amount in inches. Default = 0.0 **/
082  private static float evenRightCropBorder_ = 0.0f;
083
084  /**  EVEN Page top Cropped amount in inches. Default = 0.0 **/
085  private static float evenTopCropBorder_ = 0.0f;
086
087  /**  EVEN Page bottom Cropped amount in inches. Default = 0.0 **/
088  private static float evenBottomCropBorder_ = 0.0f;
089
090  /**  An angle in Radians to rotate the page after it has been cropped.
091   *   For example: To change a Portrait page to landscape - set this to
092   *                Math.toRadians(270)
093   **/
094  private static double rotation_ = Math.toRadians(0);
095
096  /**  The size of the resultant page. */
097  private static Rectangle outputPageSize_ = PageSize.LETTER;//PageSize.A4;
098
099  /**  A scaling ratio to enlarge the newly cropped page
100   *   if scaling is enabled. **/
101  private static float scale_ = outputPageSize_.getWidth() /
102               (outputPageSize_.getWidth() - (leftCropBorder_ + rightCropBorder_));
103
104  /**
105   * flags if there are different crop borders for the even pages as opposed to
106   * the even pages.
107   **/
108  private static boolean evenOddPageCrops_ = false;
109
110  /**  Flags the auto execution of acro reader after the cropping.
111   *   DEFAULT = false **/
112  private static boolean launchAcroread_ = false;
113
114  /**  Flags whether the gui launches on startup.
115   *   DEFAULT = false **/
116  private static boolean launchGui_ = false;
117
118  /**  Flags if the cropped page will also be scalled to max fit on the
119   *   new output page. DEFAULT - false.  This is usefull if you want to crop
120   *   the margins off a page and the expand all the remaining doc to fit
121   *   on the output page.  If set to true, this will set the output page to
122   *   the size of the input page and enlarge the cropped doc to fill the
123   *   output page*/
124  private static boolean doTheScale_ = false;
125
126  /**  The path/command to run the acroreader. **/
127  private static String acroCmd_ = "/home/tgutwin/bin/acro";
128
129  /**  The usage string. **/
130  private static String usage_ = "SYNTAX: CropPDF [leftPageCrop] [rightPageCrop] "+
131      "[topPageCrop] [bottomPageCrop] [evenLeftPageCrop evenrightPageCrop "+
132      "eventopPageCrop evenbottomPageCrop] PDFFilename\n" +
133      "        where pageCrop is in inches";
134
135
136  /**
137   * Default Constructor assumes default values for EVERYTHING.
138   **/
139   public CropPDF()
140   {
141
142   }
143
144
145  /**
146   * Constructor that takes the edge crop sizes in inches.
147   **/
148   public CropPDF(float left, float right, float top, float bottom )
149   {
150     setLeftCropBorder(left);
151     setRightCropBorder(right);
152     setTopCropBorder(top);
153     setBottomCropBorder(bottom);
154   }
155
156
157  /**
158   * Constructor that takes the edge crop sizes in inches.
159   **/
160   public CropPDF(float left, float right, float top, float bottom, String fName )
161   {
162     setLeftCropBorder(left);
163     setRightCropBorder(right);
164     setTopCropBorder(top);
165     setBottomCropBorder(bottom);
166     setInFileName(fName);
167     setOutFileName(fName.substring(0, fName.length()-4)+"-Crop.pdf");
168   }
169
170
171  /**
172   * Constructor that takes the edge crop sizes in inches for even pages and odd pages
173   **/
174   public CropPDF(float evenleft, float evenright, float eventop,
175                  float evenbottom, float left, float right, float top,
176                  float bottom )
177   {
178     setEvenLeftCropBorder(evenleft);
179     setEvenRightCropBorder(evenright);
180     setEvenTopCropBorder(eventop);
181     setEvenBottomCropBorder(evenbottom);
182     setLeftCropBorder(left);
183     setRightCropBorder(right);
184     setTopCropBorder(top);
185     setBottomCropBorder(bottom);
186   }
187
188
189  /**
190   * Constructor that takes the edge crop sizes in inches for even pages and odd pages
191   **/
192   public CropPDF(float evenleft, float evenright, float eventop,
193                  float evenbottom, float left, float right, float top,
194                  float bottom, String fName )
195   {
196     setEvenLeftCropBorder(evenleft);
197     setEvenRightCropBorder(evenright);
198     setEvenTopCropBorder(eventop);
199     setEvenBottomCropBorder(evenbottom);
200     setLeftCropBorder(left);
201     setRightCropBorder(right);
202     setTopCropBorder(top);
203     setBottomCropBorder(bottom);
204     setInFileName(fName);
205     setOutFileName(fName.substring(0, fName.length()-4)+"-Crop.pdf");
206   }
207
208
209  /**
210   *  The main program for the CropPDF class
211   *
212   * @param  args  The command line arguments
213   */
214  public static void main(String[] args)
215  {
216    CropPDF instance = new CropPDF();
217    boolean validParms = instance.parseCmdlineParms(args);
218
219
220    /*
221     *  Parms are now parsed.... start the transform/crop
222     */
223    if (validParms)
224    {
225      if (launchGui_)
226      {
227        // launch gui for crop info
228      }
229      else
230      {
231        // just do the crop
232        instance.doCrop();
233      }
234    }
235    else
236    {
237      // nothing for now.
238    }
239  }
240
241
242  /**
243   * Parses the input commandline parameters and assigns the various class
244   * variables.
245   *
246   * @return success or failure
247   **/
248  private boolean parseCmdlineParms(String [] args)
249  {
250    boolean retVal = false;
251    switch (args.length)
252    {
253        /* Only the Filename Spec'd */
254        /* ------------------------ */
255        case (1):
256          if (args[0].endsWith(".pdf"))
257          {
258            setInFileName(args[0]);
259            setOutFileName(null);
260            retVal = true;
261          }
262          else if (args[0].startsWith("-gui"))
263          {
264            this.launchGui_ = true;
265            retVal = true;
266          }
267          break;
268
269        /* Left Border and Filename Spec'd */
270        /* ------------------------------- */
271        case (2):
272          try
273          {
274            leftCropBorder_ = Float.parseFloat(args[0]);
275            if (args[args.length - 1].endsWith(".pdf"))
276            {
277              setInFileName(args[args.length - 1]);
278              setOutFileName(null);
279              retVal = true;
280            }
281          }
282          catch (NumberFormatException numEx)
283          {
284            System.out.println("Left Crop Size error: ");
285            System.out.println(usage_);
286            retVal = false;
287          }
288          break;
289
290        /* Left, Right Border and Filename Spec'd */
291        /* -------------------------------------- */
292        case (3):
293          try
294          {
295            leftCropBorder_ = Float.parseFloat(args[0]);
296            rightCropBorder_ = Float.parseFloat(args[1]);
297            if (args[args.length - 1].endsWith(".pdf"))
298            {
299              setInFileName(args[args.length - 1]);
300              setOutFileName(null);
301              retVal = true;
302            }
303          }
304          catch (NumberFormatException numEx)
305          {
306            System.out.println("Crop Size error: ");
307            System.out.println(usage_);
308            retVal = false;
309          }
310          break;
311
312        /* Left, Right, Top Border and Filename Spec'd */
313        /* ------------------------------------------- */
314        case (4):
315          try
316          {
317            leftCropBorder_ = Float.parseFloat(args[0]);
318            rightCropBorder_ = Float.parseFloat(args[1]);
319            topCropBorder_ = Float.parseFloat(args[2]);
320            if (args[args.length - 1].endsWith(".pdf"))
321            {
322              setInFileName(args[args.length - 1]);
323              setOutFileName(null);
324              retVal = true;
325            }
326          }
327          catch (NumberFormatException numEx)
328          {
329            System.out.println("Crop Size error: ");
330            System.out.println(usage_);
331            retVal = false;
332          }
333          break;
334
335        /* Left, Right, Top, Bottom Border and Filename Spec'd */
336        /* --------------------------------------------------- */
337        case (5):
338          try
339          {
340            leftCropBorder_ = Float.parseFloat(args[0]);
341            rightCropBorder_ = Float.parseFloat(args[1]);
342            topCropBorder_ = Float.parseFloat(args[2]);
343            bottomCropBorder_ = Float.parseFloat(args[3]);
344            if (args[args.length - 1].endsWith(".pdf"))
345            {
346              setInFileName(args[args.length - 1]);
347              setOutFileName(null);
348              retVal = true;
349            }
350          }
351          catch (NumberFormatException numEx)
352          {
353            System.out.println("Crop Size error: ");
354            System.out.println(usage_);
355            retVal = false;
356          }
357          break;
358
359        /* Left, Right, Top, Bottom Border and different border
360           margins for the opposite pages and Filename Spec'd */
361        /* --------------------------------------------------- */
362        case (9):
363          try
364          {
365            evenLeftCropBorder_ = Float.parseFloat(args[0]);
366            evenRightCropBorder_ = Float.parseFloat(args[1]);
367            evenTopCropBorder_ = Float.parseFloat(args[2]);
368            evenBottomCropBorder_ = Float.parseFloat(args[3]);
369            leftCropBorder_ = Float.parseFloat(args[4]);
370            rightCropBorder_ = Float.parseFloat(args[5]);
371            topCropBorder_ = Float.parseFloat(args[6]);
372            bottomCropBorder_ = Float.parseFloat(args[7]);
373            if (args[args.length - 1].endsWith(".pdf"))
374            {
375              evenOddPageCrops_ = true;
376              setInFileName(args[args.length - 1]);
377              setOutFileName(null);
378              retVal = true;
379            }
380          }
381          catch (NumberFormatException numEx)
382          {
383            System.out.println("Crop Size error: ");
384            System.out.println(usage_);
385            retVal = false;
386          }
387          break;
388
389        default:
390          System.out.println("Commandline parameter error: ");
391          System.out.println(usage_);
392          retVal = false;
393    }
394
395    if (!retVal)
396    {
397      System.out.println("Filename ERROR - must end with \".pdf\": ");
398      System.out.println(usage_);
399
400    }
401    return retVal;
402  }
403
404
405
406  /**
407   * Executes the reading of a PDF file into a Document Object.
408   *
409   * @return success or failure
410   **/
411  public static Document parsePDFFile(String inFileName)
412  {
413    Document retVal = null;
414    String outFileName =
415        inFileName.substring(0, inFileName.length()-4)+".tmp.pdf";
416    try
417    {
418      // we create a reader for a certain document
419      System.out.println("Reading: " + inFileName);
420      PdfReader reader = new PdfReader(inFileName);
421      float width_DPI = reader.getPageSize(1).getWidth();
422      float height_DPI = reader.getPageSize(1).getHeight();
423      float width = width_DPI / 72.0f;
424      float height = height_DPI / 72.0f;
425
426      // step 1: creation of a document-object
427      Rectangle newPageSize = null;
428      newPageSize = reader.getPageSize(1);
429
430      // we retrieve the total number of pages
431      int numPages = reader.getNumberOfPages();
432
433      Document document =
434          new Document(newPageSize, 0, 0, 0, 0);// Margins: left, right,top, bottom
435
436      // step 2: we create a writer that listens to the document
437      System.out.println("Writing: " + outFileName);
438      FileOutputStream outFileStream = new FileOutputStream(outFileName);
439      PdfWriter writer = PdfWriter.getInstance(document, outFileStream);
440
441      // step 3: we open the document
442      document.open();
443
444      // step 4: calculate the new scaling / layout postion
445      PdfContentByte cb = writer.getDirectContent();
446      System.out.println("There are " + numPages +
447          " pages in the document.");
448      PdfImportedPage currentPDFPage;
449      int currentPageNum = 0;
450
451      /*
452       *  step 5: Start Copying the pages to the new Doc
453       *  ******* ***************************************
454       */
455      while (currentPageNum < numPages)
456      {
457        document.newPage();
458        currentPDFPage = writer.getImportedPage(reader, ++currentPageNum);
459        cb.addTemplate(currentPDFPage, 0.0f, 0.0f);
460      }
461
462      // step 6: we close the document
463      document.close();
464      retVal = document;
465    }
466    catch (Exception de)
467    {
468      de.printStackTrace();
469      retVal = null;
470    }
471
472    return retVal;
473  }
474
475
476  /**
477   * Executes the cropping/resizing as specified by the class parms.
478   *
479   * @return success or failure
480   **/
481  public boolean doCrop()
482  {
483    return doCrop(this.evenOddPageCrops_);
484  }
485
486
487  /**
488   * Executes the cropping/resizing as specified by the class parms.
489   *
490   * @return success or failure
491   **/
492  public boolean doCrop(boolean doingEvenOddCrops)
493  {
494    boolean retVal = true;
495
496    try
497    {
498      // we create a reader for a certain document
499      System.out.println("Reading: " + inFileName_);
500      PdfReader reader = new PdfReader(inFileName_);
501      float width_DPI = reader.getPageSize(1).getWidth();
502      float height_DPI = reader.getPageSize(1).getHeight();
503      float width = width_DPI / 72.0f;
504      float height = height_DPI / 72.0f;
505
506      // step 1: creation of a document-object
507      Rectangle newEvenPageSize = null;
508      Rectangle newOddPageSize = null;
509      if (doTheScale_)
510      {
511        scale_ = (width) / (width - (leftCropBorder_ + rightCropBorder_));
512        newOddPageSize = reader.getPageSize(1);
513        scale_ = (width) / (width - (evenLeftCropBorder_ + evenRightCropBorder_));
514        newEvenPageSize = reader.getPageSize(1);
515      }
516      else
517      {
518        scale_ = 1.0f;
519        newOddPageSize = new Rectangle(
520            width_DPI - (leftCropBorder_ + rightCropBorder_) * 72.0f,
521            height_DPI - (topCropBorder_ + bottomCropBorder_) * 72.0f);
522        newEvenPageSize = new Rectangle(
523            width_DPI - (evenLeftCropBorder_ + evenRightCropBorder_) * 72.0f,
524            height_DPI - (evenTopCropBorder_ + evenBottomCropBorder_) * 72.0f);
525      }
526
527      // we retrieve the total number of pages
528      int numPages = reader.getNumberOfPages();
529      System.out.println("Cropping " + numPages + " - " + width + "x" + height +
530          " pages by (left=" + leftCropBorder_ + " right=" + rightCropBorder_ +
531          " Top=" + topCropBorder_ + " bottom=" + bottomCropBorder_ +
532          " scale=" + scale_ + ")");
533      if (doingEvenOddCrops)
534        System.out.println("Cropping Even pages" +
535          " by (left=" + evenLeftCropBorder_ + " right=" + evenRightCropBorder_ +
536          " Top=" + evenTopCropBorder_ + " bottom=" + evenBottomCropBorder_ +
537          " scale=" + scale_ + ")");
538      System.out.println("New Page Size: " +  newOddPageSize.getWidth()/ 72.0f + "x" +
539                          newOddPageSize.getHeight()/ 72.0f);
540
541      Document document =
542          new Document(newOddPageSize, 0, 0, 0, 0);// Margins: left, right,top, bottom
543
544      // step 2: we create a writer that listens to the document
545      String outFileName =
546          inFileName_.substring(0, inFileName_.length()-4)+"-Crop.pdf";
547      System.out.println("Writing: " + outFileName_);
548      FileOutputStream outFileStream = new FileOutputStream(outFileName_);
549      PdfWriter writer = PdfWriter.getInstance(document, outFileStream);
550
551      // step 3: we open the document
552      document.open();
553
554      // step 4: calculate the new scaling / layout postion
555      PdfContentByte cb = writer.getDirectContent();
556      System.out.println("There are " + numPages +
557          " pages in the document.");
558      PdfImportedPage importedPage;
559      int currentPage = 0;
560
561      // Calculate the Transfor parameters to use given the scale and postion
562      float cosRotation = (new Float(Math.cos(rotation_))).floatValue();
563      float sinRotation = (new Float(Math.sin(rotation_))).floatValue();
564      float a = scale_ * cosRotation;
565      float b = scale_ * sinRotation;
566      float c = -1.0f * scale_ * sinRotation;
567      float d = scale_ * cosRotation;
568      //System.out.println("a= " +a);
569      //System.out.println("b= " +b);
570      //System.out.println("c= " +c);
571      //System.out.println("d= " +d);
572
573      /*
574       *  step 5: Start Copying the pages to the new Doc (in 2Up format)
575       *  ******* ******************************************************
576       */
577      while (currentPage < numPages)
578      {
579        document.newPage();
580        importedPage = writer.getImportedPage(reader, ++currentPage);
581        if (doingEvenOddCrops && currentPage%2 ==0)
582          cb.addTemplate(importedPage, a, b, c, d, -(evenLeftCropBorder_* 72.0f),
583                                                   -(evenBottomCropBorder_* 72.0f));
584        else
585          cb.addTemplate(importedPage, a, b, c, d, -(leftCropBorder_* 72.0f),
586                                                   -(bottomCropBorder_* 72.0f));
587      }
588
589      // step 6: we close the document
590      document.close();
591    }
592    catch (Exception de)
593    {
594      de.printStackTrace();
595      retVal = false;
596    }
597
598    return retVal;
599  }
600
601
602  /**
603   * Setter.
604   **/
605  public void setLeftCropBorder(float leftCropBorder)
606  {
607    this.leftCropBorder_ = leftCropBorder;
608  }
609
610
611  /**
612   * Getter.
613   **/
614  public float getLeftCropBorder()
615  {
616    return leftCropBorder_;
617  }
618
619
620  /**
621   * Setter.
622   **/
623  public void setEvenLeftCropBorder(float evenLeftCropBorder)
624  {
625    this.evenLeftCropBorder_ = evenLeftCropBorder;
626  }
627
628
629  /**
630   * Getter.
631   **/
632  public float getEvenLeftCropBorder()
633  {
634    return evenLeftCropBorder_;
635  }
636
637
638  /**
639   * Setter.
640   **/
641  public void setRightCropBorder(float rightCropBorder)
642  {
643    this.rightCropBorder_ = rightCropBorder;
644  }
645
646
647  /**
648   * Getter.
649   **/
650  public float getRightCropBorder()
651  {
652    return rightCropBorder_;
653  }
654
655
656  /**
657   * Setter.
658   **/
659  public void setEvenRightCropBorder(float evenRightCropBorder)
660  {
661    this.evenRightCropBorder_ = evenRightCropBorder;
662  }
663
664
665  /**
666   * Getter.
667   **/
668  public float getEvenRightCropBorder()
669  {
670    return evenRightCropBorder_;
671  }
672
673
674  /**
675   * Setter.
676   **/
677  public void setTopCropBorder(float topCropBorder)
678  {
679    this.topCropBorder_ = topCropBorder;
680  }
681
682
683  /**
684   * Getter.
685   **/
686  public float getTopCropBorder()
687  {
688    return topCropBorder_;
689  }
690
691
692  /**
693   * Setter.
694   **/
695  public void setEvenTopCropBorder(float evenTopCropBorder)
696  {
697    this.evenTopCropBorder_ = evenTopCropBorder;
698  }
699
700
701  /**
702   * Getter.
703   **/
704  public float getEvenTopCropBorder()
705  {
706    return evenTopCropBorder_;
707  }
708
709
710  /**
711   * Setter.
712   **/
713  public void setBottomCropBorder(float bottomCropBorder)
714  {
715    this.bottomCropBorder_ = bottomCropBorder;
716  }
717
718
719  /**
720   * Getter.
721   **/
722  public float getBottomCropBorder()
723  {
724    return bottomCropBorder_;
725  }
726
727
728  /**
729   * Setter.
730   **/
731  public void setEvenBottomCropBorder(float evenBottomCropBorder)
732  {
733    this.evenBottomCropBorder_ = evenBottomCropBorder;
734  }
735
736
737  /**
738   * Getter.
739   **/
740  public float getEvenBottomCropBorder()
741  {
742    return evenBottomCropBorder_;
743  }
744
745
746  /**
747   * Setter.
748   **/
749  public void setDoTheScale(boolean doTheScale)
750  {
751    this.doTheScale_ = doTheScale;
752  }
753
754
755  /**
756   * Getter.
757   **/
758  public boolean getDoTheScale()
759  {
760    return doTheScale_;
761  }
762
763
764  /**
765   * Setter for input filename. IT MUST END WITH A ".pdf".
766   **/
767  public void setInFileName(String inFileName)
768  {
769    if (inFileName == null ||
770        inFileName.equals("") ||
771        !inFileName.endsWith(".pdf"))
772      this.inFileName_ = DEFAULT_INPUT_FILENAME;
773    else
774      this.inFileName_ = inFileName;
775  }
776
777
778  /**
779   * Getter.
780   **/
781  public String getInFileName()
782  {
783    return inFileName_;
784  }
785
786
787  /**
788   * Setter.
789   **/
790  public void setOutFileName()
791  {
792    setOutFileName(null);
793  }
794
795
796  /**
797   * Setter.
798   **/
799  public void setOutFileName(String outFileName)
800  {
801    if (outFileName == null || outFileName.equals(""))
802      this.outFileName_ = this.inFileName_.substring(
803                              0, this.inFileName_.length() - 4) + "-Crop.pdf";
804    else
805      this.outFileName_ = outFileName;
806  }
807
808
809  /**
810   * Getter.
811   **/
812  public String getOutFileName()
813  {
814    return outFileName_;
815  }
816
817
818}
819