001/* 002 * Copyright (C) 2016 Hobrasoft s.r.o. 003 * 004 * This program is free software: you can redistribute it and/or modify 005 * it under the terms of the GNU Affero General Public License as published by 006 * the Free Software Foundation, either version 3 of the License, or 007 * (at your option) any later version. 008 * 009 * This program is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU Affero General Public License for more details. 013 * 014 * You should have received a copy of the GNU Affero General Public License 015 * along with this program. If not, see <http://www.gnu.org/licenses/>. 016 */ 017package cz.hobrasoft.pdfmu.operation; 018 019import com.itextpdf.text.pdf.PdfStamper; 020import cz.hobrasoft.pdfmu.PdfmuUtils; 021import static cz.hobrasoft.pdfmu.error.ErrorType.ATTACH_ATTACHMENT_EQUALS_OUTPUT; 022import static cz.hobrasoft.pdfmu.error.ErrorType.ATTACH_FAIL; 023import cz.hobrasoft.pdfmu.jackson.EmptyResult; 024import cz.hobrasoft.pdfmu.operation.args.InOutPdfArgs; 025import java.io.File; 026import java.io.IOException; 027import java.util.AbstractMap.SimpleEntry; 028import java.util.logging.Logger; 029import java.util.regex.Matcher; 030import java.util.regex.Pattern; 031import net.sourceforge.argparse4j.impl.Arguments; 032import net.sourceforge.argparse4j.inf.Namespace; 033import net.sourceforge.argparse4j.inf.Subparser; 034 035/** 036 * Attaches one or more files to a PDF document 037 * 038 * @author <a href="mailto:filip.bartek@hobrasoft.cz">Filip Bartek</a> 039 */ 040public class OperationAttach extends OperationCommon { 041 042 private static final Logger logger = Logger.getLogger(OperationAttach.class.getName()); 043 044 private final String metavarIn = "IN.pdf"; 045 private final InOutPdfArgs inout = new InOutPdfArgs(metavarIn); 046 047 @Override 048 public Subparser configureSubparser(Subparser subparser) { 049 String help = "Attach a file to a PDF document"; 050 051 subparser.help(help) 052 .description(help) 053 .defaultHelp(true); 054 055 inout.addArguments(subparser); 056 057 String metavarAttachment = "ATTACHMENT"; 058 059 // TODO: Try to reuse InPdfArgs 060 subparser.addArgument("attachment") 061 .help(String.format("file to attach to %s", metavarIn)) 062 .metavar(metavarAttachment) 063 .type(Arguments.fileType().acceptSystemIn()); 064 subparser.addArgument("-r", "--rename") 065 .help(String.format("attachment filename shown in the output PDF document (default: <%s>)", metavarAttachment)) 066 .metavar("FILENAME") 067 .type(String.class); 068 subparser.addArgument("-d", "--description") 069 .help("attachment description shown in the output PDF document (default: <none>)") 070 .type(String.class); 071 072 return subparser; 073 } 074 075 @Override 076 public void execute(Namespace namespace) throws OperationException { 077 inout.setFromNamespace(namespace); 078 079 File file = namespace.get("attachment"); 080 081 File outFile = inout.getOut().getFile(); 082 assert outFile != null; 083 if (outFile.equals(file)) { 084 throw new OperationException(ATTACH_ATTACHMENT_EQUALS_OUTPUT, 085 PdfmuUtils.sortedMap( 086 new SimpleEntry<String, Object>("outputFile", outFile), 087 new SimpleEntry<String, Object>("attachmentFile", file))); 088 } 089 090 String description = namespace.getString("description"); 091 String fileDisplay = namespace.getString("rename"); 092 093 if (fileDisplay == null) { 094 fileDisplay = file.getName(); 095 } 096 097 try { 098 inout.open(); 099 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}