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