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