using System; using System.IO; using System.Collections; using System.Security.Cryptography; using iTextSharp.text; /* * $Id: PdfSmartCopy.cs,v 1.7 2008/05/13 11:25:23 psoares33 Exp $ * * * Copyright 2007 Michael Neuweiler and 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. * * This class was written by Michael Neuweiler based on hints given by Bruno Lowagie * * 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 { /** * PdfSmartCopy has the same functionality as PdfCopy, * but when resources (such as fonts, images,...) are * encountered, a reference to these resources is saved * in a cache, so that they can be reused. * This requires more memory, but reduces the file size * of the resulting PDF document. */ public class PdfSmartCopy : PdfCopy { /** the cache with the streams and references. */ private Hashtable streamMap = null; /** Creates a PdfSmartCopy instance. */ public PdfSmartCopy(Document document, Stream os) : base(document, os) { this.streamMap = new Hashtable(); } /** * Translate a PRIndirectReference to a PdfIndirectReference * In addition, translates the object numbers, and copies the * referenced object to the output file if it wasn't available * in the cache yet. If it's in the cache, the reference to * the already used stream is returned. * * NB: PRIndirectReferences (and PRIndirectObjects) really need to know what * file they came from, because each file has its own namespace. The translation * we do from their namespace to ours is *at best* heuristic, and guaranteed to * fail under some circumstances. */ protected override PdfIndirectReference CopyIndirect(PRIndirectReference inp) { PdfObject srcObj = PdfReader.GetPdfObjectRelease(inp); ByteStore streamKey = null; bool validStream = false; if (srcObj.IsStream()) { streamKey = new ByteStore((PRStream)srcObj); validStream = true; PdfIndirectReference streamRef = (PdfIndirectReference) streamMap[streamKey]; if (streamRef != null) { return streamRef; } } PdfIndirectReference theRef; RefKey key = new RefKey(inp); IndirectReferences iRef = (IndirectReferences) indirects[key]; if (iRef != null) { theRef = iRef.Ref; if (iRef.Copied) { return theRef; } } else { theRef = body.PdfIndirectReference; iRef = new IndirectReferences(theRef); indirects[key] = iRef; } if (srcObj != null && srcObj.IsDictionary()) { PdfObject type = PdfReader.GetPdfObjectRelease(((PdfDictionary)srcObj).Get(PdfName.TYPE)); if (type != null && PdfName.PAGE.Equals(type)) { return theRef; } } iRef.SetCopied(); if (validStream) { streamMap[streamKey] = theRef; } PdfObject obj = CopyObject(srcObj); AddToBody(obj, theRef); return theRef; } internal class ByteStore { private byte[] b; private int hash; private MD5 md5; private void SerObject(PdfObject obj, int level, ByteBuffer bb) { if (level <= 0) return; if (obj == null) { bb.Append("$Lnull"); return; } obj = PdfReader.GetPdfObject(obj); if (obj.IsStream()) { bb.Append("$B"); SerDic((PdfDictionary)obj, level - 1, bb); if (level > 0) { md5.Initialize(); bb.Append(md5.ComputeHash(PdfReader.GetStreamBytesRaw((PRStream)obj))); } } else if (obj.IsDictionary()) { SerDic((PdfDictionary)obj, level - 1, bb); } else if (obj.IsArray()) { SerArray((PdfArray)obj, level - 1, bb); } else if (obj.IsString()) { bb.Append("$S").Append(obj.ToString()); } else if (obj.IsName()) { bb.Append("$N").Append(obj.ToString()); } else bb.Append("$L").Append(obj.ToString()); } private void SerDic(PdfDictionary dic, int level, ByteBuffer bb) { bb.Append("$D"); if (level <= 0) return; Object[] keys = new Object[dic.Size]; dic.Keys.CopyTo(keys, 0); Array.Sort(keys); for (int k = 0; k < keys.Length; ++k) { SerObject((PdfObject)keys[k], level, bb); SerObject(dic.Get((PdfName)keys[k]), level, bb); } } private void SerArray(PdfArray array, int level, ByteBuffer bb) { bb.Append("$A"); if (level <= 0) return; ArrayList ar = array.ArrayList; for (int k = 0; k < ar.Count; ++k) { SerObject((PdfObject)ar[k], level, bb); } } internal ByteStore(PRStream str) { md5 = new MD5CryptoServiceProvider(); ByteBuffer bb = new ByteBuffer(); int level = 10; SerObject(str, level, bb); this.b = bb.ToByteArray(); md5 = null; } public override bool Equals(Object obj) { if (obj == null || !(obj is ByteStore)) return false; if (GetHashCode() != obj.GetHashCode()) return false; byte[] b2 = ((ByteStore)obj).b; if (b2.Length != b.Length) return false; int len = b.Length; for (int k = 0; k < len; ++k) { if (b[k] != b2[k]) return false; } return true; } public override int GetHashCode() { if (hash == 0) { int len = b.Length; for (int k = 0; k < len; ++k) { hash = hash * 31 + b[k]; } } return hash; } } } }