001/* 002 * $Id: XmpReader.java 4784 2011-03-15 08:33:00Z 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.ByteArrayInputStream; 047import java.io.ByteArrayOutputStream; 048import java.io.IOException; 049 050import javax.xml.parsers.DocumentBuilder; 051import javax.xml.parsers.DocumentBuilderFactory; 052import javax.xml.parsers.ParserConfigurationException; 053 054import org.w3c.dom.Document; 055import org.w3c.dom.NamedNodeMap; 056import org.w3c.dom.Node; 057import org.w3c.dom.NodeList; 058import org.xml.sax.SAXException; 059 060import com.itextpdf.text.ExceptionConverter; 061import com.itextpdf.text.xml.XmlDomWriter; 062 063/** 064 * Reads an XMP stream into an org.w3c.dom.Document objects. 065 * Allows you to replace the contents of a specific tag. 066 * @since 2.1.3 067 */ 068 069public class XmpReader { 070 071 private Document domDocument; 072 073 /** 074 * Constructs an XMP reader 075 * @param bytes the XMP content 076 * @throws ExceptionConverter 077 * @throws IOException 078 * @throws SAXException 079 */ 080 public XmpReader(byte[] bytes) throws SAXException, IOException { 081 try { 082 DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance(); 083 fact.setNamespaceAware(true); 084 DocumentBuilder db = fact.newDocumentBuilder(); 085 ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 086 domDocument = db.parse(bais); 087 } catch (ParserConfigurationException e) { 088 throw new ExceptionConverter(e); 089 } 090 } 091 092 /** 093 * Replaces the content of a tag. 094 * @param namespaceURI the URI of the namespace 095 * @param localName the tag name 096 * @param value the new content for the tag 097 * @return true if the content was successfully replaced 098 * @since 2.1.6 the return type has changed from void to boolean 099 */ 100 public boolean replaceNode(String namespaceURI, String localName, String value) { 101 NodeList nodes = domDocument.getElementsByTagNameNS(namespaceURI, localName); 102 Node node; 103 if (nodes.getLength() == 0) 104 return false; 105 for (int i = 0; i < nodes.getLength(); i++) { 106 node = nodes.item(i); 107 setNodeText(domDocument, node, value); 108 } 109 return true; 110 } 111 112 /** 113 * Replaces the content of an attribute in the description tag. 114 * @param namespaceURI the URI of the namespace 115 * @param localName the tag name 116 * @param value the new content for the tag 117 * @return true if the content was successfully replaced 118 * @since 5.0.0 the return type has changed from void to boolean 119 */ 120 public boolean replaceDescriptionAttribute(String namespaceURI, String localName, String value) { 121 NodeList descNodes = domDocument.getElementsByTagNameNS("http://www.w3.org/1999/02/22-rdf-syntax-ns#","Description"); 122 if(descNodes.getLength() == 0) { 123 return false; 124 } 125 Node node; 126 for(int i = 0; i < descNodes.getLength(); i++) { 127 node = descNodes.item(i); 128 Node attr = node.getAttributes().getNamedItemNS(namespaceURI, localName); 129 if(attr != null) { 130 attr.setNodeValue(value); 131 return true; 132 } 133 } 134 return false; 135 } 136 137 /** 138 * Adds a tag. 139 * @param namespaceURI the URI of the namespace 140 * @param parent the tag name of the parent 141 * @param localName the name of the tag to add 142 * @param value the new content for the tag 143 * @return true if the content was successfully added 144 * @since 2.1.6 145 */ 146 public boolean add(String parent, String namespaceURI, String localName, String value) { 147 NodeList nodes = domDocument.getElementsByTagName(parent); 148 if (nodes.getLength() == 0) 149 return false; 150 Node pNode; 151 Node node; 152 for (int i = 0; i < nodes.getLength(); i++) { 153 pNode = nodes.item(i); 154 NamedNodeMap attrs = pNode.getAttributes(); 155 for (int j = 0; j < attrs.getLength(); j++) { 156 node = attrs.item(j); 157 if (namespaceURI.equals(node.getNodeValue())) { 158 node = domDocument.createElement(localName); 159 node.appendChild(domDocument.createTextNode(value)); 160 pNode.appendChild(node); 161 return true; 162 } 163 } 164 } 165 return false; 166 } 167 168 /** 169 * Sets the text of this node. All the child's node are deleted and a new 170 * child text node is created. 171 * @param domDocument the <CODE>Document</CODE> that contains the node 172 * @param n the <CODE>Node</CODE> to add the text to 173 * @param value the text to add 174 */ 175 public boolean setNodeText(Document domDocument, Node n, String value) { 176 if (n == null) 177 return false; 178 Node nc = null; 179 while ((nc = n.getFirstChild()) != null) { 180 n.removeChild(nc); 181 } 182 n.appendChild(domDocument.createTextNode(value)); 183 return true; 184 } 185 186 /** 187 * Writes the document to a byte array. 188 */ 189 public byte[] serializeDoc() throws IOException { 190 XmlDomWriter xw = new XmlDomWriter(); 191 ByteArrayOutputStream fout = new ByteArrayOutputStream(); 192 xw.setOutput(fout, null); 193 fout.write(XmpWriter.XPACKET_PI_BEGIN.getBytes("UTF-8")); 194 fout.flush(); 195 NodeList xmpmeta = domDocument.getElementsByTagName("x:xmpmeta"); 196 xw.write(xmpmeta.item(0)); 197 fout.flush(); 198 for (int i = 0; i < 20; i++) { 199 fout.write(XmpWriter.EXTRASPACE.getBytes()); 200 } 201 fout.write(XmpWriter.XPACKET_PI_END_W.getBytes()); 202 fout.close(); 203 return fout.toByteArray(); 204 } 205}