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/TwoUpPDF.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 ca.bc.webarts.tools.NativeAppLauncher;
030
031import com.itextpdf.text.*;
032import com.itextpdf.text.pdf.*;
033
034import java.io.*;
035
036
037/**
038 *  A Utility class to perform specific manipulation to a PDF document.
039 *  It Crops the pages a specified ammount and then places the pages in a
040 *  Two Up format on a page.<br><br>It Can be called with a single
041 * parameter specifying the PDF file to manipulate.<br> or it can be called
042 * with two paramters: the 1st is the amount to crop off each page and the
043 * 2nd is the filename.<br>
044 * <br>
045 * Examples:<br>
046 * java ca.bc.webarts.tools.TwoUpPDF myFile.pdf<br>
047 * or<br>
048 * java ca.bc.webarts.tools.TwoUpPDF 0.75 myFile.pdf<br><br>
049 * If you need unequal cropping on each side of your page, use the
050 * ca.bc.webarts.tools.CropPDF class in this package. It provides additional
051 * functioanlity that can be used to create an intermidiate PDF file that
052 * can then be sent to this class fro Two Uping.
053 * <p><b>It does it all in Java (no native code calls) and its FREE!</b></p>
054 * <br>All PDF document handling is done using the freely available iText PDF
055 * Java library -
056 * <a href="http://www.lowagie.com/iText/">http://www.lowagie.com/iText/</a>
057 * <hr><br>License: GNU GPL<pre>
058 * This program is free software; you can redistribute it and/or modify it
059 * under the terms of the GNU General Public License as published by the Free
060 * Software Foundation; version 2 of the License.
061 *
062 * This program is distributed in the hope that it will be useful, but WITHOUT
063 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
064 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
065 * more details.
066 *
067 * You should have received a copy of the GNU General Public License along
068 * with this program; if not, write to the Free Software Foundation, Inc.,
069 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.</pre>
070 *
071 * @author    TGutwin
072 */
073public class TwoUpPDF
074{
075
076  /**  Description of the Field */
077  private static String outFileName_ = "2upOut.pdf";
078  /**  Description of the Field */
079  private static String inFileName_ = "infile.pdf";
080  /**  Description of the Field */
081  private static float cropBorder_ = 0.0f;
082  /**  The amount to scale a page so it will fit 2Up on the page */
083  private static float scale_ = (11.0f / 2.0f) / (8.5f - (2.0f * cropBorder_));//0.90f;
084  /**  The offset amount to get it placed from the origin of the page.*/
085  private static int offset_ = ((new Float(62.0f * cropBorder_)).intValue());
086  /**  Description of the Field */
087  private static double rotation_ = Math.toRadians(0);
088  /**  Description of the Field */
089  private static Rectangle outputPageSize_ = PageSize.LETTER;//PageSize.A4;
090  /**  Flags if acroreader is auto launched after this process. */
091  private static boolean launchAcroread_ = false;
092  /**  Flag for double sided printing binding offset on the long side of the page */
093  private static boolean bindingOffset_ = false;
094  /**
095    * The amount to offset the long side of the page if a double sided print
096    * page offset is requested. (default is 0.75 inches)
097   **/
098  private static float topOffsetAmount_ = ((new Float(62.0f * 0.5f)).intValue());
099  private static String acroCmd_ = "/home/tgutwin/bin/acro";
100  /**  Description of the Field */
101  private static String usage_ = "SYNTAX: TwoUpPDF [-bindingOffset] [pageCropBorderSize] PDFFilename\n" +
102      "        where pageCropBorderSize is in inches";
103
104
105  /**
106   *  The main program for the TwoUpPDF class.<br>It Can be called with a single
107   * parameter specifying the PDF file to manipulate.<br> or it can be called
108   * with two paramters: the 1st is the amount to crop off each page and the
109   * 2nd is the filename.<br>
110   * <br>
111   * Examples:<br>
112   * java ca.bc.webarts.tools.TwoUpPDF myFile.pdf<br>
113   * or<br>
114   * java ca.bc.webarts.tools.TwoUpPDF 0.75 myFile.pdf<br>
115   * @param  args  The command line arguments
116   */
117  public static void main(String[] args)
118  {
119    // Only the filename was spec'd
120    if (args.length == 1 && args[0].endsWith(".pdf"))
121    {
122      inFileName_ = args[0];
123      outFileName_ = args[0].substring(0, args[0].length() - 4) + "-2Up.pdf";
124    }
125    // Crop Border and the Filename
126    else if (args.length == 2 &&
127        args[1].endsWith(".pdf"))
128    {
129      try
130      {
131        cropBorder_ = Float.parseFloat(args[0]);
132        offset_ = ((new Float(62.0f * cropBorder_)).intValue());
133        inFileName_ = args[1];
134        outFileName_ = args[1].substring(0, args[1].length() - 4) + "-2Up.pdf";
135      }
136      catch (NumberFormatException numEx)
137      {
138        System.out.println("Page Border Crop Size error: ");
139        System.out.println(usage_);
140        System.exit(1);
141      }
142    }
143    // specify that a double sided printing binding is wanted.
144    else if (args.length == 3 &&
145        args[2].endsWith(".pdf"))
146    {
147      try
148      {
149        bindingOffset_ = true;
150        cropBorder_ = Float.parseFloat(args[1]);
151        offset_ = ((new Float(62.0f * cropBorder_)).intValue());
152        inFileName_ = args[2];
153        outFileName_ = args[2].substring(0, args[2].length() - 4) + "-2Up.pdf";
154      }
155      catch (NumberFormatException numEx)
156      {
157        System.out.println("Page Border Crop Size error: ");
158        System.out.println(usage_);
159        System.exit(1);
160      }
161    }
162    else
163    {
164      System.out.println("Filename error: ");
165      System.out.println(usage_);
166      System.exit(1);
167    }
168
169    try
170    {
171      // we create a reader for a certain document
172      System.out.println("Reading: " + inFileName_);
173      PdfReader reader = new PdfReader(inFileName_);
174
175      // we retrieve the total number of pages
176      int numPages = reader.getNumberOfPages();
177      System.out.println("Preformating a " + numPages +
178          " page PDF file for 2Up display. (scale=" + scale_ + "  offset=" + offset_ + ")");
179
180      float width = outputPageSize_.getWidth();
181      float height = outputPageSize_.getHeight();
182
183      // step 1: creation of a document-object
184      Document document =
185          new Document(
186          new Rectangle(height, width), // these are reversed to get a landscape page
187      0, 0, 0, 20);// Margins: left, right,top, bottom
188
189      // step 2: we create a writer that listens to the document
190      System.out.println("Writing: " + outFileName_);
191      FileOutputStream outFileStream = new FileOutputStream(outFileName_);
192      PdfWriter writer = PdfWriter.getInstance(document, outFileStream);
193
194      // step 3: we open the document
195      document.open();
196
197      // step 4: calculate the new scaling / layout postion
198      PdfContentByte cb = writer.getDirectContent();
199      System.out.println("There are " + numPages +
200          " pages in the document.");
201      PdfImportedPage leftPage;
202      PdfImportedPage rightPage;
203      int currentPage = 0;
204
205      /*
206       *  step 5: Start Copying the pages to the new Doc (in 2Up format)
207       */
208      /*
209       *  ******* ******************************************************
210       */
211      System.out.println("Page offset= -" +offset_);
212      boolean currentPageIsOffset = false;
213      while (currentPage < numPages)
214      {
215        document.newPage();
216        leftPage = writer.getImportedPage(reader, ++currentPage);
217        // Calculate the Transfor parameters to use given the scale and postion
218        scale_ = (height / 2.0f) / (leftPage.getWidth() - (2.0f * cropBorder_));
219        float cosRotation = (new Float(Math.cos(rotation_))).floatValue();
220        float sinRotation = (new Float(Math.sin(rotation_))).floatValue();
221        float a = scale_ * cosRotation;
222        float b = scale_ * sinRotation;
223        float c = -1.0f * scale_ * sinRotation;
224        float d = scale_ * cosRotation;
225        //System.out.println("a= " +a);
226        //System.out.println("b= " +b);
227        //System.out.println("c= " +c);
228        //System.out.println("d= " +d);
229
230        if (bindingOffset_ && currentPageIsOffset)
231        {
232          cb.addTemplate(leftPage, a, b, c, d, -offset_,
233            (topOffsetAmount_-offset_));
234        }
235        else
236        {
237          cb.addTemplate(leftPage, a, b, c, d, -offset_, -offset_);
238        }
239
240        rightPage = null;
241        if (currentPage < numPages)
242        {
243          rightPage = writer.getImportedPage(reader, ++currentPage);
244          if (bindingOffset_ && currentPageIsOffset)
245          {
246            System.out.println("binding offset= -" +(offset_+topOffsetAmount_));
247            cb.addTemplate(rightPage, a, b, c, d, (height / 2) - offset_,
248                           (topOffsetAmount_-offset_));
249          }
250          else
251          {
252            cb.addTemplate(rightPage, a, b, c, d, (height / 2) - offset_, -offset_);
253          }
254        }
255        currentPageIsOffset = !currentPageIsOffset;
256
257        // Draw a faint page seperator line between the 2Up pages
258        cb.setRGBColorStroke(200, 200, 2000);
259        cb.moveTo(height / 2, 0);
260        cb.lineTo(height / 2, width);
261        cb.stroke();
262      }
263
264      // step 6: we close the document
265      document.close();
266
267      // optional launch the acroreader
268      if (launchAcroread_)
269      {
270        System.out.println("Launching acroread " + outFileName_);
271        String[] appArgs = {(new File(outFileName_)).getAbsolutePath()};
272        new NativeAppLauncher(acroCmd_, appArgs);
273      }
274    }
275    catch (Exception de)
276    {
277      de.printStackTrace();
278    }
279  }
280}
281