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    }