001/* 002 * $Id: XmpWriter.java 4887 2011-05-29 14:47:50Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.xml.xmp; 045 046import java.io.IOException; 047import java.io.OutputStream; 048import java.io.OutputStreamWriter; 049import java.util.Map; 050 051import com.itextpdf.text.pdf.PdfDate; 052import com.itextpdf.text.pdf.PdfDictionary; 053import com.itextpdf.text.pdf.PdfName; 054import com.itextpdf.text.pdf.PdfObject; 055import com.itextpdf.text.pdf.PdfString; 056import com.itextpdf.text.pdf.PdfWriter; 057 058/** 059 * With this class you can create an Xmp Stream that can be used for adding 060 * Metadata to a PDF Dictionary. Remark that this class doesn't cover the 061 * complete XMP specification. 062 */ 063public class XmpWriter { 064 065 /** A possible charset for the XMP. */ 066 public static final String UTF8 = "UTF-8"; 067 /** A possible charset for the XMP. */ 068 public static final String UTF16 = "UTF-16"; 069 /** A possible charset for the XMP. */ 070 public static final String UTF16BE = "UTF-16BE"; 071 /** A possible charset for the XMP. */ 072 public static final String UTF16LE = "UTF-16LE"; 073 074 /** String used to fill the extra space. */ 075 public static final String EXTRASPACE = " \n"; 076 077 /** You can add some extra space in the XMP packet; 1 unit in this variable represents 100 spaces and a newline. */ 078 protected int extraSpace; 079 080 /** The writer to which you can write bytes for the XMP stream. */ 081 protected OutputStreamWriter writer; 082 083 /** The about string that goes into the rdf:Description tags. */ 084 protected String about; 085 086 /** 087 * Processing Instruction required at the start of an XMP stream 088 * @since iText 2.1.6 089 */ 090 public static final String XPACKET_PI_BEGIN = "<?xpacket begin=\"\uFEFF\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n"; 091 092 /** 093 * Processing Instruction required at the end of an XMP stream for XMP streams that can be updated 094 * @since iText 2.1.6 095 */ 096 public static final String XPACKET_PI_END_W = "<?xpacket end=\"w\"?>"; 097 098 /** 099 * Processing Instruction required at the end of an XMP stream for XMP streams that are read only 100 * @since iText 2.1.6 101 */ 102 public static final String XPACKET_PI_END_R = "<?xpacket end=\"r\"?>"; 103 104 /** The end attribute. */ 105 protected char end = 'w'; 106 107 /** 108 * Creates an XmpWriter. 109 * @param os 110 * @param utfEncoding 111 * @param extraSpace 112 * @throws IOException 113 */ 114 public XmpWriter(OutputStream os, String utfEncoding, int extraSpace) throws IOException { 115 this.extraSpace = extraSpace; 116 writer = new OutputStreamWriter(os, utfEncoding); 117 writer.write(XPACKET_PI_BEGIN); 118 writer.write("<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n"); 119 writer.write("<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n"); 120 about = ""; 121 } 122 123 /** 124 * Creates an XmpWriter. 125 * @param os 126 * @throws IOException 127 */ 128 public XmpWriter(OutputStream os) throws IOException { 129 this(os, UTF8, 20); 130 } 131 132 /** Sets the XMP to read-only */ 133 public void setReadOnly() { 134 end = 'r'; 135 } 136 137 /** 138 * @param about The about to set. 139 */ 140 public void setAbout(String about) { 141 this.about = about; 142 } 143 144 /** 145 * Adds an rdf:Description. 146 * @param xmlns 147 * @param content 148 * @throws IOException 149 */ 150 public void addRdfDescription(String xmlns, String content) throws IOException { 151 writer.write("<rdf:Description rdf:about=\""); 152 writer.write(about); 153 writer.write("\" "); 154 writer.write(xmlns); 155 writer.write(">"); 156 writer.write(content); 157 writer.write("</rdf:Description>\n"); 158 } 159 160 /** 161 * Adds an rdf:Description. 162 * @param s 163 * @throws IOException 164 */ 165 public void addRdfDescription(XmpSchema s) throws IOException { 166 writer.write("<rdf:Description rdf:about=\""); 167 writer.write(about); 168 writer.write("\" "); 169 writer.write(s.getXmlns()); 170 writer.write(">"); 171 writer.write(s.toString()); 172 writer.write("</rdf:Description>\n"); 173 } 174 175 /** 176 * Flushes and closes the XmpWriter. 177 * @throws IOException 178 */ 179 public void close() throws IOException { 180 writer.write("</rdf:RDF>"); 181 writer.write("</x:xmpmeta>\n"); 182 for (int i = 0; i < extraSpace; i++) { 183 writer.write(EXTRASPACE); 184 } 185 writer.write(end == 'r' ? XPACKET_PI_END_R : XPACKET_PI_END_W); 186 writer.flush(); 187 writer.close(); 188 } 189 190 /** 191 * @param os 192 * @param info 193 * @throws IOException 194 */ 195 public XmpWriter(OutputStream os, PdfDictionary info, int PdfXConformance) throws IOException { 196 this(os); 197 if (info != null) { 198 DublinCoreSchema dc = new DublinCoreSchema(); 199 PdfSchema p = new PdfSchema(); 200 XmpBasicSchema basic = new XmpBasicSchema(); 201 PdfName key; 202 PdfObject obj; 203 for (PdfName pdfName : info.getKeys()) { 204 key = pdfName; 205 obj = info.get(key); 206 if (obj == null) 207 continue; 208 if (PdfName.TITLE.equals(key)) { 209 dc.addTitle(((PdfString)obj).toUnicodeString()); 210 } 211 if (PdfName.AUTHOR.equals(key)) { 212 dc.addAuthor(((PdfString)obj).toUnicodeString()); 213 } 214 if (PdfName.SUBJECT.equals(key)) { 215 dc.addSubject(((PdfString)obj).toUnicodeString()); 216 dc.addDescription(((PdfString)obj).toUnicodeString()); 217 } 218 if (PdfName.KEYWORDS.equals(key)) { 219 p.addKeywords(((PdfString)obj).toUnicodeString()); 220 } 221 if (PdfName.CREATOR.equals(key)) { 222 basic.addCreatorTool(((PdfString)obj).toUnicodeString()); 223 } 224 if (PdfName.PRODUCER.equals(key)) { 225 p.addProducer(((PdfString)obj).toUnicodeString()); 226 } 227 if (PdfName.CREATIONDATE.equals(key)) { 228 basic.addCreateDate(PdfDate.getW3CDate(obj.toString())); 229 } 230 if (PdfName.MODDATE.equals(key)) { 231 basic.addModDate(PdfDate.getW3CDate(obj.toString())); 232 } 233 } 234 if (dc.size() > 0) addRdfDescription(dc); 235 if (p.size() > 0) addRdfDescription(p); 236 if (basic.size() > 0) addRdfDescription(basic); 237 if (PdfXConformance == PdfWriter.PDFA1A || PdfXConformance == PdfWriter.PDFA1B) { 238 PdfA1Schema a1 = new PdfA1Schema(); 239 if (PdfXConformance == PdfWriter.PDFA1A) 240 a1.addConformance("A"); 241 else 242 a1.addConformance("B"); 243 addRdfDescription(a1); 244 } 245 } 246 } 247 248 /** 249 * @param os 250 * @param info 251 * @throws IOException 252 * @since 5.0.1 (generic type in signature) 253 */ 254 public XmpWriter(OutputStream os, Map<String, String> info) throws IOException { 255 this(os); 256 if (info != null) { 257 DublinCoreSchema dc = new DublinCoreSchema(); 258 PdfSchema p = new PdfSchema(); 259 XmpBasicSchema basic = new XmpBasicSchema(); 260 String key; 261 String value; 262 for (Map.Entry<String, String> entry: info.entrySet()) { 263 key = entry.getKey(); 264 value = entry.getValue(); 265 if (value == null) 266 continue; 267 if ("Title".equals(key)) { 268 dc.addTitle(value); 269 } 270 if ("Author".equals(key)) { 271 dc.addAuthor(value); 272 } 273 if ("Subject".equals(key)) { 274 dc.addSubject(value); 275 dc.addDescription(value); 276 } 277 if ("Keywords".equals(key)) { 278 p.addKeywords(value); 279 } 280 if ("Creator".equals(key)) { 281 basic.addCreatorTool(value); 282 } 283 if ("Producer".equals(key)) { 284 p.addProducer(value); 285 } 286 if ("CreationDate".equals(key)) { 287 basic.addCreateDate(PdfDate.getW3CDate(value)); 288 } 289 if ("ModDate".equals(key)) { 290 basic.addModDate(PdfDate.getW3CDate(value)); 291 } 292 } 293 if (dc.size() > 0) addRdfDescription(dc); 294 if (p.size() > 0) addRdfDescription(p); 295 if (basic.size() > 0) addRdfDescription(basic); 296 } 297 } 298}