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.metadata; 018 019import com.itextpdf.text.pdf.PdfReader; 020import cz.hobrasoft.pdfmu.MapSorter; 021import cz.hobrasoft.pdfmu.PdfmuUtils; 022import cz.hobrasoft.pdfmu.PreferenceListComparator; 023import cz.hobrasoft.pdfmu.operation.args.ArgsConfiguration; 024import java.util.AbstractMap; 025import java.util.Arrays; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029import java.util.SortedMap; 030import net.sourceforge.argparse4j.impl.Arguments; 031import net.sourceforge.argparse4j.inf.ArgumentGroup; 032import net.sourceforge.argparse4j.inf.ArgumentParser; 033import net.sourceforge.argparse4j.inf.Namespace; 034 035public class MetadataParameters implements ArgsConfiguration { 036 037 private Map<String, String> info = new HashMap<>(); 038 private boolean clearall = false; 039 040 public Map<String, String> getInfo(PdfReader pdfReader) { 041 if (clearall) { 042 // We need the keys that are already set in the input file. 043 Map<String, String> inInfo = pdfReader.getInfo(); 044 Map<String, String> res = new HashMap<>(); 045 for (String key : inInfo.keySet()) { 046 // Unset the property `key` 047 // TODO?: Do not unset the properties "Producer" and "ModDate" 048 res.put(key, null); 049 } 050 // Set all the properties in `info`, possibly overwriting the unset properties 051 res.putAll(info); 052 return res; 053 } 054 return info; 055 } 056 057 // Table with the descriptions: 058 // http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf 059 // (table 317, section 14.3.3, page 550) 060 private static final Map<String, String> standardProperties = PdfmuUtils.sortedMap( 061 new AbstractMap.SimpleEntry<>("Title", "The document's title."), 062 new AbstractMap.SimpleEntry<>("Subject", "The subject of the document."), 063 new AbstractMap.SimpleEntry<>("Author", "The name of the person who created the document."), 064 new AbstractMap.SimpleEntry<>("Keywords", "Keywords associated with the document."), 065 new AbstractMap.SimpleEntry<>("Creator", "The name of the product that created the document in the original format."), 066 new AbstractMap.SimpleEntry<>("Producer", "The name of the product that converted the document from the original format to PDF."), 067 new AbstractMap.SimpleEntry<>("CreationDate", "The date and time the document was created, in human-readable form."), 068 new AbstractMap.SimpleEntry<>("ModDate", "The date and time the document was most recently modified, in human-readable form."), 069 new AbstractMap.SimpleEntry<>("Trapped", "Has the document been modified to include trapping information? (recommended values: True,False,Unknown)") 070 ); 071 072 // iText does not let us set the Producer property. 073 // The ModDate property also seems to be set automatically. 074 // TODO: Check spelling of "settable" 075 private static final List<String> standardSettableProperties = Arrays.asList(new String[]{ 076 "Title", "Subject", "Author", "Keywords", "Creator", "CreationDate", "Trapped"}); 077 078 @Override 079 public void addArguments(ArgumentParser parser) { 080 // Generic properties 081 parser.addArgument("-s", "--set") 082 .help("set the property P to the value V") 083 .metavar("P", "V") 084 .nargs(2) 085 .type(String.class) 086 .action(Arguments.append()); 087 088 parser.addArgument("-c", "--clear") 089 .help("clear the property P") 090 .metavar("P") 091 .type(String.class) 092 .action(Arguments.append()); 093 094 // Remove all properties 095 parser.addArgument("--clear-all") 096 .help("clear all the properties") 097 .type(boolean.class) 098 .action(Arguments.storeTrue()); 099 100 // Standard properties 101 ArgumentGroup group = parser.addArgumentGroup("standard properties") 102 .description("These are simple shortcuts for setting the standard properties. For example, `--Title TITLE` is equivalent to `--set Title TITLE`."); 103 for (String property : standardSettableProperties) { 104 assert standardProperties.containsKey(property); 105 String help = standardProperties.get(property); 106 group.addArgument("--" + property) 107 .help(help) 108 .type(String.class); 109 } 110 } 111 112 @Override 113 public void setFromNamespace(Namespace namespace) { 114 clearall = namespace.getBoolean("clear_all"); 115 116 // Clear the selected properties 117 { // clearedProperties 118 List<String> clearedProperties = namespace.getList("clear"); 119 if (clearedProperties != null) { 120 for (String p : clearedProperties) { 121 info.put(p, null); 122 } 123 } 124 } 125 126 // Generic properties 127 List<List<String>> elements = namespace.getList("set"); 128 if (elements != null) { 129 for (List<String> element : elements) { 130 String key = element.get(0); 131 // TODO: Warn if key is Producer or ModDate 132 String value = element.get(1); 133 // TODO: Warn if key is Trapped and value is not one of True, False, Unknown 134 info.put(key, value); 135 } 136 } 137 138 // Standard properties 139 for (String property : standardSettableProperties) { 140 String value = namespace.getString(property); 141 if (value != null) { 142 info.put(property, value); 143 } 144 } 145 } 146 147 public void setFromInfo(Map<String, String> info) { 148 this.info = info; 149 } 150 151 private static final MapSorter<String> propertySorter 152 = new PreferenceListComparator<>(standardProperties.keySet().iterator()); 153 154 public SortedMap<String, String> getSorted() { 155 return propertySorter.sort(info); 156 } 157 158}