1

重複した画像をPDFファイルで利用可能な画像に置き換えようとしていますが、結果が破損しています。 PdfReader.KillIndirect複製された画像をnullにwriter.AddDirectImageSimpleしますが、以前に使用可能な画像の参照に置き換えません。ここでの問題は何ですか?

コードは次のとおりです。

using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace ReplaceDuplicateImages
{
    class Program
    {
        /// <summary>
        /// Adding one image, 2 times.
        /// </summary>
        private static void createSampleFile()
        {
            using (var pdfDoc = new Document(PageSize.A4))
            {
                var pdfWriter = PdfWriter.GetInstance(pdfDoc, new FileStream("Test.pdf", FileMode.Create));
                pdfDoc.Open();

                var table = new PdfPTable(new float[] { 1, 2 });
                table.AddCell(Image.GetInstance("01.png")); 
                table.AddCell(Image.GetInstance("01.png"));
                pdfDoc.Add(table);
            }
        }

        private static void RemoveDuplicateImagesFromPdfFile(string inFile, string outFile)
        {
            var pdfReader = new PdfReader(inFile);
            var pdfStamper = new PdfStamper(pdfReader, new FileStream(outFile, FileMode.Create));
            var writer = pdfStamper.Writer;

            var md5Service = new MD5CryptoServiceProvider();
            var enc = new UTF8Encoding();
            var imagesDictionary = new Dictionary<string, PRIndirectReference>();

            int pageNum = pdfReader.NumberOfPages;
            for (int i = 1; i <= pageNum; i++)
            {
                var page = pdfReader.GetPageN(i);
                var resources = PdfReader.GetPdfObject(page.Get(PdfName.RESOURCES)) as PdfDictionary;
                if (resources == null) continue;

                var xObject = PdfReader.GetPdfObject(resources.Get(PdfName.XOBJECT)) as PdfDictionary;
                if (xObject == null) continue;

                foreach (var name in xObject.Keys)
                {
                    var pdfObject = xObject.Get(name);
                    if (!pdfObject.IsIndirect()) continue;

                    var imgObject = PdfReader.GetPdfObject(pdfObject) as PdfDictionary;
                    if (imgObject == null) continue;

                    var subType = PdfReader.GetPdfObject(imgObject.Get(PdfName.SUBTYPE)) as PdfName;
                    if (subType == null) continue;

                    if (!PdfName.IMAGE.Equals(subType)) continue;

                    var imageBytes = PdfReader.GetStreamBytesRaw((PRStream)imgObject);
                    var md5 = enc.GetString(md5Service.ComputeHash(imageBytes));

                    if (!imagesDictionary.ContainsKey(md5)) // is it duplicate?
                    {
                        imagesDictionary.Add(md5, (PRIndirectReference)pdfObject);
                    }
                    else
                    {
                        PdfReader.KillIndirect(pdfObject); // nulls the duplicate image

                        // trying to replace it with the reference of the available image
                        var imageRef = imagesDictionary[md5];
                        var image = Image.GetInstance(imageRef);
                        Image maskImage = image.ImageMask; // it's always null here.
                        if (maskImage != null)
                            writer.AddDirectImageSimple(maskImage);
                        writer.AddDirectImageSimple(image, (PRIndirectReference)pdfObject);
                    }
                }
            }

            pdfReader.RemoveUnusedObjects();
            pdfReader.Close();
            pdfStamper.Close();
        }

        static void Main(string[] args)
        {
            createSampleFile();
            RemoveDuplicateImagesFromPdfFile("test.pdf", "Optimized.pdf");
            Process.Start("Optimized.pdf");
        }
    }
}

私は知っていPdfCopyますPdfSmartCopy。使いたくない。

4

1 に答える 1

3

冗長な情報 (画像の複製、XObject の複製、フォントの複製など) を削除したい場合は、iTextSharp の低レベル機能を使用しようとして車輪を再発明しないでください。代わりに PdfSmartCopy を使用すると、難しい作業がすべて実行されます。

コードの主な問題は、重複する画像を削除しても、それらの画像への参照を更新しないことです。そうすることで、PDFが壊れます。

同一 (バイト単位) で、重複して格納されている 2 つの画像を含む PDF があるとします (同じバイトが PDF に 2 回あります)。オブジェクトに次の参照があると仮定します: 10 0 R(最初の画像) および20 0 R(2 番目の画像)。

すべてのページの Image XObjects をループすると、10 0 R. そのイメージを保持し、その MD5 ハッシュを保存します。

imagesDictionary.Add(md5, (PRIndirectReference)pdfObject);

次に、遭遇し20 0 Rます。10 0 R両方のイメージの md5 ハッシュが一致するため、このイメージが同一であることがわかります。その画像を削除します20 0 R

PdfReader.KillIndirect(pdfObject);

そして、あなたは本当に奇妙なことをします。20 0 R複製イメージ ( ) への参照をイメージの最初のインスタンス( )の参照に変更する代わりに、10 0 Rその最初のインスタンス ( ) を取得し、元の参照 ( )imageで新たに追加します。10 0 R

writer.AddDirectImageSimple(image, (PRIndirectReference)pdfObject);

つまり、オブジェクト番号 10 の 2 番目のオブジェクトを PDF に追加しようとしますが、これは違法です (各オブジェクト番号は一意です)。iText はその行を無視します。PdfName画像に使用されたオリジナルを返すだけです。

最終的に、オブジェクト番号 20 の画像を削除したために存在しなくなったオブジェクトを正しく参照10 0 Rする画像と、存在しないオブジェクトを参照する画像を含む PDF が作成されます。20 0 R

それはあなたが経験した問題を説明しており、あなたは次のように説明しました:

「結果が壊れています」。最初の画像は問題ありませんが、2 番目の画像は完全に削除され、Adobe Reader はこの破損したファイルに関するエラー メッセージを表示します。

于 2012-12-06T14:13:48.167 に答える