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