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    }