001 package railo.runtime.tag; 002 003 import java.awt.Color; 004 import java.awt.image.BufferedImage; 005 import java.io.ByteArrayInputStream; 006 import java.io.ByteArrayOutputStream; 007 import java.io.IOException; 008 import java.io.OutputStream; 009 import java.security.InvalidParameterException; 010 import java.util.ArrayList; 011 import java.util.HashMap; 012 import java.util.Iterator; 013 import java.util.Set; 014 015 import org.apache.oro.text.regex.MalformedPatternException; 016 import org.pdfbox.exceptions.CryptographyException; 017 import org.pdfbox.exceptions.InvalidPasswordException; 018 019 import railo.commons.io.IOUtil; 020 import railo.commons.io.res.Resource; 021 import railo.commons.io.res.filter.ExtensionResourceFilter; 022 import railo.commons.io.res.filter.ResourceFilter; 023 import railo.commons.io.res.util.ResourceUtil; 024 import railo.commons.io.res.util.WildCardFilter; 025 import railo.commons.lang.StringUtil; 026 import railo.runtime.exp.ApplicationException; 027 import railo.runtime.exp.CasterException; 028 import railo.runtime.exp.ExpressionException; 029 import railo.runtime.exp.PageException; 030 import railo.runtime.ext.tag.BodyTagImpl; 031 import railo.runtime.op.Caster; 032 import railo.runtime.op.Decision; 033 import railo.runtime.text.pdf.PDFDocument; 034 import railo.runtime.text.pdf.PDFUtil; 035 import railo.runtime.type.Array; 036 import railo.runtime.type.Collection.Key; 037 import railo.runtime.type.List; 038 import railo.runtime.type.Struct; 039 040 import com.lowagie.text.DocumentException; 041 import com.lowagie.text.Image; 042 import com.lowagie.text.pdf.PdfContentByte; 043 import com.lowagie.text.pdf.PdfGState; 044 import com.lowagie.text.pdf.PdfImportedPage; 045 import com.lowagie.text.pdf.PdfReader; 046 import com.lowagie.text.pdf.PdfStamper; 047 import com.lowagie.text.pdf.PdfWriter; 048 import com.lowagie.text.pdf.SimpleBookmark; 049 050 public class PDF extends BodyTagImpl { 051 052 private static final int ACTION_ADD_WATERMARK = 0; 053 private static final int ACTION_DELETE_PAGES = 1; 054 private static final int ACTION_GET_INFO = 2; 055 private static final int ACTION_MERGE = 3; 056 private static final int ACTION_PROCESSDDX = 5; 057 private static final int ACTION_PROTECT = 5; 058 private static final int ACTION_READ = 6; 059 private static final int ACTION_REMOVE_WATERMARK = 7; 060 private static final int ACTION_SET_INFO = 8; 061 private static final int ACTION_THUMBNAIL = 9; 062 private static final int ACTION_WRITE = 10; 063 private static final int ACTION_EXTRACT_TEXT = 11; 064 065 066 private static final String FORMAT_JPG ="jpg"; 067 private static final String FORMAT_TIFF = "tiff"; 068 private static final String FORMAT_PNG = "png"; 069 070 private static final int ORDER_TIME = 0; 071 private static final int ORDER_NAME = 1; 072 073 private static final int RESOLUTION_HIGH = 0; 074 private static final int RESOLUTION_LOW = 1; 075 076 private static final int SAVE_OPTION_FULL = 0; 077 private static final int SAVE_OPTION_INCREMENTAL = 1; 078 private static final int SAVE_OPTION_LINEAR = 2; 079 080 private static final int TYPE_STRING = 1; 081 private static final int TYPE_XML = 2; 082 083 084 private static final ExtensionResourceFilter PDF_FILTER = new ExtensionResourceFilter("pdf"); 085 private static final int UNDEFINED = Integer.MIN_VALUE; 086 087 088 089 private int action=ACTION_PROCESSDDX; 090 private boolean ascending =false; 091 private Object copyFrom=null; 092 private String ddxFile=null; 093 private Resource destination=null; 094 private Resource directory=null; 095 private int encrypt=PDFUtil.ENCRYPT_RC4_128; 096 private boolean flatten=false; 097 private boolean foreground=false; 098 private String format=FORMAT_JPG; 099 private Object image=null; 100 private Struct info=null; 101 private Struct inputFiles=null; 102 private Struct outputFiles=null; 103 private boolean isBase64=false; 104 private boolean keepBookmark=false; 105 private String name=null; 106 private String newOwnerPassword=null; 107 private String newUserPassword=null; 108 private float opacity=0.3F; 109 private int order=ORDER_TIME; 110 private boolean overwrite=false; 111 private String pages=null; 112 private String password=null; 113 private int permissions=0; 114 private String position=null; 115 private int resolution=RESOLUTION_HIGH; 116 private float rotation=0; 117 private int saveOption=SAVE_OPTION_FULL; 118 private int scale=25; 119 private boolean showOnPrint=false; 120 private Object source =null; 121 private boolean stopOnError=false; 122 private boolean transparent=false; 123 private char version=0; 124 private java.util.List<PDFParamBean> params; 125 private ResourceFilter filter=null; 126 private String imagePrefix=null; 127 private int type=TYPE_XML; 128 129 /** 130 * @see railo.runtime.ext.tag.BodyTagImpl#release() 131 */ 132 public void release() { 133 super.release(); 134 action=ACTION_PROCESSDDX; 135 ascending =false; 136 copyFrom=null; 137 ddxFile=null; 138 destination=null; 139 directory=null; 140 encrypt=PDFUtil.ENCRYPT_RC4_128; 141 flatten=false; 142 foreground=false; 143 format=FORMAT_JPG; 144 image=null; 145 info=null; 146 inputFiles=null; 147 outputFiles=null; 148 isBase64=false; 149 keepBookmark=false; 150 name=null; 151 newOwnerPassword=null; 152 newUserPassword=null; 153 opacity=0.3F; 154 order=ORDER_TIME; 155 overwrite=false; 156 pages=null; 157 password=null; 158 permissions=0; 159 position=null; 160 resolution=RESOLUTION_HIGH; 161 rotation=0; 162 saveOption=SAVE_OPTION_FULL; 163 scale=25; 164 showOnPrint=false; 165 source =null; 166 stopOnError=false; 167 transparent=false; 168 version=0; 169 params=null; 170 filter=null; 171 imagePrefix=null; 172 type=TYPE_XML; 173 } 174 175 176 177 /** 178 * @param imagePrefix the imagePrefix to set 179 */ 180 public void setImageprefix(String imagePrefix) { 181 this.imagePrefix = imagePrefix; 182 } 183 184 185 186 /** 187 * @param action the action to set 188 * @throws ApplicationException 189 */ 190 public void setAction(String strAction) throws ApplicationException { 191 192 strAction=StringUtil.toLowerCase(strAction.trim()); 193 if("addwatermark".equals(strAction)) action=ACTION_ADD_WATERMARK; 194 else if("add-watermark".equals(strAction)) action=ACTION_ADD_WATERMARK; 195 else if("add_watermark".equals(strAction)) action=ACTION_ADD_WATERMARK; 196 else if("deletepages".equals(strAction)) action=ACTION_DELETE_PAGES; 197 else if("delete-pages".equals(strAction)) action=ACTION_DELETE_PAGES; 198 else if("delete_pages".equals(strAction)) action=ACTION_DELETE_PAGES; 199 else if("deletepage".equals(strAction)) action=ACTION_DELETE_PAGES; 200 else if("delete-page".equals(strAction)) action=ACTION_DELETE_PAGES; 201 else if("delete_page".equals(strAction)) action=ACTION_DELETE_PAGES; 202 else if("getinfo".equals(strAction)) action=ACTION_GET_INFO; 203 else if("get-info".equals(strAction)) action=ACTION_GET_INFO; 204 else if("get_info".equals(strAction)) action=ACTION_GET_INFO; 205 else if("merge".equals(strAction)) action=ACTION_MERGE; 206 //else if("processddx".equals(strAction)) action=ACTION_PROCESSDDX; 207 //else if("process-ddx".equals(strAction)) action=ACTION_PROCESSDDX; 208 //else if("process_ddx".equals(strAction)) action=ACTION_PROCESSDDX; 209 else if("protect".equals(strAction)) action=ACTION_PROTECT; 210 else if("read".equals(strAction)) action=ACTION_READ; 211 else if("removewatermark".equals(strAction)) action=ACTION_REMOVE_WATERMARK; 212 else if("removewater-mark".equals(strAction)) action=ACTION_REMOVE_WATERMARK; 213 else if("removewater_mark".equals(strAction)) action=ACTION_REMOVE_WATERMARK; 214 else if("setinfo".equals(strAction)) action=ACTION_SET_INFO; 215 else if("set-info".equals(strAction)) action=ACTION_SET_INFO; 216 else if("set_info".equals(strAction)) action=ACTION_SET_INFO; 217 else if("thumbnail".equals(strAction)) action=ACTION_THUMBNAIL; 218 else if("write".equals(strAction)) action=ACTION_WRITE; 219 else if("extracttext".equals(strAction)) action=ACTION_EXTRACT_TEXT; 220 else if("extract-text".equals(strAction)) action=ACTION_EXTRACT_TEXT; 221 else if("extract_text".equals(strAction)) action=ACTION_EXTRACT_TEXT; 222 223 else throw new ApplicationException("invalid action definition ["+strAction+"], valid actions definitions are " + 224 "[addWatermark,deletePages,getInfo,merge,protect,read,removeWatermark,setInfo,thumbnail,write]"); 225 226 } 227 228 229 public void setType(String strType) throws ApplicationException { 230 231 strType=StringUtil.toLowerCase(strType.trim()); 232 if("string".equals(strType)) type=TYPE_STRING; 233 else if("text".equals(strType)) type=TYPE_STRING; 234 else if("plain".equals(strType)) type=TYPE_STRING; 235 else if("xml".equals(strType)) type=TYPE_XML; 236 237 else throw new ApplicationException("invalid type definition ["+strType+"], valid type definitions are " + 238 "[string,xml]"); 239 240 } 241 242 /** 243 * sets a filter pattern 244 * @param pattern 245 * @throws PageException 246 **/ 247 public void setFilter(String pattern) throws PageException { 248 if(pattern.trim().length()>0) { 249 try { 250 this.filter=new WildCardFilter(pattern); 251 } 252 catch (MalformedPatternException e) { 253 throw Caster.toPageException(e); 254 } 255 } 256 } 257 258 /** 259 * @param ascending the ascending to set 260 */ 261 public void setAscending(boolean ascending) { 262 this.ascending = ascending; 263 } 264 /** 265 * @param copyFrom the copyFrom to set 266 * @throws ExpressionException 267 */ 268 public void setCopyfrom(Object copyFrom) throws ExpressionException { 269 this.copyFrom = copyFrom;//ResourceUtil.toResourceExisting(pageContext, copyFrom); 270 } 271 /** 272 * @param ddxFile the ddxFile to set 273 */ 274 public void setDdxfile(String ddxFile) { 275 this.ddxFile = ddxFile;// MUST 276 } 277 /** 278 * @param destination the destination to set 279 */ 280 public void setDestination(String destination) { 281 this.destination = ResourceUtil.toResourceNotExisting(pageContext, destination); 282 } 283 /** 284 * @param directory the directory to set 285 * @throws ExpressionException 286 */ 287 public void setDirectory(String directory) throws ExpressionException { 288 this.directory = ResourceUtil.toResourceExisting(pageContext, directory); 289 } 290 /** 291 * @param encrypt the encrypt to set 292 * @throws ApplicationException 293 */ 294 public void setEncrypt(String strEncrypt) throws ApplicationException { 295 296 strEncrypt=StringUtil.toLowerCase(strEncrypt.trim()); 297 if("aes128".equals(strEncrypt)) encrypt=PDFUtil.ENCRYPT_AES_128; 298 else if("aes-128".equals(strEncrypt)) encrypt=PDFUtil.ENCRYPT_AES_128; 299 else if("aes_128".equals(strEncrypt)) encrypt=PDFUtil.ENCRYPT_AES_128; 300 else if("none".equals(strEncrypt)) encrypt=PDFUtil.ENCRYPT_NONE; 301 else if("".equals(strEncrypt)) encrypt=PDFUtil.ENCRYPT_NONE; 302 else if("rc4128".equals(strEncrypt)) encrypt=PDFUtil.ENCRYPT_RC4_128; 303 else if("rc4-128".equals(strEncrypt)) encrypt=PDFUtil.ENCRYPT_RC4_128; 304 else if("rc4_128".equals(strEncrypt)) encrypt=PDFUtil.ENCRYPT_RC4_128; 305 else if("rc4128m".equals(strEncrypt)) encrypt=PDFUtil.ENCRYPT_RC4_128M; 306 else if("rc4-128m".equals(strEncrypt)) encrypt=PDFUtil.ENCRYPT_RC4_128M; 307 else if("rc4_128m".equals(strEncrypt)) encrypt=PDFUtil.ENCRYPT_RC4_128M; 308 else if("rc440".equals(strEncrypt)) encrypt=PDFUtil.ENCRYPT_RC4_40; 309 else if("rc4-40".equals(strEncrypt)) encrypt=PDFUtil.ENCRYPT_RC4_40; 310 else if("rc4_40".equals(strEncrypt)) encrypt=PDFUtil.ENCRYPT_RC4_40; 311 312 313 else throw new ApplicationException("invalid encrypt definition ["+strEncrypt+"], valid encrypt definitions are " + 314 "[aes_128,none,rc4_128,rc4_128m,rc4_40]"); 315 } 316 /** 317 * @param flatten the flatten to set 318 */ 319 public void setFlatten(boolean flatten) { 320 this.flatten = flatten; 321 } 322 /** 323 * @param foreground the foreground to set 324 */ 325 public void setForeground(boolean foreground) { 326 this.foreground = foreground; 327 } 328 /** 329 * @param format the format to set 330 * @throws ApplicationException 331 */ 332 public void setFormat(String strFormat) throws ApplicationException { 333 strFormat=StringUtil.toLowerCase(strFormat.trim()); 334 if("jpg".equals(strFormat)) format=FORMAT_JPG; 335 else if("jpeg".equals(strFormat)) format=FORMAT_JPG; 336 else if("jpe".equals(strFormat)) format=FORMAT_JPG; 337 else if("tiff".equals(strFormat)) format=FORMAT_TIFF; 338 else if("tif".equals(strFormat)) format=FORMAT_TIFF; 339 else if("png".equals(strFormat)) format=FORMAT_PNG; 340 341 else throw new ApplicationException("invalid format definition ["+strFormat+"], valid format definitions are " + 342 "[jpg,tiff,png]"); 343 } 344 /** 345 * @param image the image to set 346 */ 347 public void setImage(Object image) { 348 this.image = image;// MUST 349 } 350 /** 351 * @param prefix the prefix to set 352 */ 353 public void setPrefix(String prefix) { 354 this.imagePrefix = prefix; 355 } 356 /** 357 * @param info the info to set 358 */ 359 public void setInfo(Struct info) { 360 this.info = info; 361 } 362 /** 363 * @param inputFiles the inputFiles to set 364 */ 365 public void setInputfiles(Struct inputFiles) { 366 this.inputFiles = inputFiles; 367 } 368 /** 369 * @param outputFiles the outputFiles to set 370 */ 371 public void setOutputfiles(Struct outputFiles) { 372 this.outputFiles = outputFiles; 373 } 374 /** 375 * @param isBase64 the isBase64 to set 376 */ 377 public void setIsbase64(boolean isBase64) { 378 this.isBase64 = isBase64; 379 } 380 /** 381 * @param keepBookmark the keepBookmark to set 382 */ 383 public void setKeepbookmark(boolean keepBookmark) { 384 this.keepBookmark = keepBookmark; 385 } 386 /** 387 * @param name the name to set 388 */ 389 public void setName(String name) { 390 this.name = name; 391 } 392 /** 393 * @param newOwnerPassword the newOwnerPassword to set 394 */ 395 public void setNewownerpassword(String newOwnerPassword) { 396 this.newOwnerPassword = newOwnerPassword; 397 } 398 /** 399 * @param newUserPassword the newUserPassword to set 400 */ 401 public void setNewuserpassword(String newUserPassword) { 402 this.newUserPassword = newUserPassword; 403 } 404 /** 405 * @param opacity the opacity to set 406 * @throws ApplicationException 407 */ 408 public void setOpacity(double opacity) throws ApplicationException { 409 if(opacity<0 || opacity>10) 410 throw new ApplicationException("invalid opacity definition ["+Caster.toString(opacity)+"], value should be in range from 0 to 10"); 411 this.opacity = (float) (opacity/10); 412 } 413 /** 414 * @param order the order to set 415 * @throws ApplicationException 416 */ 417 public void setOrder(String strOrder) throws ApplicationException { 418 strOrder=StringUtil.toLowerCase(strOrder.trim()); 419 if("name".equals(strOrder)) order=ORDER_NAME; 420 else if("time".equals(strOrder)) order=ORDER_TIME; 421 422 else throw new ApplicationException("invalid order definition ["+strOrder+"], valid order definitions are " + 423 "[name,time]"); 424 } 425 /** 426 * @param overwrite the overwrite to set 427 */ 428 public void setOverwrite(boolean overwrite) { 429 this.overwrite = overwrite; 430 } 431 /** 432 * @param pages the pages to set 433 */ 434 public void setPages(String pages) { 435 this.pages = pages; 436 } 437 /** 438 * @param password the password to set 439 */ 440 public void setPassword(String password) { 441 this.password = password; 442 } 443 /** 444 * @param permissions the permissions to set 445 * @throws PageException 446 */ 447 public void setPermissions(String strPermissions) throws PageException { 448 permissions=PDFUtil.toPermissions(strPermissions); 449 } 450 /** 451 * @param position the position to set 452 */ 453 public void setPosition(String position) { 454 this.position = position;//MUST 455 } 456 /** 457 * @param resolution the resolution to set 458 * @throws ApplicationException 459 */ 460 public void setResolution(String strResolution) throws ApplicationException { 461 strResolution=StringUtil.toLowerCase(strResolution.trim()); 462 if("low".equals(strResolution)) resolution=RESOLUTION_LOW; 463 else if("high".equals(strResolution)) resolution=RESOLUTION_HIGH; 464 465 else throw new ApplicationException("invalid resolution definition ["+strResolution+"], valid resolution definitions are " + 466 "[low,high]"); 467 } 468 469 /** 470 * @param rotation the rotation to set 471 */ 472 public void setRotation(double rotation) { 473 rotation=rotation%360D; 474 //rotation=rotation/360*6.28318525; 475 476 this.rotation = (float) rotation; 477 } 478 479 /** 480 * @param saveOption the saveOption to set 481 * @throws ApplicationException 482 */ 483 public void setSaveoption(String strSaveOption) throws ApplicationException { 484 strSaveOption=StringUtil.toLowerCase(strSaveOption.trim()); 485 if("full".equals(strSaveOption)) saveOption=SAVE_OPTION_FULL; 486 else if("incremental".equals(strSaveOption))saveOption=SAVE_OPTION_INCREMENTAL; 487 else if("linear".equals(strSaveOption)) saveOption=SAVE_OPTION_LINEAR; 488 489 else throw new ApplicationException("invalid saveOption definition ["+strSaveOption+"], valid saveOption definitions are " + 490 "[full,linear,incremental]"); 491 } 492 493 /** 494 * @param scale the scale to set 495 * @throws ApplicationException 496 */ 497 public void setScale(double scale) throws ApplicationException { 498 //if(scale<1 || scale>1000) this check is now done inside PDF2IMage implementation 499 // throw new ApplicationException("invalid scale definition ["+Caster.toString(scale)+"], value should be in range from 1 to 100"); 500 this.scale = (int) scale; 501 } 502 503 /** 504 * @param showOnPrint the showOnPrint to set 505 */ 506 public void setShowonprint(boolean showOnPrint) { 507 this.showOnPrint = showOnPrint; 508 } 509 510 /** 511 * @param source the source to set 512 */ 513 public void setSource(Object source) { 514 this.source = source; 515 } 516 /** 517 * @param stopOnError the stopOnError to set 518 */ 519 public void setStoponerror(boolean stopOnError) { 520 this.stopOnError = stopOnError; 521 } 522 /** 523 * @param transparent the transparent to set 524 */ 525 public void setTransparent(boolean transparent) { 526 this.transparent = transparent; 527 } 528 /** 529 * @param version the version to set 530 * @throws ApplicationException 531 */ 532 public void setVersion(double version) throws ApplicationException { 533 if(1.1 == version) this.version='1'; 534 else if(1.2 == version) this.version=PdfWriter.VERSION_1_2; 535 else if(1.3 == version) this.version=PdfWriter.VERSION_1_3; 536 else if(1.4 == version) this.version=PdfWriter.VERSION_1_4; 537 else if(1.5 == version) this.version=PdfWriter.VERSION_1_5; 538 else if(1.6 == version) this.version=PdfWriter.VERSION_1_6; 539 540 else throw new ApplicationException("invalid version definition ["+Caster.toString(version)+"], valid version definitions are " + 541 "[1.1, 1.2, 1.3, 1.4, 1.5, 1.6]"); 542 } 543 544 /** 545 * @throws PageException 546 * @see javax.servlet.jsp.tagext.Tag#doStartTag() 547 */ 548 public int doStartTag() throws PageException { 549 // RR SerialNumber sn = pageContext.getConfig().getSerialNumber(); 550 //if(sn.getVersion()==SerialNumber.VERSION_COMMUNITY) 551 // throw new SecurityException("no access to this functionality with the "+sn.getStringVersion()+" version of railo"); 552 553 return EVAL_BODY_BUFFERED; 554 } 555 556 /** 557 * @see javax.servlet.jsp.tagext.BodyTag#doInitBody() 558 */ 559 public void doInitBody() { 560 561 } 562 563 /** 564 * @see javax.servlet.jsp.tagext.BodyTag#doAfterBody() 565 */ 566 public int doAfterBody() { 567 return SKIP_BODY; 568 } 569 570 /** 571 * 572 * @throws IOException 573 * @throws InvalidParameterException 574 * @see railo.runtime.ext.tag.TagImpl#doEndTag() 575 */ 576 public int doEndTag() throws PageException { 577 try { 578 579 if(ACTION_ADD_WATERMARK==action) doActionAddWatermark(); 580 else if(ACTION_REMOVE_WATERMARK==action) doActionRemoveWatermark(); 581 else if(ACTION_READ==action) doActionRead(); 582 else if(ACTION_WRITE==action) doActionWrite(); 583 else if(ACTION_GET_INFO==action) doActionGetInfo(); 584 else if(ACTION_SET_INFO==action) doActionSetInfo(); 585 else if(ACTION_MERGE==action) doActionMerge(); 586 else if(ACTION_DELETE_PAGES==action) doActionDeletePages(); 587 else if(ACTION_PROTECT==action) doActionProtect(); 588 else if(ACTION_THUMBNAIL==action) doActionThumbnail(); 589 else if(ACTION_EXTRACT_TEXT==action) { 590 if(true)throw new ApplicationException("not supported yet, see https://issues.jboss.org/browse/RAILO-1559"); 591 doActionExtractText(); 592 } 593 594 //else if(ACTION_PROCESSDDX==action) throw new ApplicationException("action [processddx] not supported"); 595 596 597 598 } 599 catch (Exception e) { 600 throw Caster.toPageException(e); 601 } 602 return EVAL_PAGE; 603 } 604 605 606 607 608 private void doActionWrite() throws PageException, IOException, DocumentException { 609 required("pdf", "write", "source", source); 610 required("pdf", "write", "destination", destination); 611 612 if(destination.exists() && !overwrite) 613 throw new ApplicationException("destination file ["+destination+"] already exists"); 614 615 PDFDocument doc = toPDFDocument(source, password, null); 616 //PdfReader pr = doc.getPdfReader(); 617 // output 618 boolean destIsSource = doc.getResource()!=null && destination.equals(doc.getResource()); 619 620 OutputStream os=null; 621 if(destIsSource){ 622 os=new ByteArrayOutputStream(); 623 } 624 else if(destination!=null) { 625 os=destination.getOutputStream(); 626 } 627 628 try { 629 PDFUtil.concat(new PDFDocument[]{doc}, os, true, true, true,version); 630 } 631 finally { 632 IOUtil.closeEL(os); 633 if(os instanceof ByteArrayOutputStream) { 634 if(destination!=null)IOUtil.copy(new ByteArrayInputStream(((ByteArrayOutputStream)os).toByteArray()), destination,true);// MUST overwrite 635 } 636 } 637 638 /* 639 // flatten = "yes|no" 640 // must saveOption = "linear|incremental|full" 641 */ 642 } 643 644 645 646 private void doActionThumbnail() throws PageException, IOException, DocumentException { 647 required("pdf", "thumbnail", "source", source); 648 649 PDFDocument doc = toPDFDocument(source, password, null); 650 PdfReader pr = doc.getPdfReader(); 651 boolean isEnc=pr.isEncrypted(); 652 pr.close(); 653 if(isEnc) { 654 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 655 //PDFUtil.concat(new PDFDocument[]{doc}, baos, true, true, true, (char)0); 656 PDFUtil.encrypt(doc,baos,null,null,0,PDFUtil.ENCRYPT_NONE); 657 baos.close(); 658 doc = new PDFDocument(baos.toByteArray(),doc.getResource(),null); 659 } 660 661 doc.setPages(pages); 662 663 // scale 664 if(scale<1) 665 throw new ApplicationException("value of attribute scale ["+scale+"] should be at least 1"); 666 667 // destination 668 if(destination==null) 669 destination=ResourceUtil.toResourceNotExisting(pageContext, "thumbnails"); 670 671 // imagePrefix 672 if(imagePrefix==null){ 673 Resource res = doc.getResource(); 674 if(res!=null){ 675 String n = res.getName(); 676 int index=n.lastIndexOf('.'); 677 if(index!=-1)imagePrefix=n.substring(0,index); 678 else imagePrefix=n; 679 } 680 else imagePrefix="memory"; 681 } 682 683 // MUST password 684 PDFUtil.writeImages(doc.getRaw(), doc.getPages(), destination, imagePrefix, format, scale,overwrite, 685 resolution==RESOLUTION_HIGH,transparent); 686 687 688 689 } 690 691 692 693 694 private void doActionAddWatermark() throws PageException, IOException, DocumentException { 695 required("pdf", "addWatermark", "source", source); 696 if(copyFrom==null && image==null) 697 throw new ApplicationException("at least one of the following attributes must be defined " + 698 "[copyFrom,image]"); 699 700 if(destination!=null && destination.exists() && !overwrite) 701 throw new ApplicationException("destination file ["+destination+"] already exists"); 702 703 704 // image 705 Image img=null; 706 if(image!=null) { 707 railo.runtime.img.Image ri = railo.runtime.img.Image.createImage(pageContext,image,false,false,true); 708 img=Image.getInstance(ri.getBufferedImage(),null,false); 709 } 710 // copy From 711 else { 712 byte[] barr; 713 try{ 714 Resource res = Caster.toResource(pageContext, copyFrom, true); 715 barr=IOUtil.toBytes(res); 716 } 717 catch(ExpressionException ee) { 718 barr=Caster.toBinary(copyFrom); 719 } 720 img=Image.getInstance(PDFUtil.toImage(barr, 1).getBufferedImage(),null,false); 721 722 } 723 724 // position 725 float x=UNDEFINED,y=UNDEFINED; 726 if(!StringUtil.isEmpty(position)) { 727 int index=position.indexOf(','); 728 if(index==-1) 729 throw new ApplicationException("attribute [position] has a invalid value ["+position+"]," + 730 "value should follow one of the following pattern [40,50], [40,] or [,50]"); 731 String strX = position.substring(0,index).trim(); 732 String strY = position.substring(index+1).trim(); 733 if(!StringUtil.isEmpty(strX))x = Caster.toIntValue(strX); 734 if(!StringUtil.isEmpty(strY))y = Caster.toIntValue(strY); 735 736 } 737 738 739 PDFDocument doc = toPDFDocument(source, password, null); 740 doc.setPages(pages); 741 PdfReader reader = doc.getPdfReader(); 742 reader.consolidateNamedDestinations(); 743 boolean destIsSource = destination!=null && doc.getResource()!=null && destination.equals(doc.getResource()); 744 java.util.List bookmarks = SimpleBookmark.getBookmark(reader); 745 ArrayList master = new ArrayList(); 746 if(bookmarks!=null)master.addAll(bookmarks); 747 748 // output 749 OutputStream os=null; 750 if(!StringUtil.isEmpty(name) || destIsSource){ 751 os=new ByteArrayOutputStream(); 752 } 753 else if(destination!=null) { 754 os=destination.getOutputStream(); 755 } 756 757 try { 758 759 int len = reader.getNumberOfPages(); 760 PdfStamper stamp = new PdfStamper(reader, os); 761 762 if(len >0){ 763 if(x==UNDEFINED || y==UNDEFINED) { 764 PdfImportedPage first = stamp.getImportedPage(reader, 1); 765 if(y==UNDEFINED)y=(first.getHeight()-img.getHeight())/2; 766 if(x==UNDEFINED)x=(first.getWidth()-img.getWidth())/2; 767 } 768 img.setAbsolutePosition(x, y); 769 //img.setAlignment(Image.ALIGN_JUSTIFIED); ration geht nicht anhand mitte 770 771 } 772 773 // rotation 774 if(rotation!=0) { 775 img.setRotationDegrees(rotation); 776 } 777 778 Set _pages = doc.getPages(); 779 for (int i=1;i<=len;i++) { 780 if(_pages!=null && !_pages.contains(Integer.valueOf(i))) continue; 781 PdfContentByte cb =foreground? stamp.getOverContent(i):stamp.getUnderContent(i); 782 PdfGState gs1 = new PdfGState(); 783 //print.out("op:"+opacity); 784 gs1.setFillOpacity(opacity); 785 //gs1.setStrokeOpacity(opacity); 786 cb.setGState(gs1); 787 cb.addImage(img); 788 } 789 if(bookmarks!=null)stamp.setOutlines(master); 790 stamp.close(); 791 } 792 finally { 793 IOUtil.closeEL(os); 794 if(os instanceof ByteArrayOutputStream) { 795 if(destination!=null)IOUtil.copy(new ByteArrayInputStream(((ByteArrayOutputStream)os).toByteArray()), destination,true);// MUST overwrite 796 if(!StringUtil.isEmpty(name)){ 797 pageContext.setVariable(name,new PDFDocument(((ByteArrayOutputStream)os).toByteArray(),password)); 798 } 799 } 800 } 801 } 802 803 private void doActionRemoveWatermark() throws PageException, IOException, DocumentException { 804 required("pdf", "removeWatermark", "source", source); 805 806 if(destination!=null && destination.exists() && !overwrite) 807 throw new ApplicationException("destination file ["+destination+"] already exists"); 808 809 railo.runtime.img.Image ri = new railo.runtime.img.Image(1,1,BufferedImage.TYPE_INT_RGB,Color.BLACK); 810 Image img = Image.getInstance(ri.getBufferedImage(),null,false); 811 img.setAbsolutePosition(1,1); 812 813 814 PDFDocument doc = toPDFDocument(source, password, null); 815 doc.setPages(pages); 816 PdfReader reader = doc.getPdfReader(); 817 818 boolean destIsSource = destination!=null && doc.getResource()!=null && destination.equals(doc.getResource()); 819 java.util.List bookmarks = SimpleBookmark.getBookmark(reader); 820 ArrayList master = new ArrayList(); 821 if(bookmarks!=null)master.addAll(bookmarks); 822 823 // output 824 OutputStream os=null; 825 if(!StringUtil.isEmpty(name) || destIsSource){ 826 os=new ByteArrayOutputStream(); 827 } 828 else if(destination!=null) { 829 os=destination.getOutputStream(); 830 } 831 832 try { 833 int len = reader.getNumberOfPages(); 834 PdfStamper stamp = new PdfStamper(reader, os); 835 836 Set _pages = doc.getPages(); 837 for (int i=1;i<=len;i++) { 838 if(_pages!=null && !_pages.contains(Integer.valueOf(i))) continue; 839 PdfContentByte cb =foreground? stamp.getOverContent(i):stamp.getUnderContent(i); 840 PdfGState gs1 = new PdfGState(); 841 gs1.setFillOpacity(0); 842 cb.setGState(gs1); 843 cb.addImage(img); 844 } 845 if(bookmarks!=null)stamp.setOutlines(master); 846 stamp.close(); 847 } 848 finally { 849 IOUtil.closeEL(os); 850 if(os instanceof ByteArrayOutputStream) { 851 if(destination!=null)IOUtil.copy(new ByteArrayInputStream(((ByteArrayOutputStream)os).toByteArray()), destination,true);// MUST overwrite 852 if(!StringUtil.isEmpty(name)){ 853 pageContext.setVariable(name,new PDFDocument(((ByteArrayOutputStream)os).toByteArray(),password)); 854 } 855 } 856 } 857 } 858 859 private void doActionDeletePages() throws PageException, IOException, DocumentException { 860 required("pdf", "deletePage", "pages", pages,true); 861 required("pdf", "deletePage", "source", source); 862 863 PDFDocument doc = toPDFDocument(source, password, null); 864 doc.setPages(pages); 865 866 867 if(destination==null && StringUtil.isEmpty(name)){ 868 if(doc.getResource()==null) 869 throw new ApplicationException("source is not based on a resource, destination attribute is required"); 870 destination=doc.getResource(); 871 } 872 else if(destination!=null && destination.exists() && !overwrite) 873 throw new ApplicationException("destination file ["+destination+"] already exists"); 874 875 boolean destIsSource = destination!=null && doc.getResource()!=null && destination.equals(doc.getResource()); 876 877 // output 878 OutputStream os=null; 879 if(!StringUtil.isEmpty(name) || destIsSource){ 880 os=new ByteArrayOutputStream(); 881 } 882 else if(destination!=null) { 883 os=destination.getOutputStream(); 884 } 885 886 try { 887 PDFUtil.concat(new PDFDocument[]{doc}, os, true, true,true,version); 888 } 889 finally { 890 //if(document!=null)document.close(); 891 IOUtil.closeEL(os); 892 if(os instanceof ByteArrayOutputStream) { 893 if(destination!=null)IOUtil.copy(new ByteArrayInputStream(((ByteArrayOutputStream)os).toByteArray()), destination,true);// MUST overwrite 894 if(!StringUtil.isEmpty(name)){ 895 pageContext.setVariable(name,new PDFDocument(((ByteArrayOutputStream)os).toByteArray(),password)); 896 } 897 } 898 } 899 } 900 901 902 private void doActionMerge() throws ApplicationException, PageException, IOException, DocumentException { 903 904 if(source==null && params==null && directory==null) 905 throw new ApplicationException("at least one of the following constellation must be defined" + 906 " attribute source, attribute directory or cfpdfparam child tags"); 907 if(destination==null && StringUtil.isEmpty(name,true)) 908 throw new ApplicationException("at least one of the following attributes must be defined " + 909 "[destination,name]"); 910 if(destination!=null && !overwrite) 911 throw new ApplicationException("destination file ["+destination+"] already exists"); 912 913 ArrayList docs = new ArrayList(); 914 PDFDocument doc; 915 boolean isListing=false; 916 917 // source 918 if(source!=null) { 919 if(Decision.isArray(source)) { 920 Array arr = Caster.toArray(source); 921 int len = arr.size(); 922 for(int i=1;i<=len;i++) { 923 docs.add(doc=toPDFDocument(arr.getE(i),password,null)); 924 doc.setPages(pages); 925 } 926 } 927 else if(source instanceof String) { 928 String[] sources = List.toStringArrayTrim(List.listToArrayRemoveEmpty((String)source, ',')); 929 for(int i=0;i<sources.length;i++) { 930 docs.add(doc=toPDFDocument(sources[i],password,null)); 931 doc.setPages(pages); 932 } 933 } 934 else docs.add(toPDFDocument(source,password,null)); 935 936 } 937 boolean destIsSource = false; 938 939 // params 940 if(directory!=null && !directory.isDirectory()) { 941 if(!directory.exists()) 942 throw new ApplicationException("defined attribute directory does not exist"); 943 throw new ApplicationException("defined attribute directory is not a directory"); 944 } 945 if(params!=null) { 946 Iterator it = params.iterator(); 947 PDFParamBean param; 948 while(it.hasNext()) { 949 param=(PDFParamBean) it.next(); 950 docs.add(doc=toPDFDocument(param.getSource(), param.getPassword(),directory)); 951 doc.setPages(param.getPages()); 952 } 953 } 954 else if(directory!=null) { 955 isListing=true; 956 Resource[] children = ResourceUtil.listResources(directory, filter); 957 958 if(ascending) { 959 for(int i=children.length-1;i>=0;i--) { 960 if(destination!=null && children[i].equals(destination)) destIsSource=true; 961 docs.add(doc=toPDFDocument(children[i],password,null)); 962 doc.setPages(pages); 963 } 964 } 965 else { 966 for(int i=0;i<children.length;i++) { 967 if(destination!=null && children[i].equals(destination)) destIsSource=true; 968 docs.add(doc=toPDFDocument(children[i],password,null)); 969 doc.setPages(pages); 970 } 971 } 972 973 } 974 975 int doclen=docs.size(); 976 if(doclen==0) 977 throw new ApplicationException("you have to define at leat 1 pdf file"); 978 979 // output 980 OutputStream os=null; 981 if(!StringUtil.isEmpty(name) || destIsSource){ 982 os=new ByteArrayOutputStream(); 983 } 984 else if(destination!=null) { 985 os=destination.getOutputStream(); 986 } 987 988 989 /*com.lowagie.text.Document document=null; 990 PdfCopy copy=null; 991 PdfReader pr; 992 Set pages; 993 int size;*/ 994 995 996 try { 997 if(!isListing)stopOnError=true; 998 PDFUtil.concat((PDFDocument[]) docs.toArray(new PDFDocument[docs.size()]), os, keepBookmark, false,stopOnError,version); 999 /* 1000 boolean init=false; 1001 for(int d=0;d<doclen;d++) { 1002 doc=(PDFDocument) docs.get(d); 1003 pages=doc.getPages(); 1004 try { 1005 pr=doc.getPdfReader(); 1006 print.out(pr.getCatalog().getKeys()); 1007 1008 } 1009 catch(Throwable t) { 1010 if(isListing && !stopOnError)continue; 1011 throw Caster.toPageException(t); 1012 } 1013 print.out("d+"+d); 1014 if(!init) { 1015 init=true; 1016 print.out("set"); 1017 document = new com.lowagie.text.Document(pr.getPageSizeWithRotation(1)); 1018 copy = new PdfCopy(document,os); 1019 document.open(); 1020 } 1021 size=pr.getNumberOfPages(); 1022 print.out("pages:"+size); 1023 for(int page=1;page<=size;page++) { 1024 if(pages==null || pages.contains(Constants.Integer(page))) { 1025 copy.addPage(copy.getImportedPage(pr, page)); 1026 } 1027 } 1028 }*/ 1029 } 1030 finally { 1031 //if(document!=null)document.close(); 1032 IOUtil.closeEL(os); 1033 if(os instanceof ByteArrayOutputStream) { 1034 if(destination!=null)IOUtil.copy(new ByteArrayInputStream(((ByteArrayOutputStream)os).toByteArray()), destination,true);// MUST overwrite 1035 if(!StringUtil.isEmpty(name))pageContext.setVariable(name, ((ByteArrayOutputStream)os).toByteArray()); 1036 } 1037 1038 } 1039 1040 1041 } 1042 private void doActionRead() throws PageException, IOException { 1043 required("pdf", "read", "name", name,true); 1044 required("pdf", "read", "source", source); 1045 1046 pageContext.setVariable(name, toPDFDocument(source,password,null)); 1047 } 1048 1049 private void doActionProtect() throws PageException, IOException, DocumentException { 1050 required("pdf", "protect", "source", source); 1051 1052 if(StringUtil.isEmpty(newUserPassword) && StringUtil.isEmpty(newOwnerPassword)) 1053 throw new ApplicationException("at least one of the following attributes must be defined [newUserPassword,newOwnerPassword]"); 1054 1055 1056 PDFDocument doc = toPDFDocument(source,password,null); 1057 1058 if(destination==null){ 1059 destination=doc.getResource(); 1060 if(destination==null) 1061 throw new ApplicationException("source is not based on a resource, destination file is required"); 1062 } 1063 else if(destination.exists() && !overwrite) 1064 throw new ApplicationException("destination file ["+destination+"] already exists"); 1065 1066 boolean destIsSource = doc.getResource()!=null && destination.equals(doc.getResource()); 1067 1068 // output 1069 OutputStream os=null; 1070 if(destIsSource){ 1071 os=new ByteArrayOutputStream(); 1072 } 1073 else { 1074 os=destination.getOutputStream(); 1075 } 1076 1077 1078 try{ 1079 PDFUtil.encrypt(doc,os,newUserPassword,newOwnerPassword,permissions,encrypt); 1080 } 1081 finally { 1082 IOUtil.closeEL(os); 1083 if(os instanceof ByteArrayOutputStream) { 1084 IOUtil.copy(new ByteArrayInputStream(((ByteArrayOutputStream)os).toByteArray()), destination,true);// MUST overwrite 1085 } 1086 1087 } 1088 1089 } 1090 1091 1092 1093 1094 private void doActionSetInfo() throws PageException, IOException, DocumentException { 1095 required("pdf", "setInfo", "info", info); 1096 required("pdf", "getInfo", "source", source); 1097 1098 PDFDocument doc = toPDFDocument(source,password,null); 1099 PdfReader pr = doc.getPdfReader(); 1100 OutputStream os=null; 1101 try { 1102 if(destination==null){ 1103 if(doc.getResource()==null) 1104 throw new ApplicationException("source is not based on a resource, destination file is required"); 1105 destination=doc.getResource(); 1106 } 1107 else if(destination.exists() && !overwrite) 1108 throw new ApplicationException("destination file ["+destination+"] already exists"); 1109 1110 PdfStamper stamp = new PdfStamper(pr, os=destination.getOutputStream()); 1111 HashMap moreInfo = new HashMap(); 1112 1113 Key[] keys = info.keys(); 1114 for(int i=0;i<keys.length;i++) { 1115 moreInfo.put(StringUtil.ucFirst(keys[i].getLowerString()), Caster.toString(info.get(keys[i]))); 1116 } 1117 // author 1118 Object value = info.get("author", null); 1119 if(value!=null)moreInfo.put("Author", Caster.toString(value)); 1120 // keywords 1121 value = info.get("keywords", null); 1122 if(value!=null)moreInfo.put("Keywords", Caster.toString(value)); 1123 // title 1124 value = info.get("title", null); 1125 if(value!=null)moreInfo.put("Title", Caster.toString(value)); 1126 // subject 1127 value = info.get("subject", null); 1128 if(value!=null)moreInfo.put("Subject", Caster.toString(value)); 1129 // creator 1130 value = info.get("creator", null); 1131 if(value!=null)moreInfo.put("Creator", Caster.toString(value)); 1132 // trapped 1133 value = info.get("Trapped", null); 1134 if(value!=null)moreInfo.put("Trapped", Caster.toString(value)); 1135 // Created 1136 value = info.get("Created", null); 1137 if(value!=null)moreInfo.put("Created", Caster.toString(value)); 1138 // Language 1139 value = info.get("Language", null); 1140 if(value!=null)moreInfo.put("Language", Caster.toString(value)); 1141 1142 1143 stamp.setMoreInfo(moreInfo); 1144 stamp.close(); 1145 1146 } 1147 finally { 1148 IOUtil.closeEL(os); 1149 pr.close(); 1150 } 1151 } 1152 1153 1154 private void doActionGetInfo() throws PageException, IOException { 1155 required("pdf", "getInfo", "name", name,true); 1156 required("pdf", "getInfo", "source", source); 1157 1158 PDFDocument doc = toPDFDocument(source,password,null); 1159 pageContext.setVariable(name, doc.getInfo()); 1160 1161 } 1162 private void doActionExtractText() throws PageException, IOException, CryptographyException, InvalidPasswordException { 1163 required("pdf", "extractText", "name", name,true); 1164 1165 PDFDocument doc = toPDFDocument(source,password,null); 1166 doc.setPages(pages); 1167 1168 pageContext.setVariable(name, PDFUtil.extractText(doc,doc.getPages())); 1169 /* 1170 <cfpdf 1171 required 1172 action="extracttext" <!---extract all the words in the PDF.---> 1173 ***source= "absolute or relative path of the PDF file|PDF document variable| 1174 cfdocument variable" 1175 pages = "*" <!----page numbers from where the text needs to be extracted from the 1176 PDF document---> 1177 1178 optional 1179 addquads = "add the position or quadrants for the text in the PDF" 1180 honourspaces = "true|false" 1181 overwrite = "true" <!---Overwrite the specified object in the PDF document---> 1182 ***password = "" <!--- PDF document password---> 1183 type = "string|xml" <!---format in which the text needs to be extracted---> 1184 one of the following: 1185 destination = "PDF output file pathname" 1186 name = "PDF document variable" 1187 usestructure = "true|false" 1188 * */ 1189 } 1190 1191 private Object allowed(boolean encrypted, int permissions, int permission) { 1192 return (!encrypted || (permissions&permission)>0)?"Allowed":"Not Allowed"; 1193 } 1194 1195 1196 private PDFDocument toPDFDocument(Object source,String password, Resource directory) throws PageException, IOException { 1197 1198 if(source instanceof PDFDocument) 1199 return (PDFDocument)source; 1200 if(Decision.isBinary(source)){ 1201 return new PDFDocument(Caster.toBinary(source),password); 1202 } 1203 if(source instanceof Resource){ 1204 return new PDFDocument((Resource) source,password); 1205 } 1206 if(source instanceof String){ 1207 if(directory!=null) { 1208 Resource res = directory.getRealResource((String)source); 1209 if(!res.isFile()){ 1210 Resource res2 = ResourceUtil.toResourceNotExisting(pageContext, (String)source); 1211 if(res2.isFile()) 1212 res=res2; 1213 else 1214 throw new ExpressionException("file or directory "+res+" not exist"); 1215 } 1216 return new PDFDocument(res,password); 1217 } 1218 return new PDFDocument(ResourceUtil.toResourceExisting(pageContext, (String)source),password); 1219 } 1220 1221 throw new CasterException(source,PdfReader.class); 1222 } 1223 1224 /*private byte[] toBinary(Object source) throws ExpressionException, IOException { 1225 1226 if(source instanceof PDFDocument) 1227 return toBinary(((PDFDocument)source).getResource()); 1228 if(Decision.isBinary(source)){ 1229 return Caster.toBinary(source); 1230 } 1231 if(source instanceof Resource){ 1232 return IOUtil.toBytes((Resource)source); 1233 } 1234 if(source instanceof String){ 1235 if(directory!=null) { 1236 Resource res = directory.getRealResource((String)source); 1237 if(!res.isFile()){ 1238 Resource res2 = ResourceUtil.toResourceNotExisting(pageContext, (String)source); 1239 if(res2.isFile()) 1240 res=res2; 1241 else 1242 throw new ExpressionException("file or directory "+res+" not exist"); 1243 } 1244 return IOUtil.toBytes(res); 1245 } 1246 return IOUtil.toBytes(ResourceUtil.toResourceExisting(pageContext, (String)source)); 1247 } 1248 1249 throw new CasterException(source,PdfReader.class); 1250 }*/ 1251 1252 protected void setParam(PDFParamBean param) { 1253 if(params==null) 1254 params=new ArrayList<PDFParamBean>(); 1255 params.add(param); 1256 } 1257 1258 1259 1260 1261 }