View Javadoc
1   /* 
2    * Copyright (C) 2016 Hobrasoft s.r.o.
3    *
4    * This program is free software: you can redistribute it and/or modify
5    * it under the terms of the GNU Affero General Public License as published by
6    * the Free Software Foundation, either version 3 of the License, or
7    * (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU Affero General Public License for more details.
13   *
14   * You should have received a copy of the GNU Affero General Public License
15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16   */
17  package cz.hobrasoft.pdfmu.operation;
18  
19  import com.itextpdf.text.pdf.PdfStamper;
20  import cz.hobrasoft.pdfmu.PdfmuUtils;
21  import static cz.hobrasoft.pdfmu.error.ErrorType.ATTACH_ATTACHMENT_EQUALS_OUTPUT;
22  import static cz.hobrasoft.pdfmu.error.ErrorType.ATTACH_FAIL;
23  import cz.hobrasoft.pdfmu.jackson.EmptyResult;
24  import cz.hobrasoft.pdfmu.operation.args.InOutPdfArgs;
25  import java.io.File;
26  import java.io.IOException;
27  import java.util.AbstractMap.SimpleEntry;
28  import java.util.logging.Logger;
29  import java.util.regex.Matcher;
30  import java.util.regex.Pattern;
31  import net.sourceforge.argparse4j.impl.Arguments;
32  import net.sourceforge.argparse4j.inf.Namespace;
33  import net.sourceforge.argparse4j.inf.Subparser;
34  
35  /**
36   * Attaches one or more files to a PDF document
37   *
38   * @author <a href="mailto:filip.bartek@hobrasoft.cz">Filip Bartek</a>
39   */
40  public class OperationAttach extends OperationCommon {
41  
42      private static final Logger logger = Logger.getLogger(OperationAttach.class.getName());
43  
44      private final String metavarIn = "IN.pdf";
45      private final InOutPdfArgs inout = new InOutPdfArgs(metavarIn);
46  
47      @Override
48      public Subparser configureSubparser(Subparser subparser) {
49          String help = "Attach a file to a PDF document";
50  
51          subparser.help(help)
52                  .description(help)
53                  .defaultHelp(true);
54  
55          inout.addArguments(subparser);
56  
57          String metavarAttachment = "ATTACHMENT";
58  
59          // TODO: Try to reuse InPdfArgs
60          subparser.addArgument("attachment")
61                  .help(String.format("file to attach to %s", metavarIn))
62                  .metavar(metavarAttachment)
63                  .type(Arguments.fileType().acceptSystemIn());
64          subparser.addArgument("-r", "--rename")
65                  .help(String.format("attachment filename shown in the output PDF document (default: <%s>)", metavarAttachment))
66                  .metavar("FILENAME")
67                  .type(String.class);
68          subparser.addArgument("-d", "--description")
69                  .help("attachment description shown in the output PDF document (default: <none>)")
70                  .type(String.class);
71  
72          return subparser;
73      }
74  
75      @Override
76      public void execute(Namespace namespace) throws OperationException {
77          inout.setFromNamespace(namespace);
78  
79          File file = namespace.get("attachment");
80  
81          File outFile = inout.getOut().getFile();
82          assert outFile != null;
83          if (outFile.equals(file)) {
84              throw new OperationException(ATTACH_ATTACHMENT_EQUALS_OUTPUT,
85                      PdfmuUtils.sortedMap(
86                              new SimpleEntry<String, Object>("outputFile", outFile),
87                              new SimpleEntry<String, Object>("attachmentFile", file)));
88          }
89  
90          String description = namespace.getString("description");
91          String fileDisplay = namespace.getString("rename");
92  
93          if (fileDisplay == null) {
94              fileDisplay = file.getName();
95          }
96  
97          try {
98              inout.open();
99              execute(inout.getPdfStamper(), description, file.getPath(), fileDisplay);
100             inout.close(true);
101         } finally {
102             inout.close(false);
103         }
104 
105         writeResult(new EmptyResult());
106     }
107 
108     private static final Pattern filenameWithExtension = Pattern.compile(".*\\.[^\\.]+");
109 
110     private static void execute(PdfStamper stp, String description, String file, String fileDisplay) throws OperationException {
111         {
112             assert stp != null;
113             assert file != null;
114             assert fileDisplay != null; // We use the attachment file name by default
115 
116             logger.info(String.format("Attached file: %s", file));
117             logger.info(String.format("Description: %s", (description != null ? description : "<none>")));
118             logger.info(String.format("Display name: %s", fileDisplay));
119             {
120                 Matcher m = filenameWithExtension.matcher(fileDisplay);
121                 if (!m.matches()) {
122                     logger.warning("Display name does not contain a file extension. Adobe Reader XI does not allow opening or saving such attachment.");
123                 }
124             }
125         }
126         try {
127             stp.addFileAttachment(description, null, file, fileDisplay);
128         } catch (IOException ex) {
129             throw new OperationException(ATTACH_FAIL, ex,
130                     PdfmuUtils.sortedMap(new String[]{"file"}, new Object[]{file}));
131         }
132         logger.info(String.format("The file \"%s\" has been attached.", file));
133     }
134 
135     private static Operation instance = null;
136 
137     public static Operation getInstance() {
138         if (instance == null) {
139             instance = new OperationAttach();
140         }
141         return instance;
142     }
143 
144     private OperationAttach() {
145         // Singleton
146     }
147 
148 }