001/* 002 * ConcatPDF.java 003 * $URL: svn://svn.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/tools/pdf/ConcatPDF.java $ 004 * $REVISION: $ 005 * $Date: 2019-04-18 18:30:08 -0700 (Thu, 18 Apr 2019) $ 006 * Copyright (c) 2003 Tom B. Gutwin P.Eng. 007 * 008 * This program is free software; you can redistribute it and/or 009 * modify it under the terms of the GNU General Public License 010 * as published by the Free Software Foundation; either version 2 011 * of the License, or any later version. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, write to the Free Software 020 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 021 */ 022package ca.bc.webarts.tools.pdf; 023 024import java.io.File; 025 026import com.itextpdf.text.*; 027import com.itextpdf.text.pdf.*; 028 029import java.io.*; 030 031 032/** 033 * This is a simple application class that concatenates pdf files. 034 * <br>It has 3 modes of operation.<br><ol> 035 * <li>DEFAULT operation simply concat's files one file after the other.</li> 036 * <li>Merge one file into another at a specified page.</li> 037 * <li>Concat ALL files in a given directory.</li></ol> 038 * <br><b><u>Syntax for basic concatenation (one file after the other) into 039 * a NEW PDF file:</u> 040 * </b><br> 041 * <pre>java ca.bc.webarts.tools.ConcatPDF destfile file1 file2 [file3 ...]</pre> 042 * <br><br><b><u>Syntax for 'insert into' concatenation (one into the other at the 043 * spec'd page) into a NEW PDF file:</u></b><br> 044 * <pre>java ca.bc.webarts.tools.ConcatPDF destfile file1 file2 insertPageNum</pre> 045 * <br><br><b><u>Syntax for directory concatenation (all files in a dir 046 * concat'd into one NEW file):</u></b><br> 047 * <pre>java ca.bc.webarts.tools.ConcatPDF destfile directoryName</pre> 048 * 049 * @author TGutwin 050 */ 051public class ConcatPDF 052{ 053 /** 054 * An optional page number at which to insert the the 2nd file into the first. 055 * For example if this field is 14, the 2nd PDF file will be concat'd into 056 * the first starting at page 14.<br>The resultant file will have 13 pages of 057 * the 1st PDF, all of the 2nd PDF and then the rest of the 1st PDF. 058 **/ 059 private int insertPage_ = 0; 060 061 private static String eol_ = System.getProperty("line.separator"); 062 063 064 /** 065 * The simple usage help message.. 066 **/ 067 private static final String usage_ = 068 "This is a simple application class that concatenates pdf files."+ 069 eol_+ 070 "It has 3 modes of operation:"+eol_+ 071 " 1) DEFAULT operation simply concat's files one file after the other."+ 072 eol_+ 073 " 2) Merge 2nd file into 1st at a specified page."+eol_+ 074 " 3) Concat ALL files in a given directory."+eol_+ 075 eol_+ 076 "Syntax for basic concatenation (one file after the other) into"+ 077 "a NEW PDF file:"+eol_+ 078 " java ca.bc.webarts.tools.ConcatPDF destfile file1 file2 [file3 ...]"+ 079 eol_+eol_+ 080 "Syntax for 'insert into' concatenation (one into the other at the"+ 081 "spec'd page) into a NEW PDF file:"+eol_+ 082 " java ca.bc.webarts.tools.ConcatPDF destfile insertIntoFile insertedFile insertPageNum"+ 083 eol_+eol_+ 084 "Syntax for directory concatenation (all files in a dir concat'd into "+ 085 "one NEW file):"+eol_+ 086 " java ca.bc.webarts.tools.ConcatPDF destfile directoryName"; 087 088 089 /** 090 * A basic empty constructor for ConcatPDF class that does nothing but 091 * instantiate the class. 092 */ 093 public ConcatPDF() 094 { 095 // the worker methods will have to be called on the instantiated object. 096 } 097 098 099 /** 100 * A constructor for ConcatPDF class that takes a array of filenames and 101 * concatenates them all into the targetPDF file. 102 * 103 * @param targetPdf The resultant target PDF file after concatenation. 104 * @param filenames the pdf files to concat. 105 */ 106 public ConcatPDF(String targetPdf, String[] filenames) 107 { 108 concatFiles(targetPdf, filenames); 109 } 110 111 112 /** 113 * A constructor for ConcatPDF class that takes a directory and 114 * concatenates ALL the pdf files into the targetPDF file. 115 * 116 * @param targetPdf The resultant target PDF file after concatenation. 117 * @param pdfDirectory A directory to search for PDFs. 118 */ 119 public ConcatPDF(String targetPdf, String pdfDirectory) 120 { 121 concatFilesFromDirectory(targetPdf, pdfDirectory, false); 122 } 123 124 125 /** 126 * A constructor for ConcatPDF class that does 'insert into' concatenation 127 * (one into the other at the spec'd page) into a NEW PDF file. 128 * 129 * @param targetPdf The resultant target PDF file after concatenation. 130 * @param first The 1st PDF file for concatenation. 131 * @param second The 2nd PDF file for concatenation. 132 * @param insertIntoPage the page number to insert the 2nd PDF at.. 133 */ 134 public ConcatPDF(String targetPdf, String first, String second, int insertIntoPage) 135 { 136 insertPage_ = insertIntoPage; 137 insertConcat(targetPdf, first, second, insertIntoPage); 138 } 139 140 141 /** 142 * A helper method to do the DEFAULT concating of individual files one 143 * after the other. 144 * 145 * @param targetPdf The resultant target PDF file after concatenation. 146 * @param filenames the pdf files to concat. 147 */ 148 public boolean concatFiles(String targetPdf, String[] filenames) 149 { 150 151 boolean retVal = true; 152 // first test if all the files are available for readings 153 File tmpFile = null; 154 boolean fileFailure = false; 155 String errorMsg = ""; 156 for (int i=0; !fileFailure && i<filenames.length; i++) 157 { 158 try 159 { 160 tmpFile = new File(filenames[i]); 161 if (!tmpFile.exists() || !tmpFile.canRead()) 162 { 163 fileFailure = true; 164 errorMsg = "Cannot read file: "+filenames[i]; 165 } 166 } 167 catch (NullPointerException npe) 168 { 169 fileFailure = true; 170 errorMsg = "File ERROR encountered for file: "+filenames[i]; 171 } 172 } 173 174 // On with the concat 175 if (!fileFailure) 176 { 177 try 178 { 179 int i = 0; 180 PdfReader pdfreader = new PdfReader(filenames[i]); 181 int numPagesInCurrPdf = pdfreader.getNumberOfPages(); 182 System.out.println("There are " + numPagesInCurrPdf 183 + " pages in "+filenames[i]); 184 Document document 185 = new Document(pdfreader.getPageSizeWithRotation(1)); 186 187 // Open the target PDF file for writing 188 PdfWriter pdfwriter 189 = PdfWriter.getInstance(document, 190 new FileOutputStream(targetPdf)); 191 document.open(); 192 PdfContentByte pdfcontentbyte = pdfwriter.getDirectContent(); 193 194 // loop through the files 195 while (i < filenames.length) 196 { 197 int currPageNum = 0; 198 199 // Loop through all the pages in the current file to merge in 200 while (currPageNum < numPagesInCurrPdf) 201 { 202 currPageNum++; 203 document.setPageSize 204 (pdfreader.getPageSizeWithRotation(currPageNum)); 205 document.newPage(); 206 207 // get the page to merge in 208 PdfImportedPage pdfimportedpage 209 = pdfwriter.getImportedPage(pdfreader, currPageNum); 210 int currPageRotationDegrees = pdfreader.getPageRotation(currPageNum); 211 212 // merge it in 213 if (currPageRotationDegrees == 90 || currPageRotationDegrees == 270) 214 { 215 pdfcontentbyte.addTemplate( 216 pdfimportedpage, 0.0F, -1.0F, 1.0F, 0.0F, 0.0F, 217 pdfreader.getPageSizeWithRotation(currPageNum).getHeight()); 218 } 219 else 220 { 221 pdfcontentbyte.addTemplate( 222 pdfimportedpage, 1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 223 0.0F); 224 } 225 //System.out.println("Processed page " + currPageNum); 226 } // more pages? 227 228 // Is there more files to do? Then increment and do anaother file 229 if (++i < filenames.length) 230 { 231 pdfreader = new PdfReader(filenames[i]); 232 numPagesInCurrPdf = pdfreader.getNumberOfPages(); 233 System.out.println("There are " + numPagesInCurrPdf 234 + " pages in "+filenames[i]); 235 } 236 } 237 238 // Done All files 239 document.close(); 240 } 241 catch (Exception exception) 242 { 243 retVal = false; 244 System.err.println(exception.getClass().getName() + ": " 245 + exception.getMessage()); 246 } 247 } 248 else 249 { 250 retVal = false; 251 System.out.println(errorMsg); 252 } 253 return retVal; 254 } 255 256 257 /** 258 * Set Method for class field 'insertPage_'. 259 * 260 * @param insertPage is the value to set this class field to. 261 * 262 **/ 263 public void setInsertPage(int insertPage) 264 { 265 this.insertPage_ = insertPage; 266 } // setInsertPage Method 267 268 269 /** 270 * Get Method for class field 'insertPage_'. 271 * 272 * @return int - The value the class field 'insertPage_'. 273 * 274 **/ 275 public int getInsertPage() 276 { 277 return insertPage_; 278 } // getInsertPage Method 279 280 281 /** Prints the Class usage to System.out.**/ 282 public static void printUsage() 283 { 284 System.out.println(usage_); 285 } 286 287 288 /** 289 * A helper method to take a directory name, search it for PDF files and then 290 * concatenate them into the targetPdf. 291 * 292 * @param targetPdf The resultant target PDF file after concatenation. 293 * @param pdfDirectory A directory to search for PDFs. 294 * @param recurse recurse into the sub-dirs (NOT IMPLEMENTED YET). 295 */ 296 public boolean concatFilesFromDirectory(String targetPdf, 297 String pdfDirectory, 298 boolean recurse) 299 { 300 // this method simply reads the dir for the pdf files and then calls the 301 // concatFiles(String targetPdf, String[] filenames) method. 302 boolean retVal = true; 303 String [] filesToConcat = null; 304 int concatFilesCount = 0; 305 File dirFile = new File(pdfDirectory); 306 if (dirFile != null && dirFile.isDirectory()) 307 { 308 String srcFileNames[] = dirFile.list(); 309 if (srcFileNames != null) 310 { 311 filesToConcat = new String[countPdfsInDir(pdfDirectory)]; 312 for (int i = 0; i < srcFileNames.length; i++) 313 { 314 File currFile = new File(srcFileNames[i]); 315 if (currFile != null && 316 !currFile.isDirectory() && 317 isThisAPdfFile(currFile)) 318 { 319 filesToConcat[concatFilesCount++] = currFile.getAbsolutePath(); 320 } 321 } 322 323 // Now send this array off to the helper method 324 retVal = concatFiles(targetPdf, filesToConcat); 325 } 326 } 327 else 328 { 329 retVal = false; 330 System.out.println( 331 "Commandline parameter specified is NOT a directoryname"); 332 } 333 334 return retVal; 335 } 336 337 338 /** 339 * A helper method to concats the 2nd PDF into the 1st at the spec'd page 340 * into the targetPdf. 341 * 342 * @param targetPdf The resultant target PDF file after concatenation. 343 * @param firstPDF The first PDF to concat the second into. 344 * @param secondPDF The PDF to insert into the first at the spec'd page. 345 * @param insertPage the page to insert the 2nd file at. 346 */ 347 public boolean insertConcat(String targetPdf, 348 String firstPDF, 349 String secondPDF, 350 int insertPage) 351 { 352 boolean retVal = true; 353 // first test if all the files are available for readings 354 File tmpFile = null; 355 boolean fileFailure = false; 356 String errorMsg = ""; 357 try 358 { 359 tmpFile = new File(firstPDF); 360 if (!tmpFile.exists() || !tmpFile.canRead()) 361 { 362 fileFailure = true; 363 errorMsg = "Cannot read file: "+firstPDF; 364 } 365 tmpFile = new File(secondPDF); 366 if (!tmpFile.exists() || !tmpFile.canRead()) 367 { 368 fileFailure = true; 369 errorMsg = "Cannot read file: "+secondPDF; 370 } 371 } 372 catch (NullPointerException npe) 373 { 374 fileFailure = true; 375 errorMsg = "File ERROR encountered for file: " + 376 tmpFile.getAbsolutePath(); 377 } 378 379 // On with the concat 380 if (!fileFailure) 381 { 382 try 383 { 384 PdfReader firstPpdfreader = new PdfReader(firstPDF); 385 PdfReader secondPpdfreader = new PdfReader(secondPDF); 386 PdfReader pdfreader = firstPpdfreader; 387 int numPagesInFirstPdf = firstPpdfreader.getNumberOfPages(); 388 int numPagesInSecondPdf = secondPpdfreader.getNumberOfPages(); 389 System.out.println("There are " + numPagesInFirstPdf 390 + " pages in "+firstPDF); 391 System.out.println("There are " + numPagesInSecondPdf 392 + " pages in "+secondPDF); 393 Document document 394 = new Document(firstPpdfreader.getPageSizeWithRotation(1)); 395 396 // Open the target PDF file for writing 397 PdfWriter pdfwriter 398 = PdfWriter.getInstance(document, 399 new FileOutputStream(targetPdf)); 400 document.open(); 401 PdfContentByte pdfcontentbyte = pdfwriter.getDirectContent(); 402 403 int currPageNum1 = 1; 404 int currPageNum2 = 1; 405 int currPageNumResult = 1; 406 float currPageSizeWithRotation; 407 int currPageRotationDegrees =0; 408 PdfImportedPage pdfImportedpage = null; 409 // Loop through all the pages in the FIRST file 410 while (currPageNumResult <= numPagesInFirstPdf+numPagesInSecondPdf) 411 { 412 document.setPageSize 413 (firstPpdfreader.getPageSizeWithRotation(currPageNum1)); 414 document.newPage(); 415 416 // get the page to merge in 417 if (currPageNumResult < insertPage || 418 currPageNumResult >= insertPage + numPagesInSecondPdf) 419 { 420 // then just get the first file page 421 System.out.println(currPageNumResult +"> Insert " + currPageNum1 + " from 1st PDF."); 422 pdfreader = firstPpdfreader; 423 pdfImportedpage 424 = pdfwriter.getImportedPage(pdfreader, currPageNum1); 425 currPageRotationDegrees = pdfreader.getPageRotation(currPageNum1); 426 currPageSizeWithRotation = 427 pdfreader.getPageSizeWithRotation(currPageNum1).getHeight(); 428 currPageNum1++; 429 } 430 else 431 { 432 // insert a 2nd file page 433 System.out.println(currPageNumResult +"> Insert " + currPageNum2 + " from 2nd PDF."); 434 pdfreader = secondPpdfreader; 435 pdfImportedpage 436 = pdfwriter.getImportedPage(pdfreader, currPageNum2); 437 currPageRotationDegrees = pdfreader.getPageRotation(currPageNum2); 438 currPageSizeWithRotation = 439 pdfreader.getPageSizeWithRotation(currPageNum2).getHeight(); 440 441 // increment the page counter and continue with the rest of the 1st PDF 442 currPageNum2++; 443 } 444 445 // merge it in 446 if (currPageRotationDegrees == 90 || currPageRotationDegrees == 270) 447 { 448 pdfcontentbyte.addTemplate( 449 pdfImportedpage, 0.0F, -1.0F, 1.0F, 0.0F, 0.0F, 450 currPageSizeWithRotation); 451 } 452 else 453 { 454 pdfcontentbyte.addTemplate( 455 pdfImportedpage, 1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 456 0.0F); 457 } 458 459 // increment the meged PDF page counter and keep going 460 //System.out.println(" Processed page " + currPageNumResult); 461 currPageNumResult++; 462 } // more pages? 463 464 465 // Done All files 466 document.close(); 467 } 468 catch (Exception exception) 469 { 470 retVal = false; 471 System.err.println(exception.getClass().getName() + ": " 472 + exception.getMessage()); 473 } 474 } 475 else 476 { 477 retVal = false; 478 System.out.println(errorMsg); 479 } 480 481 return retVal; 482 } 483 484 485 /** 486 *Counts the PDFs in a given dir. 487 @return the number of PDF files in the spec'd directory 488 **/ 489 public int countPdfsInDir(String pdfDirectory) 490 { 491 File dirFile = new File(pdfDirectory); 492 int filesCount = 0; 493 if (dirFile != null && dirFile.isDirectory()) 494 { 495 String srcFileNames[] = dirFile.list(); 496 if (srcFileNames != null) 497 { 498 for (int i = 0; i < srcFileNames.length; i++) 499 { 500 File currFile = new File(srcFileNames[i]); 501 if (currFile != null && 502 !currFile.isDirectory() && 503 isThisAPdfFile(currFile)) 504 { 505 filesCount++; 506 } 507 } 508 } 509 } 510 return filesCount; 511 } 512 513 514 /** Checks if the passef File is a PDF file (ie has a pdf extension). 515 **/ 516 protected boolean isThisAPdfFile(File fileToCheck) 517 { 518 boolean retVal = false; 519 520 try 521 { 522 String filename = fileToCheck.getAbsolutePath(); 523 if (filename.trim().toLowerCase().endsWith(".pdf")) 524 { 525 retVal = true; 526 } 527 } 528 catch (Exception exception) 529 { 530 retVal = false; 531 System.err.println(exception.getClass().getName() + ": " 532 + exception.getMessage()); 533 } 534 535 return retVal; 536 } 537 538 539 /** 540 * The main program for the ConcatPDF class. <br> 541 * <br><b><u>Syntax for basic concatenation (one file after the other) into 542 * a NEW PDF file:</u> 543 * </b><br> 544 * <pre>java ca.bc.webarts.tools.ConcatPDF destfile file1 file2 [file3 ...]</pre> 545 * <br><br><b><u>Syntax for 'insert into' concatenation (one into the other at the 546 * spec'd page) into a NEW PDF file:</u></b><br> 547 * <pre>java ca.bc.webarts.tools.ConcatPDF destfile file1 file2 insertPageNum</pre> 548 * <br><br><b><u>Syntax for directory concatenation (all files in a dir 549 * concat'd into one NEW file):</u></b><br> 550 * <pre>java ca.bc.webarts.tools.ConcatPDF destfile directoryName</pre> 551 * 552 * @param args The command line arguments 553 */ 554 public static void main(String[] args) 555 { 556 if (args.length < 2) 557 { 558 // WRONG 559 printUsage(); 560 } 561 else if (args.length < 3) 562 { 563 // the concat dir mode 564 new ConcatPDF(args[0], args[1]); 565 } 566 else // more than 2 args were passed 567 { 568 // this can be DEFAULT concat or insert into concat mode 569 try 570 { 571 int insertPage = Integer.parseInt(args[3]); 572 // if we get here without exception then INSERT concat mode 573 new ConcatPDF(args[0], args[1], args[2], insertPage); 574 } 575 catch (java.lang.NumberFormatException numFormatEx) 576 { 577 String [] cFilenames = new String [args.length-1]; 578 for (int i = 1; i <args.length; i++) 579 cFilenames[i-1] = args[i]; 580 new ConcatPDF(args[0], cFilenames); 581 } 582 } 583 } 584} 585 586