Click here to Skip to main content
15,881,686 members
Articles / Programming Languages / Visual Basic

Open Door - Reporting, Charts, Enquiry Drill-Downs

Rate me:
Please Sign up or sign in to vote.
4.37/5 (11 votes)
2 Feb 2009CPOL6 min read 39.1K   2K   59  
A utility for generating user editable reports, charts, documents, enquiries
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;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions