From 167fa81408b3dcc77f561c472b42f76535315579 Mon Sep 17 00:00:00 2001 From: psoares33 Date: Sun, 14 Jun 2009 10:26:24 +0000 Subject: [PATCH] Porting update. git-svn-id: svn://svn.code.sf.net/p/itextsharp/code/trunk@30 820d3149-562b-4f88-9aa4-a8e61a3485cf --- .../iTextSharp/text/pdf/PdfContentByte.cs | 90 +++++++-- src/core/iTextSharp/text/pdf/PdfEFStream.cs | 171 ++++++++++++++++++ src/core/iTextSharp/text/pdf/PdfWriter.cs | 2 +- src/core/iTextSharp/text/pdf/XfaForm.cs | 83 +++++++-- src/core/iTextSharp/text/xml/xmp/XmpReader.cs | 168 +++++++++++++++++ src/core/iTextSharp/text/xml/xmp/XmpWriter.cs | 22 ++- src/core/itextsharp.csproj | 12 +- 7 files changed, 517 insertions(+), 31 deletions(-) create mode 100644 src/core/iTextSharp/text/pdf/PdfEFStream.cs create mode 100644 src/core/iTextSharp/text/xml/xmp/XmpReader.cs diff --git a/src/core/iTextSharp/text/pdf/PdfContentByte.cs b/src/core/iTextSharp/text/pdf/PdfContentByte.cs index d3867e3..9f87229 100644 --- a/src/core/iTextSharp/text/pdf/PdfContentByte.cs +++ b/src/core/iTextSharp/text/pdf/PdfContentByte.cs @@ -1,6 +1,8 @@ using System; using System.Collections; using System.Text; +using iTextSharp.text; +using iTextSharp.text.exceptions; using iTextSharp.text.pdf; using iTextSharp.text.pdf.intern; @@ -175,6 +177,9 @@ namespace iTextSharp.text.pdf { */ protected int separator = '\n'; + private int mcDepth = 0; + private bool inText = false; + private static Hashtable abrev = new Hashtable(); static PdfContentByte() { @@ -234,6 +239,7 @@ namespace iTextSharp.text.pdf { */ public byte[] ToPdf(PdfWriter writer) { + SanityCheck(); return content.ToByteArray(); } @@ -1169,6 +1175,14 @@ namespace iTextSharp.text.pdf { if (inlineImage) { content.Append("\nBI\n"); PdfImage pimage = new PdfImage(image, "", null); + if (image is ImgJBIG2) { + byte[] globals = ((ImgJBIG2)image).GlobalBytes; + if (globals != null) { + PdfDictionary decodeparms = new PdfDictionary(); + decodeparms.Put(PdfName.JBIG2GLOBALS, writer.GetReferenceJBIG2Globals(globals)); + pimage.Put(PdfName.DECODEPARMS, decodeparms); + } + } foreach (PdfName key in pimage.Keys) { PdfObject value = pimage.Get(key); String s = (String)abrev[key]; @@ -1177,12 +1191,12 @@ namespace iTextSharp.text.pdf { content.Append(s); bool check = true; if (key.Equals(PdfName.COLORSPACE) && value.IsArray()) { - ArrayList ar = ((PdfArray)value).ArrayList; - if (ar.Count == 4 - && PdfName.INDEXED.Equals(ar[0]) - && ((PdfObject)ar[1]).IsName() - && ((PdfObject)ar[2]).IsNumber() - && ((PdfObject)ar[3]).IsString() + PdfArray ar = (PdfArray)value; + if (ar.Size == 4 + && PdfName.INDEXED.Equals(ar.GetAsName(0)) + && ar[1].IsName() + && ar[2].IsNumber() + && ar[3].IsString() ) { check = false; } @@ -1251,11 +1265,23 @@ namespace iTextSharp.text.pdf { } /** - * Makes this PdfContentByte empty. - */ + * Makes this PdfContentByte empty. + * Calls reset( true ) + */ public void Reset() { + Reset( true ); + } + + /** + * Makes this PdfContentByte empty. + * @param validateContent will call sanityCheck() if true. + * @since 2.1.6 + */ + public void Reset( bool validateContent ) { content.Reset(); - stateList.Clear(); + if (validateContent) { + SanityCheck(); + } state = new GraphicState(); } @@ -1263,6 +1289,10 @@ namespace iTextSharp.text.pdf { * Starts the writing of text. */ public void BeginText() { + if (inText) { + throw new IllegalPdfSyntaxException("Unbalanced begin/end text operators." ); + } + inText = true; state.xTLM = 0; state.yTLM = 0; content.Append("BT").Append_i(separator); @@ -1272,6 +1302,10 @@ namespace iTextSharp.text.pdf { * Ends the writing of text and makes the current font invalid. */ public void EndText() { + if (!inText) { + throw new IllegalPdfSyntaxException("Unbalanced begin/end text operators." ); + } + inText = false; content.Append("ET").Append_i(separator); } @@ -1292,7 +1326,7 @@ namespace iTextSharp.text.pdf { content.Append('Q').Append_i(separator); int idx = stateList.Count - 1; if (idx < 0) - throw new Exception("Unbalanced save/restore state operators."); + throw new IllegalPdfSyntaxException("Unbalanced save/restore state operators."); state = (GraphicState)stateList[idx]; stateList.RemoveAt(idx); } @@ -2788,6 +2822,8 @@ namespace iTextSharp.text.pdf { if (layerDepth != null && layerDepth.Count > 0) { n = (int)layerDepth[layerDepth.Count - 1]; layerDepth.RemoveAt(layerDepth.Count - 1); + } else { + throw new IllegalPdfSyntaxException("Unbalanced layer operators." ); } while (n-- > 0) content.Append("EMC").Append_i(separator); @@ -2831,7 +2867,7 @@ namespace iTextSharp.text.pdf { } else if (obj.IsArray()) { ar = (PdfArray)obj; - if (!((PdfObject)ar.ArrayList[0]).IsNumber()) + if (!ar[0].IsNumber()) throw new ArgumentException("The structure has kids."); } else @@ -2847,6 +2883,7 @@ namespace iTextSharp.text.pdf { struc.Put(PdfName.PG, writer.CurrentPage); } pdf.IncMarkPoint(); + mcDepth++; content.Append(struc.Get(PdfName.S).GetBytes()).Append(" <> BDC").Append_i(separator); } @@ -2854,6 +2891,10 @@ namespace iTextSharp.text.pdf { * Ends a marked content sequence */ public void EndMarkedContentSequence() { + if (mcDepth == 0) { + throw new IllegalPdfSyntaxException("Unbalanced begin/end marked content operators." ); + } + --mcDepth; content.Append("EMC").Append_i(separator); } @@ -2885,6 +2926,7 @@ namespace iTextSharp.text.pdf { content.Append(name.GetBytes()); } content.Append(" BDC").Append_i(separator); + ++mcDepth; } /** @@ -2894,5 +2936,31 @@ namespace iTextSharp.text.pdf { public void BeginMarkedContentSequence(PdfName tag) { BeginMarkedContentSequence(tag, null, false); } + + /** + * Checks for any dangling state: Mismatched save/restore state, begin/end text, + * begin/end layer, or begin/end marked content sequence. + * If found, this function will throw. This function is called automatically + * during a reset() (from Document.newPage() for example), and before writing + * itself out in toPdf(). + * One possible cause: not calling myPdfGraphics2D.dispose() will leave dangling + * saveState() calls. + * @since 2.1.6 + * @throws IllegalPdfSyntaxException (a runtime exception) + */ + public void SanityCheck() { + if (mcDepth != 0) { + throw new IllegalPdfSyntaxException("Unbalanced marked content operators." ); + } + if (inText) { + throw new IllegalPdfSyntaxException("Unbalanced begin/end text operators." ); + } + if (layerDepth != null && layerDepth.Count > 0) { + throw new IllegalPdfSyntaxException("Unbalanced layer operators." ); + } + if (stateList.Count > 0) { + throw new IllegalPdfSyntaxException("Unbalanced save/restore state operators." ); + } + } } } \ No newline at end of file diff --git a/src/core/iTextSharp/text/pdf/PdfEFStream.cs b/src/core/iTextSharp/text/pdf/PdfEFStream.cs new file mode 100644 index 0000000..171b778 --- /dev/null +++ b/src/core/iTextSharp/text/pdf/PdfEFStream.cs @@ -0,0 +1,171 @@ +using System; +using System.IO; +using System.Collections; +using System.util.zlib; +/* + * $Id: PdfEFStream.java 3735 2009-02-26 01:44:03Z xlv $ + * + * Copyright (c) 2008 by Bruno Lowagie + * + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the License. + * + * The Original Code is 'iText, a free JAVA-PDF library'. + * + * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by + * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. + * All Rights Reserved. + * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer + * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. + * + * Contributor(s): all the names of the contributors are added in the source code + * where applicable. + * + * Alternatively, the contents of this file may be used under the terms of the + * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the + * provisions of LGPL are applicable instead of those above. If you wish to + * allow use of your version of this file only under the terms of the LGPL + * License and not to allow others to use your version of this file under + * the MPL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the LGPL. + * If you do not delete the provisions above, a recipient may use your version + * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MPL as stated above or under the terms of the GNU + * Library General Public License as published by the Free Software Foundation; + * either version 2 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more + * details. + * + * If you didn't download this code from the following link, you should check if + * you aren't using an obsolete version: + * http://www.lowagie.com/iText/ + */ + +namespace iTextSharp.text.pdf { + + /** + * Extends PdfStream and should be used to create Streams for Embedded Files + * (file attachments). + * @since 2.1.3 + */ + + public class PdfEFStream : PdfStream { + + /** + * Creates a Stream object using an InputStream and a PdfWriter object + * @param in the InputStream that will be read to get the Stream object + * @param writer the writer to which the stream will be added + */ + public PdfEFStream(Stream inp, PdfWriter writer) : base (inp, writer) { + } + + /** + * Creates a Stream object using a byte array + * @param fileStore the bytes for the stream + */ + public PdfEFStream(byte[] fileStore) : base(fileStore) { + } + + /** + * @see com.lowagie.text.pdf.PdfDictionary#toPdf(com.lowagie.text.pdf.PdfWriter, java.io.OutputStream) + */ + public override void ToPdf(PdfWriter writer, Stream os) { + if (inputStream != null && compressed) + Put(PdfName.FILTER, PdfName.FLATEDECODE); + PdfEncryption crypto = null; + if (writer != null) + crypto = writer.Encryption; + if (crypto != null) { + PdfObject filter = Get(PdfName.FILTER); + if (filter != null) { + if (PdfName.CRYPT.Equals(filter)) + crypto = null; + else if (filter.IsArray()) { + PdfArray a = (PdfArray)filter; + if (!a.IsEmpty() && PdfName.CRYPT.Equals(a[0])) + crypto = null; + } + } + } + if (crypto != null && crypto.IsEmbeddedFilesOnly()) { + PdfArray filter = new PdfArray(); + PdfArray decodeparms = new PdfArray(); + PdfDictionary crypt = new PdfDictionary(); + crypt.Put(PdfName.NAME, PdfName.STDCF); + filter.Add(PdfName.CRYPT); + decodeparms.Add(crypt); + if (compressed) { + filter.Add(PdfName.FLATEDECODE); + decodeparms.Add(new PdfNull()); + } + Put(PdfName.FILTER, filter); + Put(PdfName.DECODEPARMS, decodeparms); + } + PdfObject nn = Get(PdfName.LENGTH); + if (crypto != null && nn != null && nn.IsNumber()) { + int sz = ((PdfNumber)nn).IntValue; + Put(PdfName.LENGTH, new PdfNumber(crypto.CalculateStreamSize(sz))); + SuperToPdf(writer, os); + Put(PdfName.LENGTH, nn); + } + else + SuperToPdf(writer, os); + + os.Write(STARTSTREAM, 0, STARTSTREAM.Length); + if (inputStream != null) { + rawLength = 0; + ZDeflaterOutputStream def = null; + OutputStreamCounter osc = new OutputStreamCounter(os); + OutputStreamEncryption ose = null; + Stream fout = osc; + if (crypto != null) + fout = ose = crypto.GetEncryptionStream(fout); + if (compressed) + fout = def = new ZDeflaterOutputStream(fout, compressionLevel); + + byte[] buf = new byte[4192]; + while (true) { + int n = inputStream.Read(buf, 0, buf.Length); + if (n <= 0) + break; + fout.Write(buf, 0, n); + rawLength += n; + } + if (def != null) + def.Finish(); + if (ose != null) + ose.Finish(); + inputStreamLength = osc.Counter; + } + else { + if (crypto == null) { + if (streamBytes != null) + streamBytes.WriteTo(os); + else + os.Write(bytes, 0, bytes.Length); + } + else { + byte[] b; + if (streamBytes != null) { + b = crypto.EncryptByteArray(streamBytes.ToArray()); + } + else { + b = crypto.EncryptByteArray(bytes); + } + os.Write(b, 0, b.Length); + } + } + os.Write(ENDSTREAM, 0, ENDSTREAM.Length); + } + } +} \ No newline at end of file diff --git a/src/core/iTextSharp/text/pdf/PdfWriter.cs b/src/core/iTextSharp/text/pdf/PdfWriter.cs index 6acad74..d5422e7 100644 --- a/src/core/iTextSharp/text/pdf/PdfWriter.cs +++ b/src/core/iTextSharp/text/pdf/PdfWriter.cs @@ -3013,7 +3013,7 @@ namespace iTextSharp.text.pdf { * @param content a byte array that may already been added to the writer inside a stream object. * @since 2.1.5 */ - protected PdfIndirectReference GetReferenceJBIG2Globals(byte[] content) { + protected internal PdfIndirectReference GetReferenceJBIG2Globals(byte[] content) { if (content == null) return null; foreach (PdfStream str in JBIG2Globals.Keys) { if (Org.BouncyCastle.Utilities.Arrays.AreEqual(content, str.GetBytes())) { diff --git a/src/core/iTextSharp/text/pdf/XfaForm.cs b/src/core/iTextSharp/text/pdf/XfaForm.cs index 5864e45..6005308 100644 --- a/src/core/iTextSharp/text/pdf/XfaForm.cs +++ b/src/core/iTextSharp/text/pdf/XfaForm.cs @@ -4,7 +4,7 @@ using System.Text; using System.IO; using System.Xml; /* - * $Id: XfaForm.cs,v 1.9 2008/06/28 18:48:46 psoares33 Exp $ + * $Id: XfaForm.cs,v 1.8 2008/06/06 14:57:18 psoares33 Exp $ * * Copyright 2006 Paulo Soares * @@ -61,6 +61,7 @@ namespace iTextSharp.text.pdf { public class XfaForm { private Xml2SomTemplate templateSom; + private XmlNode templateNode; private Xml2SomDatasets datasetsSom; private AcroFieldsSearch acroFieldsSom; private PdfReader reader; @@ -76,6 +77,21 @@ namespace iTextSharp.text.pdf { public XfaForm() { } + /** + * Return the XFA Object, could be an array, could be a Stream. + * Returns null f no XFA Object is present. + * @param reader a PdfReader instance + * @return the XFA object + * @since 2.1.3 + */ + public static PdfObject GetXfaObject(PdfReader reader) { + PdfDictionary af = (PdfDictionary)PdfReader.GetPdfObjectRelease(reader.Catalog.Get(PdfName.ACROFORM)); + if (af == null) { + return null; + } + return PdfReader.GetPdfObjectRelease(af.Get(PdfName.XFA)); + } + /** * A constructor from a PdfReader. It basically does everything * from finding the XFA stream to the XML parsing. @@ -86,12 +102,7 @@ namespace iTextSharp.text.pdf { */ public XfaForm(PdfReader reader) { this.reader = reader; - PdfDictionary af = (PdfDictionary)PdfReader.GetPdfObjectRelease(reader.Catalog.Get(PdfName.ACROFORM)); - if (af == null) { - xfaPresent = false; - return; - } - PdfObject xfa = PdfReader.GetPdfObjectRelease(af.Get(PdfName.XFA)); + PdfObject xfa = GetXfaObject(reader); if (xfa == null) { xfaPresent = false; return; @@ -99,9 +110,9 @@ namespace iTextSharp.text.pdf { xfaPresent = true; MemoryStream bout = new MemoryStream(); if (xfa.IsArray()) { - ArrayList ar = ((PdfArray)xfa).ArrayList; - for (int k = 1; k < ar.Count; k += 2) { - PdfObject ob = PdfReader.GetPdfObject((PdfObject)ar[k]); + PdfArray ar = (PdfArray)xfa; + for (int k = 1; k < ar.Size; k += 2) { + PdfObject ob = ar.GetDirectObject(k); if (ob is PRStream) { byte[] b = PdfReader.GetStreamBytes((PRStream)ob); bout.Write(b, 0, b.Length); @@ -117,6 +128,14 @@ namespace iTextSharp.text.pdf { domDocument = new XmlDocument(); domDocument.PreserveWhitespace = true; domDocument.Load(xtr); + ExtractNodes(); + } + + /** + * Extracts the nodes from the domDocument. + * @since 2.1.5 + */ + private void ExtractNodes() { XmlNode n = domDocument.FirstChild; while (n.NodeType != XmlNodeType.Element || n.ChildNodes.Count == 0) n = n.NextSibling; @@ -125,6 +144,7 @@ namespace iTextSharp.text.pdf { if (n.NodeType == XmlNodeType.Element) { String s = n.LocalName; if (s.Equals("template")) { + templateNode = n; templateSom = new Xml2SomTemplate(n); } else if (s.Equals("datasets")) { @@ -138,19 +158,46 @@ namespace iTextSharp.text.pdf { /** * Sets the XFA key from a byte array. The old XFA is erased. - * @param xfaData the data + * @param form the data * @param reader the reader * @param writer the writer * @throws java.io.IOException on error */ - public static void SetXfa(byte[] xfaData, PdfReader reader, PdfWriter writer) { + public static void SetXfa(XfaForm form, PdfReader reader, PdfWriter writer) { PdfDictionary af = (PdfDictionary)PdfReader.GetPdfObjectRelease(reader.Catalog.Get(PdfName.ACROFORM)); if (af == null) { return; } + PdfObject xfa = GetXfaObject(reader); + if (xfa.IsArray()) { + PdfArray ar = (PdfArray)xfa; + int t = -1; + int d = -1; + for (int k = 0; k < ar.Size; k += 2) { + PdfString s = ar.GetAsString(k); + if ("template".Equals(s.ToString())) { + t = k + 1; + } + if ("datasets".Equals(s.ToString())) { + d = k + 1; + } + } + if (t > -1 && d > -1) { + reader.KillXref(ar.GetAsIndirectObject(t)); + reader.KillXref(ar.GetAsIndirectObject(d)); + PdfStream tStream = new PdfStream(SerializeDoc(form.templateNode)); + tStream.FlateCompress(writer.CompressionLevel); + ar[t] = writer.AddToBody(tStream).IndirectReference; + PdfStream dStream = new PdfStream(SerializeDoc(form.datasetsNode)); + dStream.FlateCompress(writer.CompressionLevel); + ar[d] = writer.AddToBody(dStream).IndirectReference; + af.Put(PdfName.XFA, new PdfArray(ar)); + return; + } + } reader.KillXref(af.Get(PdfName.XFA)); - PdfStream str = new PdfStream(xfaData); - str.FlateCompress(); + PdfStream str = new PdfStream(SerializeDoc(form.domDocument)); + str.FlateCompress(writer.CompressionLevel); PdfIndirectReference refe = writer.AddToBody(str).IndirectReference; af.Put(PdfName.XFA, refe); } @@ -161,7 +208,7 @@ namespace iTextSharp.text.pdf { * @throws java.io.IOException on error */ public void SetXfa(PdfWriter writer) { - SetXfa(SerializeDoc(domDocument), reader, writer); + SetXfa(this, reader, writer); } /** @@ -201,6 +248,7 @@ namespace iTextSharp.text.pdf { } set { domDocument = value; + ExtractNodes(); } } @@ -217,7 +265,10 @@ namespace iTextSharp.text.pdf { if (items.ContainsKey(name)) return name; if (acroFieldsSom == null) { - acroFieldsSom = new AcroFieldsSearch(items.Keys); + if (items.Count == 0 && xfaPresent) + acroFieldsSom = new AcroFieldsSearch(datasetsSom.Name2Node.Keys); + else + acroFieldsSom = new AcroFieldsSearch(items.Keys); } if (acroFieldsSom.AcroShort2LongName.ContainsKey(name)) return (String)acroFieldsSom.AcroShort2LongName[name]; diff --git a/src/core/iTextSharp/text/xml/xmp/XmpReader.cs b/src/core/iTextSharp/text/xml/xmp/XmpReader.cs new file mode 100644 index 0000000..d2a30b0 --- /dev/null +++ b/src/core/iTextSharp/text/xml/xmp/XmpReader.cs @@ -0,0 +1,168 @@ +using System; +using System.IO; +using System.Xml; +using System.Text; +/* + * Copyright 2008 by Bruno Lowagie + * + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the License. + * + * The Original Code is 'iText, a free JAVA-PDF library'. + * + * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by + * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. + * All Rights Reserved. + * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer + * are Copyright (C) 2008 by Paulo Soares. All Rights Reserved. + * + * Contributor(s): all the names of the contributors are added in the source code + * where applicable. + * + * Alternatively, the contents of this file may be used under the terms of the + * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the + * provisions of LGPL are applicable instead of those above. If you wish to + * allow use of your version of this file only under the terms of the LGPL + * License and not to allow others to use your version of this file under + * the MPL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the LGPL. + * If you do not delete the provisions above, a recipient may use your version + * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MPL as stated above or under the terms of the GNU + * Library General Public License as published by the Free Software Foundation; + * either version 2 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more + * details. + * + * If you didn't download this code from the following link, you should check if + * you aren't using an obsolete version: + * http://www.lowagie.com/iText/ + */ +namespace iTextSharp.text.xml.xmp { + + + /** + * Reads an XMP stream into an org.w3c.dom.Document objects. + * Allows you to replace the contents of a specific tag. + * @since 2.1.3 + */ + + public class XmpReader { + + private XmlDocument domDocument; + + /** + * Constructs an XMP reader + * @param bytes the XMP content + * @throws ExceptionConverter + * @throws IOException + * @throws SAXException + */ + public XmpReader(byte[] bytes) { + MemoryStream bout = new MemoryStream(); + bout.Write(bytes, 0, bytes.Length); + bout.Seek(0, SeekOrigin.Begin); + XmlTextReader xtr = new XmlTextReader(bout); + domDocument = new XmlDocument(); + domDocument.PreserveWhitespace = true; + domDocument.Load(xtr); + } + + /** + * Replaces the content of a tag. + * @param namespaceURI the URI of the namespace + * @param localName the tag name + * @param value the new content for the tag + * @return true if the content was successfully replaced + * @since 2.1.6 the return type has changed from void to boolean + */ + public bool Replace(String namespaceURI, String localName, String value) { + XmlNodeList nodes = domDocument.GetElementsByTagName(localName, namespaceURI); + XmlNode node; + if (nodes.Count == 0) + return false; + for (int i = 0; i < nodes.Count; i++) { + node = nodes[i]; + SetNodeText(domDocument, node, value); + } + return true; + } + + /** + * Adds a tag. + * @param namespaceURI the URI of the namespace + * @param parent the tag name of the parent + * @param localName the name of the tag to add + * @param value the new content for the tag + * @return true if the content was successfully added + * @since 2.1.6 + */ + public bool Add(String parent, String namespaceURI, String localName, String value) { + XmlNodeList nodes = domDocument.GetElementsByTagName(parent); + if (nodes.Count == 0) + return false; + XmlNode pNode; + XmlNode node; + for (int i = 0; i < nodes.Count; i++) { + pNode = nodes[i]; + XmlAttributeCollection attrs = pNode.Attributes; + for (int j = 0; j < attrs.Count; j++) { + node = attrs[j]; + if (namespaceURI.Equals(node.Value)) { + node = domDocument.CreateElement(localName); + node.AppendChild(domDocument.CreateTextNode(value)); + pNode.AppendChild(node); + return true; + } + } + } + return false; + } + + /** + * Sets the text of this node. All the child's node are deleted and a new + * child text node is created. + * @param domDocument the Document that contains the node + * @param n the Node to add the text to + * @param value the text to add + */ + public bool SetNodeText(XmlDocument domDocument, XmlNode n, String value) { + if (n == null) + return false; + XmlNode nc = null; + while ((nc = n.FirstChild) != null) { + n.RemoveChild(nc); + } + n.AppendChild(domDocument.CreateTextNode(value)); + return true; + } + + /** + * Writes the document to a byte array. + */ + public byte[] SerializeDoc() { + MemoryStream fout = new MemoryStream(); + byte[] b = new UTF8Encoding(false).GetBytes(XmpWriter.XPACKET_PI_BEGIN); + fout.Write(b, 0, b.Length); + fout.Flush(); + XmlNodeList xmpmeta = domDocument.GetElementsByTagName("x:xmpmeta"); + XmlTextWriter xw = new XmlTextWriter(fout, new UTF8Encoding(false)); + xw.WriteNode(new XmlNodeReader(xmpmeta[0]), true); + xw.Flush(); + b = new UTF8Encoding(false).GetBytes(XmpWriter.XPACKET_PI_END_W); + fout.Write(b, 0, b.Length); + fout.Close(); + return fout.ToArray(); + } + } +} \ No newline at end of file diff --git a/src/core/iTextSharp/text/xml/xmp/XmpWriter.cs b/src/core/iTextSharp/text/xml/xmp/XmpWriter.cs index d60a175..d35d4a0 100644 --- a/src/core/iTextSharp/text/xml/xmp/XmpWriter.cs +++ b/src/core/iTextSharp/text/xml/xmp/XmpWriter.cs @@ -83,6 +83,24 @@ namespace iTextSharp.text.xml.xmp { /** The about string that goes into the rdf:Description tags. */ protected String about; + /** + * Processing Instruction required at the start of an XMP stream + * @since iText 2.1.6 + */ + public const String XPACKET_PI_BEGIN = "\n"; + + /** + * Processing Instruction required at the end of an XMP stream for XMP streams that can be updated + * @since iText 2.1.6 + */ + public const String XPACKET_PI_END_W = ""; + + /** + * Processing Instruction required at the end of an XMP stream for XMP streams that are read only + * @since iText 2.1.6 + */ + public const String XPACKET_PI_END_R = ""; + /** The end attribute. */ protected char end = 'w'; @@ -96,7 +114,7 @@ namespace iTextSharp.text.xml.xmp { public XmpWriter(Stream os, string utfEncoding, int extraSpace) { this.extraSpace = extraSpace; writer = new StreamWriter(os, new EncodingNoPreamble(IanaEncodings.GetEncodingEncoding(utfEncoding))); - writer.Write("\n"); + writer.Write(XPACKET_PI_BEGIN); writer.Write("\n"); writer.Write("\n"); about = ""; @@ -165,7 +183,7 @@ namespace iTextSharp.text.xml.xmp { for (int i = 0; i < extraSpace; i++) { writer.Write(EXTRASPACE); } - writer.Write(""); + writer.Write(end == 'r' ? XPACKET_PI_END_R : XPACKET_PI_END_W); writer.Flush(); writer.Close(); } diff --git a/src/core/itextsharp.csproj b/src/core/itextsharp.csproj index 1729fff..fda766d 100644 --- a/src/core/itextsharp.csproj +++ b/src/core/itextsharp.csproj @@ -872,6 +872,11 @@ SubType = "Code" BuildAction = "Compile" /> + +