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