001 package railo.runtime.text.pdf; 002 003 import java.io.IOException; 004 import java.io.OutputStream; 005 import java.io.StringWriter; 006 import java.util.ArrayList; 007 import java.util.HashMap; 008 import java.util.HashSet; 009 import java.util.List; 010 import java.util.Map; 011 import java.util.Set; 012 013 import org.pdfbox.exceptions.CryptographyException; 014 import org.pdfbox.exceptions.InvalidPasswordException; 015 import org.pdfbox.pdmodel.PDDocument; 016 import org.pdfbox.util.PDFText2HTML; 017 018 import railo.commons.io.IOUtil; 019 import railo.commons.io.res.Resource; 020 import railo.commons.lang.StringUtil; 021 import railo.runtime.PageContext; 022 import railo.runtime.exp.ApplicationException; 023 import railo.runtime.exp.CasterException; 024 import railo.runtime.exp.PageException; 025 import railo.runtime.img.Image; 026 import railo.runtime.op.Caster; 027 import railo.runtime.op.Constants; 028 import railo.runtime.op.Decision; 029 030 import com.lowagie.text.Document; 031 import com.lowagie.text.DocumentException; 032 import com.lowagie.text.pdf.PRAcroForm; 033 import com.lowagie.text.pdf.PdfCopy; 034 import com.lowagie.text.pdf.PdfImportedPage; 035 import com.lowagie.text.pdf.PdfReader; 036 import com.lowagie.text.pdf.PdfWriter; 037 import com.lowagie.text.pdf.SimpleBookmark; 038 039 public class PDFUtil { 040 041 042 public static final int ENCRYPT_RC4_40 = PdfWriter.STANDARD_ENCRYPTION_40; 043 public static final int ENCRYPT_RC4_128 = PdfWriter.STANDARD_ENCRYPTION_128; 044 public static final int ENCRYPT_RC4_128M = PdfWriter.STANDARD_ENCRYPTION_128; 045 public static final int ENCRYPT_AES_128 = PdfWriter.ENCRYPTION_AES_128; 046 public static final int ENCRYPT_NONE = -1; 047 048 private static final int PERMISSION_ALL = 049 PdfWriter.ALLOW_ASSEMBLY+ 050 PdfWriter.ALLOW_COPY+ 051 PdfWriter.ALLOW_DEGRADED_PRINTING+ 052 PdfWriter.ALLOW_FILL_IN+ 053 PdfWriter.ALLOW_MODIFY_ANNOTATIONS+ 054 PdfWriter.ALLOW_MODIFY_CONTENTS+ 055 PdfWriter.ALLOW_PRINTING+ 056 PdfWriter.ALLOW_SCREENREADERS+PdfWriter.ALLOW_COPY;// muss 2 mal sein, keine ahnung wieso 057 058 /** 059 * convert a string list of permission 060 * @param strPermissions 061 * @return 062 * @throws PageException 063 */ 064 public static int toPermissions(String strPermissions) throws PageException { 065 if(strPermissions==null) return 0; 066 int permissions=0; 067 strPermissions=strPermissions.trim(); 068 069 String[] arr = railo.runtime.type.util.ListUtil.toStringArray(railo.runtime.type.util.ListUtil.listToArrayRemoveEmpty(strPermissions, ',')); 070 for(int i=0;i<arr.length;i++) { 071 permissions=add(permissions,toPermission(arr[i])); 072 } 073 return permissions; 074 } 075 076 /** 077 * convert a string defintion of a permision in a integer Constant (PdfWriter.ALLOW_XXX) 078 * @param strPermission 079 * @return 080 * @throws ApplicationException 081 */ 082 public static int toPermission(String strPermission) throws ApplicationException { 083 strPermission=strPermission.trim().toLowerCase(); 084 if("allowassembly".equals(strPermission)) return PdfWriter.ALLOW_ASSEMBLY; 085 else if("none".equals(strPermission)) return 0; 086 else if("all".equals(strPermission)) return PERMISSION_ALL; 087 else if("assembly".equals(strPermission)) return PdfWriter.ALLOW_ASSEMBLY; 088 else if("documentassembly".equals(strPermission)) return PdfWriter.ALLOW_ASSEMBLY; 089 else if("allowdegradedprinting".equals(strPermission)) return PdfWriter.ALLOW_DEGRADED_PRINTING; 090 else if("degradedprinting".equals(strPermission)) return PdfWriter.ALLOW_DEGRADED_PRINTING; 091 else if("printing".equals(strPermission)) return PdfWriter.ALLOW_DEGRADED_PRINTING; 092 else if("allowfillin".equals(strPermission)) return PdfWriter.ALLOW_FILL_IN; 093 else if("fillin".equals(strPermission)) return PdfWriter.ALLOW_FILL_IN; 094 else if("fillingform".equals(strPermission)) return PdfWriter.ALLOW_FILL_IN; 095 else if("allowmodifyannotations".equals(strPermission)) return PdfWriter.ALLOW_MODIFY_ANNOTATIONS; 096 else if("modifyannotations".equals(strPermission)) return PdfWriter.ALLOW_MODIFY_ANNOTATIONS; 097 else if("allowmodifycontents".equals(strPermission)) return PdfWriter.ALLOW_MODIFY_CONTENTS; 098 else if("modifycontents".equals(strPermission)) return PdfWriter.ALLOW_MODIFY_CONTENTS; 099 else if("allowcopy".equals(strPermission)) return PdfWriter.ALLOW_COPY; 100 else if("copy".equals(strPermission)) return PdfWriter.ALLOW_COPY; 101 else if("copycontent".equals(strPermission)) return PdfWriter.ALLOW_COPY; 102 else if("allowprinting".equals(strPermission)) return PdfWriter.ALLOW_PRINTING; 103 else if("printing".equals(strPermission)) return PdfWriter.ALLOW_PRINTING; 104 else if("allowscreenreaders".equals(strPermission)) return PdfWriter.ALLOW_SCREENREADERS; 105 else if("screenreaders".equals(strPermission)) return PdfWriter.ALLOW_SCREENREADERS; 106 107 else throw new ApplicationException("invalid permission ["+strPermission+"], valid permission values are [AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations, AllowFillIn, AllowScreenReaders, AllowAssembly, AllowDegradedPrinting]"); 108 } 109 110 111 private static int add(int permissions, int permission) { 112 if(permission==0 || (permissions&permission)>0)return permissions; 113 return permissions+permission; 114 } 115 116 117 /** 118 * @param docs 119 * @param os 120 * @param removePages if true, pages defined in PDFDocument will be removed, otherwise all other pages will be removed 121 * @param version 122 * @throws PageException 123 * @throws IOException 124 * @throws DocumentException 125 */ 126 public static void concat(PDFDocument[] docs,OutputStream os, boolean keepBookmark,boolean removePages, boolean stopOnError, char version) throws PageException, IOException, DocumentException { 127 Document document = null; 128 PdfCopy writer = null; 129 PdfReader reader; 130 Set pages; 131 boolean isInit=false; 132 PdfImportedPage page; 133 try { 134 int pageOffset = 0; 135 ArrayList master = new ArrayList(); 136 137 for(int i=0;i<docs.length;i++) { 138 // we create a reader for a certain document 139 pages = docs[i].getPages(); 140 try { 141 reader = docs[i].getPdfReader(); 142 } 143 catch(Throwable t) { 144 if(!stopOnError)continue; 145 throw Caster.toPageException(t); 146 } 147 reader.consolidateNamedDestinations(); 148 149 // we retrieve the total number of pages 150 int n = reader.getNumberOfPages(); 151 List bookmarks = keepBookmark?SimpleBookmark.getBookmark(reader):null; 152 if (bookmarks != null) { 153 removeBookmarks(bookmarks,pages,removePages); 154 if (pageOffset != 0) SimpleBookmark.shiftPageNumbers(bookmarks, pageOffset, null); 155 master.addAll(bookmarks); 156 } 157 158 if (!isInit) { 159 isInit=true; 160 document = new Document(reader.getPageSizeWithRotation(1)); 161 writer = new PdfCopy(document, os); 162 163 if(version!=0)writer.setPdfVersion(version); 164 165 166 document.open(); 167 } 168 169 170 for (int y = 1; y <= n; y++) { 171 if(pages!=null && removePages==pages.contains(Integer.valueOf(y))){ 172 continue; 173 } 174 pageOffset++; 175 page = writer.getImportedPage(reader, y); 176 writer.addPage(page); 177 } 178 PRAcroForm form = reader.getAcroForm(); 179 if (form != null) 180 writer.copyAcroForm(reader); 181 } 182 if (master.size() > 0) 183 writer.setOutlines(master); 184 185 } 186 finally { 187 IOUtil.closeEL(document); 188 } 189 } 190 191 192 private static void removeBookmarks(List bookmarks,Set pages, boolean removePages) { 193 int size = bookmarks.size(); 194 for(int i=size-1;i>=0;i--) { 195 if(removeBookmarks((Map) bookmarks.get(i),pages, removePages)) 196 bookmarks.remove(i); 197 } 198 } 199 200 private static boolean removeBookmarks(Map bookmark, Set pages, boolean removePages) { 201 List kids=(List) bookmark.get("Kids"); 202 if(kids!=null)removeBookmarks(kids,pages,removePages); 203 Integer page=Caster.toInteger(railo.runtime.type.util.ListUtil.first((String) bookmark.get("Page")," ",true),Constants.INTEGER_MINUS_ONE); 204 return removePages==(pages!=null && pages.contains(page)); 205 } 206 207 public static Set parsePageDefinition(String strPages) throws PageException { 208 if(StringUtil.isEmpty(strPages)) return null; 209 HashSet<Integer> set=new HashSet<Integer>(); 210 parsePageDefinition(set, strPages); 211 return set; 212 } 213 public static void parsePageDefinition(Set<Integer> pages, String strPages) throws PageException { 214 if(StringUtil.isEmpty(strPages)) return; 215 String[] arr = railo.runtime.type.util.ListUtil.toStringArrayTrim(railo.runtime.type.util.ListUtil.listToArrayRemoveEmpty(strPages, ',')); 216 int index,from,to; 217 for(int i=0;i<arr.length;i++){ 218 index=arr[i].indexOf('-'); 219 if(index==-1)pages.add(Caster.toInteger(arr[i].trim())); 220 else { 221 from=Caster.toIntValue(arr[i].substring(0,index).trim()); 222 to=Caster.toIntValue(arr[i].substring(index+1).trim()); 223 for(int y=from;y<=to;y++){ 224 pages.add(Integer.valueOf(y)); 225 } 226 } 227 } 228 } 229 230 231 232 233 234 public static void encrypt(PDFDocument doc, OutputStream os, String newUserPassword, String newOwnerPassword, int permissions, int encryption) throws ApplicationException, DocumentException, IOException { 235 byte[] user = newUserPassword==null?null:newUserPassword.getBytes(); 236 byte[] owner = newOwnerPassword==null?null:newOwnerPassword.getBytes(); 237 238 PdfReader pr = doc.getPdfReader(); 239 List bookmarks = SimpleBookmark.getBookmark(pr); 240 int n = pr.getNumberOfPages(); 241 242 Document document = new Document(pr.getPageSizeWithRotation(1)); 243 PdfCopy writer = new PdfCopy(document, os); 244 if(encryption!=ENCRYPT_NONE)writer.setEncryption(user, owner, permissions, encryption); 245 document.open(); 246 247 248 PdfImportedPage page; 249 for (int i = 1; i <= n; i++) { 250 page = writer.getImportedPage(pr, i); 251 writer.addPage(page); 252 } 253 PRAcroForm form = pr.getAcroForm(); 254 if (form != null)writer.copyAcroForm(pr); 255 if (bookmarks!=null)writer.setOutlines(bookmarks); 256 document.close(); 257 } 258 259 public static HashMap generateGoToBookMark(String title,int page) { 260 return generateGoToBookMark(title,page, 0, 731); 261 } 262 263 public static HashMap generateGoToBookMark(String title,int page, int x, int y) { 264 HashMap map=new HashMap(); 265 map.put("Title", title); 266 map.put("Action", "GoTo"); 267 map.put("Page", page+" XYZ "+x+" "+y+" null"); 268 269 return map; 270 } 271 272 public static void setChildBookmarks(Map parent, List children) { 273 Object kids = parent.get("Kids"); 274 if(kids instanceof List){ 275 ((List)kids).addAll(children); 276 } 277 else parent.put("Kids", children); 278 } 279 280 public static PdfReader toPdfReader(PageContext pc,Object value, String password) throws IOException, PageException { 281 if(value instanceof PdfReader) return (PdfReader) value; 282 if(value instanceof PDFDocument) return ((PDFDocument) value).getPdfReader(); 283 if(Decision.isBinary(value)){ 284 if(password!=null)return new PdfReader(Caster.toBinary(value),password.getBytes()); 285 return new PdfReader(Caster.toBinary(value)); 286 } 287 if(value instanceof Resource) { 288 if(password!=null)return new PdfReader(IOUtil.toBytes((Resource)value),password.getBytes()); 289 return new PdfReader(IOUtil.toBytes((Resource)value)); 290 } 291 if(value instanceof String) { 292 if(password!=null)return new PdfReader(IOUtil.toBytes(Caster.toResource(pc,value,true)),password.getBytes()); 293 return new PdfReader(IOUtil.toBytes((Resource)value)); 294 } 295 throw new CasterException(value,PdfReader.class); 296 } 297 298 299 /*public static void main(String[] args) throws IOException { 300 301 302 303 PdfReader pr = new PdfReader("/Users/mic/Projects/Railo/webroot/jm/test/tags/pdf/Parallels.pdf"); 304 List bm = SimpleBookmark.getBookmark(pr); 305 print.out(bm); 306 ByteArrayOutputStream os = new ByteArrayOutputStream(); 307 try { 308 SimpleBookmark.exportToXML(bm, os, "UTF-8",false); 309 } 310 finally { 311 IOUtil.closeEL(os); 312 } 313 print.out("*********************************"); 314 print.out(IOUtil.toString(os.toByteArray(), "UTF-8")); 315 }*/ 316 317 public static Image toImage(byte[] input,int page) throws PageException, IOException { 318 return PDF2Image.getInstance().toImage(input, page); 319 } 320 321 public static void writeImages(byte[] input,Set pages,Resource outputDirectory, String prefix, 322 String format, int scale, boolean overwrite, boolean goodQuality,boolean transparent) throws PageException, IOException { 323 PDF2Image.getInstance().writeImages(input, pages, outputDirectory, prefix, format, scale, overwrite, goodQuality, transparent); 324 } 325 326 public static Object extractText(PDFDocument doc, Set<Integer> pageNumbers) throws IOException, CryptographyException, InvalidPasswordException { 327 PDDocument pdDoc = doc.toPDDocument(); 328 //PDPageNode pages = pdDoc.getDocumentCatalog().getPages(); 329 //pages. 330 //pdDoc.getDocumentCatalog(). 331 332 /*Iterator<Integer> it = pageNumbers.iterator(); 333 int p; 334 while(it.hasNext()){ 335 p=it.next().intValue(); 336 337 pdDoc.getDocumentCatalog().getPages() 338 } 339 */ 340 341 //print.o(pages); 342 343 344 345 //pdDoc. 346 347 348 //PDFTextStripperByArea stripper = new PDFTextStripperByArea(); 349 //PDFHighlighter stripper = new PDFHighlighter(); 350 PDFText2HTML stripper = new PDFText2HTML(); 351 //PDFTextStripper stripper = new PDFTextStripper(); 352 StringWriter writer = new StringWriter(); 353 stripper.writeText(pdDoc, writer); 354 355 356 return writer.toString(); 357 } 358 }