From 4aae65fcab6e84825998a1c9ca27da91b50a5e1a Mon Sep 17 00:00:00 2001 From: psoares33 Date: Wed, 8 Oct 2008 10:02:17 +0000 Subject: [PATCH] The bookmarks were being written twice. git-svn-id: svn://svn.code.sf.net/p/itextsharp/code/trunk@15 820d3149-562b-4f88-9aa4-a8e61a3485cf --- src/core/iTextSharp/text/pdf/PdfCopy.cs | 1 - .../iTextSharp/text/pdf/PdfCopyFieldsImp.cs | 1199 ++++++++--------- 2 files changed, 599 insertions(+), 601 deletions(-) diff --git a/src/core/iTextSharp/text/pdf/PdfCopy.cs b/src/core/iTextSharp/text/pdf/PdfCopy.cs index aa5eaed..fea4c47 100644 --- a/src/core/iTextSharp/text/pdf/PdfCopy.cs +++ b/src/core/iTextSharp/text/pdf/PdfCopy.cs @@ -405,7 +405,6 @@ namespace iTextSharp.text.pdf { } else AddFieldResources(theCat); - WriteOutlines(theCat, false); return theCat; } diff --git a/src/core/iTextSharp/text/pdf/PdfCopyFieldsImp.cs b/src/core/iTextSharp/text/pdf/PdfCopyFieldsImp.cs index a3ed660..79a6290 100644 --- a/src/core/iTextSharp/text/pdf/PdfCopyFieldsImp.cs +++ b/src/core/iTextSharp/text/pdf/PdfCopyFieldsImp.cs @@ -1,600 +1,599 @@ -using System; -using System.Collections; -using System.IO; -using System.util; -/* - * Copyright 2004 by Paulo Soares. - * - * 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 { - /** - * - * @author psoares - */ - internal class PdfCopyFieldsImp : PdfWriter { - - private static readonly PdfName iTextTag = new PdfName("_iTextTag_"); - private static object zero = 0; - internal ArrayList readers = new ArrayList(); - internal Hashtable readers2intrefs = new Hashtable(); - internal Hashtable pages2intrefs = new Hashtable(); - internal Hashtable visited = new Hashtable(); - internal ArrayList fields = new ArrayList(); - internal RandomAccessFileOrArray file; - internal Hashtable fieldTree = new Hashtable(); - internal ArrayList pageRefs = new ArrayList(); - internal ArrayList pageDics = new ArrayList(); - internal PdfDictionary resources = new PdfDictionary(); - internal PdfDictionary form; - bool closing = false; - internal Document nd; - private Hashtable tabOrder; - private ArrayList calculationOrder = new ArrayList(); - private ArrayList calculationOrderRefs; - - internal PdfCopyFieldsImp(Stream os) : this(os, '\0') { - } - - internal PdfCopyFieldsImp(Stream os, char pdfVersion) : base(new PdfDocument(), os) { - pdf.AddWriter(this); - if (pdfVersion != 0) - base.PdfVersion = pdfVersion; - nd = new Document(); - nd.AddDocListener(pdf); - } - - internal void AddDocument(PdfReader reader, ArrayList pagesToKeep) { - if (!readers2intrefs.ContainsKey(reader) && reader.Tampered) - throw new DocumentException("The document was reused."); - reader = new PdfReader(reader); - reader.SelectPages(pagesToKeep); - if (reader.NumberOfPages == 0) - return; - reader.Tampered = false; - AddDocument(reader); - } - - internal void AddDocument(PdfReader reader) { - if (!reader.IsOpenedWithFullPermissions) - throw new ArgumentException("PdfReader not opened with owner password"); - OpenDoc(); - if (readers2intrefs.ContainsKey(reader)) { - reader = new PdfReader(reader); - } - else { - if (reader.Tampered) - throw new DocumentException("The document was reused."); - reader.ConsolidateNamedDestinations(); - reader.Tampered = true; - } - reader.ShuffleSubsetNames(); - readers2intrefs[reader] = new IntHashtable(); - readers.Add(reader); - int len = reader.NumberOfPages; - IntHashtable refs = new IntHashtable(); - for (int p = 1; p <= len; ++p) { - refs[reader.GetPageOrigRef(p).Number] = 1; - reader.ReleasePage(p); - } - pages2intrefs[reader] = refs; - visited[reader] = new IntHashtable(); - fields.Add(reader.AcroFields); - UpdateCalculationOrder(reader); - } - - private static String GetCOName(PdfReader reader, PRIndirectReference refi) { - String name = ""; - while (refi != null) { - PdfObject obj = PdfReader.GetPdfObject(refi); - if (obj == null || obj.Type != PdfObject.DICTIONARY) - break; - PdfDictionary dic = (PdfDictionary)obj; - PdfString t = (PdfString)PdfReader.GetPdfObject(dic.Get(PdfName.T)); - if (t != null) { - name = t.ToUnicodeString()+ "." + name; - } - refi = (PRIndirectReference)dic.Get(PdfName.PARENT); - } - if (name.EndsWith(".")) - name = name.Substring(0, name.Length - 1); - return name; - } - - private void UpdateCalculationOrder(PdfReader reader) { - PdfDictionary catalog = reader.Catalog; - PdfDictionary acro = (PdfDictionary)PdfReader.GetPdfObject(catalog.Get(PdfName.ACROFORM)); - if (acro == null) - return; - PdfArray co = (PdfArray)PdfReader.GetPdfObject(acro.Get(PdfName.CO)); - if (co == null || co.Size == 0) - return; - AcroFields af = reader.AcroFields; - ArrayList coa = co.ArrayList; - for (int k = 0; k < coa.Count; ++k) { - PdfObject obj = (PdfObject)coa[k]; - if (obj == null || !obj.IsIndirect()) - continue; - String name = GetCOName(reader, (PRIndirectReference)obj) ; - if (af.GetFieldItem(name) == null) - continue; - name = "." + name; - if (calculationOrder.Contains(name)) - continue; - calculationOrder.Add(name); - } - } - - internal void Propagate(PdfObject obj, PdfIndirectReference refo, bool restricted) { - if (obj == null) - return; - // if (refo != null) - // AddToBody(obj, refo); - if (obj is PdfIndirectReference) - return; - switch (obj.Type) { - case PdfObject.DICTIONARY: - case PdfObject.STREAM: { - PdfDictionary dic = (PdfDictionary)obj; - foreach (PdfName key in dic.Keys) { - if (restricted && (key.Equals(PdfName.PARENT) || key.Equals(PdfName.KIDS))) - continue; - PdfObject ob = dic.Get(key); - if (ob != null && ob.IsIndirect()) { - PRIndirectReference ind = (PRIndirectReference)ob; - if (!SetVisited(ind) && !IsPage(ind)) { - PdfIndirectReference refi = GetNewReference(ind); - Propagate(PdfReader.GetPdfObjectRelease(ind), refi, restricted); - } - } - else - Propagate(ob, null, restricted); - } - break; - } - case PdfObject.ARRAY: { - ArrayList list = ((PdfArray)obj).ArrayList; - //PdfArray arr = new PdfArray(); - foreach (PdfObject ob in list) { - if (ob != null && ob.IsIndirect()) { - PRIndirectReference ind = (PRIndirectReference)ob; - if (!IsVisited(ind) && !IsPage(ind)) { - PdfIndirectReference refi = GetNewReference(ind); - Propagate(PdfReader.GetPdfObjectRelease(ind), refi, restricted); - } - } - else - Propagate(ob, null, restricted); - } - break; - } - case PdfObject.INDIRECT: { - throw new Exception("Reference pointing to reference."); - } - } - } - - private void AdjustTabOrder(PdfArray annots, PdfIndirectReference ind, PdfNumber nn) { - int v = nn.IntValue; - ArrayList t = (ArrayList)tabOrder[annots] ; - if (t == null) { - t = new ArrayList(); - int size = annots.Size - 1; - for (int k = 0; k < size; ++k) { - t.Add(zero); - } - t.Add(v); - tabOrder[annots] = t; - annots.Add(ind); - } - else { - int size = t.Count - 1; - for (int k = size; k >= 0; --k) { - if ((int)t[k] <= v) { - t.Insert(k + 1, v); - annots.ArrayList.Insert(k + 1, ind); - size = -2; - break; - } - } - if (size != -2) { - t.Insert(0, v); - annots.ArrayList.Insert(0, ind); - } - } - } - - protected PdfArray BranchForm(Hashtable level, PdfIndirectReference parent, String fname) { - PdfArray arr = new PdfArray(); - foreach (DictionaryEntry entry in level) { - String name = (String)entry.Key; - Object obj = entry.Value; - PdfIndirectReference ind = PdfIndirectReference; - PdfDictionary dic = new PdfDictionary(); - if (parent != null) - dic.Put(PdfName.PARENT, parent); - dic.Put(PdfName.T, new PdfString(name, PdfObject.TEXT_UNICODE)); - String fname2 = fname + "." + name; - int coidx = calculationOrder.IndexOf(fname2); - if (coidx >= 0) - calculationOrderRefs[coidx] = ind; - if (obj is Hashtable) { - dic.Put(PdfName.KIDS, BranchForm((Hashtable)obj, ind, fname2)); - arr.Add(ind); - AddToBody(dic, ind); - } - else { - ArrayList list = (ArrayList)obj; - dic.MergeDifferent((PdfDictionary)list[0]); - if (list.Count == 3) { - dic.MergeDifferent((PdfDictionary)list[2]); - int page = (int)list[1]; - PdfDictionary pageDic = (PdfDictionary)pageDics[page - 1]; - PdfArray annots = (PdfArray)PdfReader.GetPdfObject(pageDic.Get(PdfName.ANNOTS)); - if (annots == null) { - annots = new PdfArray(); - pageDic.Put(PdfName.ANNOTS, annots); - } - PdfNumber nn = (PdfNumber)dic.Get(iTextTag); - dic.Remove(iTextTag); - AdjustTabOrder(annots, ind, nn); - } - else { - PdfArray kids = new PdfArray(); - for (int k = 1; k < list.Count; k += 2) { - int page = (int)list[k]; - PdfDictionary pageDic = (PdfDictionary)pageDics[page - 1]; - PdfArray annots = (PdfArray)PdfReader.GetPdfObject(pageDic.Get(PdfName.ANNOTS)); - if (annots == null) { - annots = new PdfArray(); - pageDic.Put(PdfName.ANNOTS, annots); - } - PdfDictionary widget = new PdfDictionary(); - widget.Merge((PdfDictionary)list[k + 1]); - widget.Put(PdfName.PARENT, ind); - PdfNumber nn = (PdfNumber)widget.Get(iTextTag); - widget.Remove(iTextTag); - PdfIndirectReference wref = AddToBody(widget).IndirectReference; - AdjustTabOrder(annots, wref, nn); - kids.Add(wref); - Propagate(widget, null, false); - } - dic.Put(PdfName.KIDS, kids); - } - arr.Add(ind); - AddToBody(dic, ind); - Propagate(dic, null, false); - } - } - return arr; - } - - protected void CreateAcroForms() { - if (fieldTree.Count == 0) - return; - form = new PdfDictionary(); - form.Put(PdfName.DR, resources); - Propagate(resources, null, false); - form.Put(PdfName.DA, new PdfString("/Helv 0 Tf 0 g ")); - tabOrder = new Hashtable(); - calculationOrderRefs = new ArrayList(calculationOrder); - form.Put(PdfName.FIELDS, BranchForm(fieldTree, null, "")); - PdfArray co = new PdfArray(); - for (int k = 0; k < calculationOrderRefs.Count; ++k) { - Object obj = calculationOrderRefs[k]; - if (obj is PdfIndirectReference) - co.Add((PdfIndirectReference)obj); - } - if (co.Size > 0) - form.Put(PdfName.CO, co); - } - - public override void Close() { - if (closing) { - base.Close(); - return; - } - closing = true; - CloseIt(); - } - - protected void CloseIt() { - for (int k = 0; k < readers.Count; ++k) { - ((PdfReader)readers[k]).RemoveFields(); - } - for (int r = 0; r < readers.Count; ++r) { - PdfReader reader = (PdfReader)readers[r]; - for (int page = 1; page <= reader.NumberOfPages; ++page) { - pageRefs.Add(GetNewReference(reader.GetPageOrigRef(page))); - pageDics.Add(reader.GetPageN(page)); - } - } - MergeFields(); - CreateAcroForms(); - for (int r = 0; r < readers.Count; ++r) { - PdfReader reader = (PdfReader)readers[r]; - for (int page = 1; page <= reader.NumberOfPages; ++page) { - PdfDictionary dic = reader.GetPageN(page); - PdfIndirectReference pageRef = GetNewReference(reader.GetPageOrigRef(page)); - PdfIndirectReference parent = root.AddPageRef(pageRef); - dic.Put(PdfName.PARENT, parent); - Propagate(dic, pageRef, false); - } - } - foreach (DictionaryEntry entry in readers2intrefs) { - PdfReader reader = (PdfReader)entry.Key; - try { - file = reader.SafeFile; - file.ReOpen(); - IntHashtable t = (IntHashtable)entry.Value; - int[] keys = t.ToOrderedKeys(); - for (int k = 0; k < keys.Length; ++k) { - PRIndirectReference refi = new PRIndirectReference(reader, keys[k]); - AddToBody(PdfReader.GetPdfObjectRelease(refi), t[keys[k]]); - } - } - finally { - try { - file.Close(); - reader.Close(); - } - catch { - // empty on purpose - } - } - } - pdf.Close(); - } - - internal void AddPageOffsetToField(Hashtable fd, int pageOffset) { - if (pageOffset == 0) - return; - foreach (AcroFields.Item item in fd.Values) { - ArrayList page = item.page; - for (int k = 0; k < page.Count; ++k) - page[k] = (int)page[k] + pageOffset; - } - } - - internal void CreateWidgets(ArrayList list, AcroFields.Item item) { - for (int k = 0; k < item.merged.Count; ++k) { - list.Add(item.page[k]); - PdfDictionary merged = (PdfDictionary)item.merged[k]; - PdfObject dr = merged.Get(PdfName.DR); - if (dr != null) - PdfFormField.MergeResources(resources, (PdfDictionary)PdfReader.GetPdfObject(dr)); - PdfDictionary widget = new PdfDictionary(); - foreach (PdfName key in merged.Keys) { - if (widgetKeys.ContainsKey(key)) - widget.Put(key, merged.Get(key)); - } - widget.Put(iTextTag, new PdfNumber((int)item.tabOrder[k] + 1)); - list.Add(widget); - } - } - - internal void MergeField(String name, AcroFields.Item item) { - Hashtable map = fieldTree; - StringTokenizer tk = new StringTokenizer(name, "."); - if (!tk.HasMoreTokens()) - return; - while (true) { - String s = tk.NextToken(); - Object obj = map[s]; - if (tk.HasMoreTokens()) { - if (obj == null) { - obj = new Hashtable(); - map[s] = obj; - map = (Hashtable)obj; - continue; - } - else if (obj is Hashtable) - map = (Hashtable)obj; - else - return; - } - else { - if (obj is Hashtable) - return; - PdfDictionary merged = (PdfDictionary)item.merged[0]; - if (obj == null) { - PdfDictionary field = new PdfDictionary(); - foreach (PdfName key in merged.Keys) { - if (fieldKeys.ContainsKey(key)) - field.Put(key, merged.Get(key)); - } - ArrayList list = new ArrayList(); - list.Add(field); - CreateWidgets(list, item); - map[s] = list; - } - else { - ArrayList list = (ArrayList)obj; - PdfDictionary field = (PdfDictionary)list[0]; - PdfName type1 = (PdfName)field.Get(PdfName.FT); - PdfName type2 = (PdfName)merged.Get(PdfName.FT); - if (type1 == null || !type1.Equals(type2)) - return; - int flag1 = 0; - PdfObject f1 = field.Get(PdfName.FF); - if (f1 != null && f1.IsNumber()) - flag1 = ((PdfNumber)f1).IntValue; - int flag2 = 0; - PdfObject f2 = merged.Get(PdfName.FF); - if (f2 != null && f2.IsNumber()) - flag2 = ((PdfNumber)f2).IntValue; - if (type1.Equals(PdfName.BTN)) { - if (((flag1 ^ flag2) & PdfFormField.FF_PUSHBUTTON) != 0) - return; - if ((flag1 & PdfFormField.FF_PUSHBUTTON) == 0 && ((flag1 ^ flag2) & PdfFormField.FF_RADIO) != 0) - return; - } - else if (type1.Equals(PdfName.CH)) { - if (((flag1 ^ flag2) & PdfFormField.FF_COMBO) != 0) - return; - } - CreateWidgets(list, item); - } - return; - } - } - } - - internal void MergeWithMaster(Hashtable fd) { - foreach (DictionaryEntry entry in fd) { - String name = (String)entry.Key; - MergeField(name, (AcroFields.Item)entry.Value); - } - } - - internal void MergeFields() { - int pageOffset = 0; - for (int k = 0; k < fields.Count; ++k) { - Hashtable fd = ((AcroFields)fields[k]).Fields; - AddPageOffsetToField(fd, pageOffset); - MergeWithMaster(fd); - pageOffset += ((PdfReader)readers[k]).NumberOfPages; - } - } - - public override PdfIndirectReference GetPageReference(int page) { - return (PdfIndirectReference)pageRefs[page - 1]; - } - - protected override PdfDictionary GetCatalog(PdfIndirectReference rootObj) { - PdfDictionary cat = pdf.GetCatalog(rootObj); - if (form != null) { - PdfIndirectReference refi = AddToBody(form).IndirectReference; - cat.Put(PdfName.ACROFORM, refi); - } - WriteOutlines(cat, false); - return cat; - } - - protected PdfIndirectReference GetNewReference(PRIndirectReference refi) { - return new PdfIndirectReference(0, GetNewObjectNumber(refi.Reader, refi.Number, 0)); - } - - protected internal override int GetNewObjectNumber(PdfReader reader, int number, int generation) { - IntHashtable refs = (IntHashtable)readers2intrefs[reader]; - int n = refs[number]; - if (n == 0) { - n = IndirectReferenceNumber; - refs[number] = n; - } - return n; - } - - protected bool IsVisited(PdfReader reader, int number, int generation) { - IntHashtable refs = (IntHashtable)readers2intrefs[reader]; - return refs.ContainsKey(number); - } - - protected bool IsVisited(PRIndirectReference refi) { - IntHashtable refs = (IntHashtable)visited[refi.Reader]; - return refs.ContainsKey(refi.Number); - } - - protected bool SetVisited(PRIndirectReference refi) { - IntHashtable refs = (IntHashtable)visited[refi.Reader]; - int old = refs[refi.Number]; - refs[refi.Number] = 1; - return (old != 0); - } - - protected bool IsPage(PRIndirectReference refi) { - IntHashtable refs = (IntHashtable)pages2intrefs[refi.Reader] ; - return refs.ContainsKey(refi.Number); - } - - internal override RandomAccessFileOrArray GetReaderFile(PdfReader reader) { - return file; - } - - public void OpenDoc() { - if (!nd.IsOpen()) - nd.Open(); - } - - protected internal static Hashtable widgetKeys = new Hashtable(); - protected internal static Hashtable fieldKeys = new Hashtable(); - static PdfCopyFieldsImp() { - object one = 1; - widgetKeys[PdfName.SUBTYPE] = one; - widgetKeys[PdfName.CONTENTS] = one; - widgetKeys[PdfName.RECT] = one; - widgetKeys[PdfName.NM] = one; - widgetKeys[PdfName.M] = one; - widgetKeys[PdfName.F] = one; - widgetKeys[PdfName.BS] = one; - widgetKeys[PdfName.BORDER] = one; - widgetKeys[PdfName.AP] = one; - widgetKeys[PdfName.AS] = one; - widgetKeys[PdfName.C] = one; - widgetKeys[PdfName.A] = one; - widgetKeys[PdfName.STRUCTPARENT] = one; - widgetKeys[PdfName.OC] = one; - widgetKeys[PdfName.H] = one; - widgetKeys[PdfName.MK] = one; - widgetKeys[PdfName.DA] = one; - widgetKeys[PdfName.Q] = one; - fieldKeys[PdfName.AA] = one; - fieldKeys[PdfName.FT] = one; - fieldKeys[PdfName.TU] = one; - fieldKeys[PdfName.TM] = one; - fieldKeys[PdfName.FF] = one; - fieldKeys[PdfName.V] = one; - fieldKeys[PdfName.DV] = one; - fieldKeys[PdfName.DS] = one; - fieldKeys[PdfName.RV] = one; - fieldKeys[PdfName.OPT] = one; - fieldKeys[PdfName.MAXLEN] = one; - fieldKeys[PdfName.TI] = one; - fieldKeys[PdfName.I] = one; - fieldKeys[PdfName.LOCK] = one; - fieldKeys[PdfName.SV] = one; - } - } -} +using System; +using System.Collections; +using System.IO; +using System.util; +/* + * Copyright 2004 by Paulo Soares. + * + * 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 { + /** + * + * @author psoares + */ + internal class PdfCopyFieldsImp : PdfWriter { + + private static readonly PdfName iTextTag = new PdfName("_iTextTag_"); + private static object zero = 0; + internal ArrayList readers = new ArrayList(); + internal Hashtable readers2intrefs = new Hashtable(); + internal Hashtable pages2intrefs = new Hashtable(); + internal Hashtable visited = new Hashtable(); + internal ArrayList fields = new ArrayList(); + internal RandomAccessFileOrArray file; + internal Hashtable fieldTree = new Hashtable(); + internal ArrayList pageRefs = new ArrayList(); + internal ArrayList pageDics = new ArrayList(); + internal PdfDictionary resources = new PdfDictionary(); + internal PdfDictionary form; + bool closing = false; + internal Document nd; + private Hashtable tabOrder; + private ArrayList calculationOrder = new ArrayList(); + private ArrayList calculationOrderRefs; + + internal PdfCopyFieldsImp(Stream os) : this(os, '\0') { + } + + internal PdfCopyFieldsImp(Stream os, char pdfVersion) : base(new PdfDocument(), os) { + pdf.AddWriter(this); + if (pdfVersion != 0) + base.PdfVersion = pdfVersion; + nd = new Document(); + nd.AddDocListener(pdf); + } + + internal void AddDocument(PdfReader reader, ArrayList pagesToKeep) { + if (!readers2intrefs.ContainsKey(reader) && reader.Tampered) + throw new DocumentException("The document was reused."); + reader = new PdfReader(reader); + reader.SelectPages(pagesToKeep); + if (reader.NumberOfPages == 0) + return; + reader.Tampered = false; + AddDocument(reader); + } + + internal void AddDocument(PdfReader reader) { + if (!reader.IsOpenedWithFullPermissions) + throw new ArgumentException("PdfReader not opened with owner password"); + OpenDoc(); + if (readers2intrefs.ContainsKey(reader)) { + reader = new PdfReader(reader); + } + else { + if (reader.Tampered) + throw new DocumentException("The document was reused."); + reader.ConsolidateNamedDestinations(); + reader.Tampered = true; + } + reader.ShuffleSubsetNames(); + readers2intrefs[reader] = new IntHashtable(); + readers.Add(reader); + int len = reader.NumberOfPages; + IntHashtable refs = new IntHashtable(); + for (int p = 1; p <= len; ++p) { + refs[reader.GetPageOrigRef(p).Number] = 1; + reader.ReleasePage(p); + } + pages2intrefs[reader] = refs; + visited[reader] = new IntHashtable(); + fields.Add(reader.AcroFields); + UpdateCalculationOrder(reader); + } + + private static String GetCOName(PdfReader reader, PRIndirectReference refi) { + String name = ""; + while (refi != null) { + PdfObject obj = PdfReader.GetPdfObject(refi); + if (obj == null || obj.Type != PdfObject.DICTIONARY) + break; + PdfDictionary dic = (PdfDictionary)obj; + PdfString t = (PdfString)PdfReader.GetPdfObject(dic.Get(PdfName.T)); + if (t != null) { + name = t.ToUnicodeString()+ "." + name; + } + refi = (PRIndirectReference)dic.Get(PdfName.PARENT); + } + if (name.EndsWith(".")) + name = name.Substring(0, name.Length - 1); + return name; + } + + private void UpdateCalculationOrder(PdfReader reader) { + PdfDictionary catalog = reader.Catalog; + PdfDictionary acro = (PdfDictionary)PdfReader.GetPdfObject(catalog.Get(PdfName.ACROFORM)); + if (acro == null) + return; + PdfArray co = (PdfArray)PdfReader.GetPdfObject(acro.Get(PdfName.CO)); + if (co == null || co.Size == 0) + return; + AcroFields af = reader.AcroFields; + ArrayList coa = co.ArrayList; + for (int k = 0; k < coa.Count; ++k) { + PdfObject obj = (PdfObject)coa[k]; + if (obj == null || !obj.IsIndirect()) + continue; + String name = GetCOName(reader, (PRIndirectReference)obj) ; + if (af.GetFieldItem(name) == null) + continue; + name = "." + name; + if (calculationOrder.Contains(name)) + continue; + calculationOrder.Add(name); + } + } + + internal void Propagate(PdfObject obj, PdfIndirectReference refo, bool restricted) { + if (obj == null) + return; + // if (refo != null) + // AddToBody(obj, refo); + if (obj is PdfIndirectReference) + return; + switch (obj.Type) { + case PdfObject.DICTIONARY: + case PdfObject.STREAM: { + PdfDictionary dic = (PdfDictionary)obj; + foreach (PdfName key in dic.Keys) { + if (restricted && (key.Equals(PdfName.PARENT) || key.Equals(PdfName.KIDS))) + continue; + PdfObject ob = dic.Get(key); + if (ob != null && ob.IsIndirect()) { + PRIndirectReference ind = (PRIndirectReference)ob; + if (!SetVisited(ind) && !IsPage(ind)) { + PdfIndirectReference refi = GetNewReference(ind); + Propagate(PdfReader.GetPdfObjectRelease(ind), refi, restricted); + } + } + else + Propagate(ob, null, restricted); + } + break; + } + case PdfObject.ARRAY: { + ArrayList list = ((PdfArray)obj).ArrayList; + //PdfArray arr = new PdfArray(); + foreach (PdfObject ob in list) { + if (ob != null && ob.IsIndirect()) { + PRIndirectReference ind = (PRIndirectReference)ob; + if (!IsVisited(ind) && !IsPage(ind)) { + PdfIndirectReference refi = GetNewReference(ind); + Propagate(PdfReader.GetPdfObjectRelease(ind), refi, restricted); + } + } + else + Propagate(ob, null, restricted); + } + break; + } + case PdfObject.INDIRECT: { + throw new Exception("Reference pointing to reference."); + } + } + } + + private void AdjustTabOrder(PdfArray annots, PdfIndirectReference ind, PdfNumber nn) { + int v = nn.IntValue; + ArrayList t = (ArrayList)tabOrder[annots] ; + if (t == null) { + t = new ArrayList(); + int size = annots.Size - 1; + for (int k = 0; k < size; ++k) { + t.Add(zero); + } + t.Add(v); + tabOrder[annots] = t; + annots.Add(ind); + } + else { + int size = t.Count - 1; + for (int k = size; k >= 0; --k) { + if ((int)t[k] <= v) { + t.Insert(k + 1, v); + annots.ArrayList.Insert(k + 1, ind); + size = -2; + break; + } + } + if (size != -2) { + t.Insert(0, v); + annots.ArrayList.Insert(0, ind); + } + } + } + + protected PdfArray BranchForm(Hashtable level, PdfIndirectReference parent, String fname) { + PdfArray arr = new PdfArray(); + foreach (DictionaryEntry entry in level) { + String name = (String)entry.Key; + Object obj = entry.Value; + PdfIndirectReference ind = PdfIndirectReference; + PdfDictionary dic = new PdfDictionary(); + if (parent != null) + dic.Put(PdfName.PARENT, parent); + dic.Put(PdfName.T, new PdfString(name, PdfObject.TEXT_UNICODE)); + String fname2 = fname + "." + name; + int coidx = calculationOrder.IndexOf(fname2); + if (coidx >= 0) + calculationOrderRefs[coidx] = ind; + if (obj is Hashtable) { + dic.Put(PdfName.KIDS, BranchForm((Hashtable)obj, ind, fname2)); + arr.Add(ind); + AddToBody(dic, ind); + } + else { + ArrayList list = (ArrayList)obj; + dic.MergeDifferent((PdfDictionary)list[0]); + if (list.Count == 3) { + dic.MergeDifferent((PdfDictionary)list[2]); + int page = (int)list[1]; + PdfDictionary pageDic = (PdfDictionary)pageDics[page - 1]; + PdfArray annots = (PdfArray)PdfReader.GetPdfObject(pageDic.Get(PdfName.ANNOTS)); + if (annots == null) { + annots = new PdfArray(); + pageDic.Put(PdfName.ANNOTS, annots); + } + PdfNumber nn = (PdfNumber)dic.Get(iTextTag); + dic.Remove(iTextTag); + AdjustTabOrder(annots, ind, nn); + } + else { + PdfArray kids = new PdfArray(); + for (int k = 1; k < list.Count; k += 2) { + int page = (int)list[k]; + PdfDictionary pageDic = (PdfDictionary)pageDics[page - 1]; + PdfArray annots = (PdfArray)PdfReader.GetPdfObject(pageDic.Get(PdfName.ANNOTS)); + if (annots == null) { + annots = new PdfArray(); + pageDic.Put(PdfName.ANNOTS, annots); + } + PdfDictionary widget = new PdfDictionary(); + widget.Merge((PdfDictionary)list[k + 1]); + widget.Put(PdfName.PARENT, ind); + PdfNumber nn = (PdfNumber)widget.Get(iTextTag); + widget.Remove(iTextTag); + PdfIndirectReference wref = AddToBody(widget).IndirectReference; + AdjustTabOrder(annots, wref, nn); + kids.Add(wref); + Propagate(widget, null, false); + } + dic.Put(PdfName.KIDS, kids); + } + arr.Add(ind); + AddToBody(dic, ind); + Propagate(dic, null, false); + } + } + return arr; + } + + protected void CreateAcroForms() { + if (fieldTree.Count == 0) + return; + form = new PdfDictionary(); + form.Put(PdfName.DR, resources); + Propagate(resources, null, false); + form.Put(PdfName.DA, new PdfString("/Helv 0 Tf 0 g ")); + tabOrder = new Hashtable(); + calculationOrderRefs = new ArrayList(calculationOrder); + form.Put(PdfName.FIELDS, BranchForm(fieldTree, null, "")); + PdfArray co = new PdfArray(); + for (int k = 0; k < calculationOrderRefs.Count; ++k) { + Object obj = calculationOrderRefs[k]; + if (obj is PdfIndirectReference) + co.Add((PdfIndirectReference)obj); + } + if (co.Size > 0) + form.Put(PdfName.CO, co); + } + + public override void Close() { + if (closing) { + base.Close(); + return; + } + closing = true; + CloseIt(); + } + + protected void CloseIt() { + for (int k = 0; k < readers.Count; ++k) { + ((PdfReader)readers[k]).RemoveFields(); + } + for (int r = 0; r < readers.Count; ++r) { + PdfReader reader = (PdfReader)readers[r]; + for (int page = 1; page <= reader.NumberOfPages; ++page) { + pageRefs.Add(GetNewReference(reader.GetPageOrigRef(page))); + pageDics.Add(reader.GetPageN(page)); + } + } + MergeFields(); + CreateAcroForms(); + for (int r = 0; r < readers.Count; ++r) { + PdfReader reader = (PdfReader)readers[r]; + for (int page = 1; page <= reader.NumberOfPages; ++page) { + PdfDictionary dic = reader.GetPageN(page); + PdfIndirectReference pageRef = GetNewReference(reader.GetPageOrigRef(page)); + PdfIndirectReference parent = root.AddPageRef(pageRef); + dic.Put(PdfName.PARENT, parent); + Propagate(dic, pageRef, false); + } + } + foreach (DictionaryEntry entry in readers2intrefs) { + PdfReader reader = (PdfReader)entry.Key; + try { + file = reader.SafeFile; + file.ReOpen(); + IntHashtable t = (IntHashtable)entry.Value; + int[] keys = t.ToOrderedKeys(); + for (int k = 0; k < keys.Length; ++k) { + PRIndirectReference refi = new PRIndirectReference(reader, keys[k]); + AddToBody(PdfReader.GetPdfObjectRelease(refi), t[keys[k]]); + } + } + finally { + try { + file.Close(); + reader.Close(); + } + catch { + // empty on purpose + } + } + } + pdf.Close(); + } + + internal void AddPageOffsetToField(Hashtable fd, int pageOffset) { + if (pageOffset == 0) + return; + foreach (AcroFields.Item item in fd.Values) { + ArrayList page = item.page; + for (int k = 0; k < page.Count; ++k) + page[k] = (int)page[k] + pageOffset; + } + } + + internal void CreateWidgets(ArrayList list, AcroFields.Item item) { + for (int k = 0; k < item.merged.Count; ++k) { + list.Add(item.page[k]); + PdfDictionary merged = (PdfDictionary)item.merged[k]; + PdfObject dr = merged.Get(PdfName.DR); + if (dr != null) + PdfFormField.MergeResources(resources, (PdfDictionary)PdfReader.GetPdfObject(dr)); + PdfDictionary widget = new PdfDictionary(); + foreach (PdfName key in merged.Keys) { + if (widgetKeys.ContainsKey(key)) + widget.Put(key, merged.Get(key)); + } + widget.Put(iTextTag, new PdfNumber((int)item.tabOrder[k] + 1)); + list.Add(widget); + } + } + + internal void MergeField(String name, AcroFields.Item item) { + Hashtable map = fieldTree; + StringTokenizer tk = new StringTokenizer(name, "."); + if (!tk.HasMoreTokens()) + return; + while (true) { + String s = tk.NextToken(); + Object obj = map[s]; + if (tk.HasMoreTokens()) { + if (obj == null) { + obj = new Hashtable(); + map[s] = obj; + map = (Hashtable)obj; + continue; + } + else if (obj is Hashtable) + map = (Hashtable)obj; + else + return; + } + else { + if (obj is Hashtable) + return; + PdfDictionary merged = (PdfDictionary)item.merged[0]; + if (obj == null) { + PdfDictionary field = new PdfDictionary(); + foreach (PdfName key in merged.Keys) { + if (fieldKeys.ContainsKey(key)) + field.Put(key, merged.Get(key)); + } + ArrayList list = new ArrayList(); + list.Add(field); + CreateWidgets(list, item); + map[s] = list; + } + else { + ArrayList list = (ArrayList)obj; + PdfDictionary field = (PdfDictionary)list[0]; + PdfName type1 = (PdfName)field.Get(PdfName.FT); + PdfName type2 = (PdfName)merged.Get(PdfName.FT); + if (type1 == null || !type1.Equals(type2)) + return; + int flag1 = 0; + PdfObject f1 = field.Get(PdfName.FF); + if (f1 != null && f1.IsNumber()) + flag1 = ((PdfNumber)f1).IntValue; + int flag2 = 0; + PdfObject f2 = merged.Get(PdfName.FF); + if (f2 != null && f2.IsNumber()) + flag2 = ((PdfNumber)f2).IntValue; + if (type1.Equals(PdfName.BTN)) { + if (((flag1 ^ flag2) & PdfFormField.FF_PUSHBUTTON) != 0) + return; + if ((flag1 & PdfFormField.FF_PUSHBUTTON) == 0 && ((flag1 ^ flag2) & PdfFormField.FF_RADIO) != 0) + return; + } + else if (type1.Equals(PdfName.CH)) { + if (((flag1 ^ flag2) & PdfFormField.FF_COMBO) != 0) + return; + } + CreateWidgets(list, item); + } + return; + } + } + } + + internal void MergeWithMaster(Hashtable fd) { + foreach (DictionaryEntry entry in fd) { + String name = (String)entry.Key; + MergeField(name, (AcroFields.Item)entry.Value); + } + } + + internal void MergeFields() { + int pageOffset = 0; + for (int k = 0; k < fields.Count; ++k) { + Hashtable fd = ((AcroFields)fields[k]).Fields; + AddPageOffsetToField(fd, pageOffset); + MergeWithMaster(fd); + pageOffset += ((PdfReader)readers[k]).NumberOfPages; + } + } + + public override PdfIndirectReference GetPageReference(int page) { + return (PdfIndirectReference)pageRefs[page - 1]; + } + + protected override PdfDictionary GetCatalog(PdfIndirectReference rootObj) { + PdfDictionary cat = pdf.GetCatalog(rootObj); + if (form != null) { + PdfIndirectReference refi = AddToBody(form).IndirectReference; + cat.Put(PdfName.ACROFORM, refi); + } + return cat; + } + + protected PdfIndirectReference GetNewReference(PRIndirectReference refi) { + return new PdfIndirectReference(0, GetNewObjectNumber(refi.Reader, refi.Number, 0)); + } + + protected internal override int GetNewObjectNumber(PdfReader reader, int number, int generation) { + IntHashtable refs = (IntHashtable)readers2intrefs[reader]; + int n = refs[number]; + if (n == 0) { + n = IndirectReferenceNumber; + refs[number] = n; + } + return n; + } + + protected bool IsVisited(PdfReader reader, int number, int generation) { + IntHashtable refs = (IntHashtable)readers2intrefs[reader]; + return refs.ContainsKey(number); + } + + protected bool IsVisited(PRIndirectReference refi) { + IntHashtable refs = (IntHashtable)visited[refi.Reader]; + return refs.ContainsKey(refi.Number); + } + + protected bool SetVisited(PRIndirectReference refi) { + IntHashtable refs = (IntHashtable)visited[refi.Reader]; + int old = refs[refi.Number]; + refs[refi.Number] = 1; + return (old != 0); + } + + protected bool IsPage(PRIndirectReference refi) { + IntHashtable refs = (IntHashtable)pages2intrefs[refi.Reader] ; + return refs.ContainsKey(refi.Number); + } + + internal override RandomAccessFileOrArray GetReaderFile(PdfReader reader) { + return file; + } + + public void OpenDoc() { + if (!nd.IsOpen()) + nd.Open(); + } + + protected internal static Hashtable widgetKeys = new Hashtable(); + protected internal static Hashtable fieldKeys = new Hashtable(); + static PdfCopyFieldsImp() { + object one = 1; + widgetKeys[PdfName.SUBTYPE] = one; + widgetKeys[PdfName.CONTENTS] = one; + widgetKeys[PdfName.RECT] = one; + widgetKeys[PdfName.NM] = one; + widgetKeys[PdfName.M] = one; + widgetKeys[PdfName.F] = one; + widgetKeys[PdfName.BS] = one; + widgetKeys[PdfName.BORDER] = one; + widgetKeys[PdfName.AP] = one; + widgetKeys[PdfName.AS] = one; + widgetKeys[PdfName.C] = one; + widgetKeys[PdfName.A] = one; + widgetKeys[PdfName.STRUCTPARENT] = one; + widgetKeys[PdfName.OC] = one; + widgetKeys[PdfName.H] = one; + widgetKeys[PdfName.MK] = one; + widgetKeys[PdfName.DA] = one; + widgetKeys[PdfName.Q] = one; + fieldKeys[PdfName.AA] = one; + fieldKeys[PdfName.FT] = one; + fieldKeys[PdfName.TU] = one; + fieldKeys[PdfName.TM] = one; + fieldKeys[PdfName.FF] = one; + fieldKeys[PdfName.V] = one; + fieldKeys[PdfName.DV] = one; + fieldKeys[PdfName.DS] = one; + fieldKeys[PdfName.RV] = one; + fieldKeys[PdfName.OPT] = one; + fieldKeys[PdfName.MAXLEN] = one; + fieldKeys[PdfName.TI] = one; + fieldKeys[PdfName.I] = one; + fieldKeys[PdfName.LOCK] = one; + fieldKeys[PdfName.SV] = one; + } + } +}