1

我正在尝试用 PDF 文件中的可用图像替换重复的图像,但结果已损坏。 PdfReader.KillIndirect将重复图像归零,但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");
        }
    }
}

我知道PdfCopyPdfSmartCopy。我不想使用它们。

4

1 回答 1

3

如果您想删除冗余信息(重复的图像、重复的 XObject、重复的字体,...),请不要通过尝试使用 iTextSharp 的低级功能来重新发明轮子。请改用 PdfSmartCopy,它会为您完成所有困难的工作。

您的代码的主要问题是您删除了重复的图像,但您从未更新对这些图像的引用。通过这样做,您破坏了 PDF。

假设您有一个 PDF,其中包含两个相同(按字​​节)的图像,并且冗余存储(相同的字节在 PDF 中出现两次)。假设对象具有以下引用:(10 0 R第一个图像)和20 0 R(第二个图像)。

您循环遍历每个页面的 Image XObject,然后遇到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);

换句话说:您尝试向 PDF 添加第二个对象编号为 10 的对象,这是非法的(每个对象编号都是唯一的)。iText 将忽略该行;它只会返回PdfName用于图像的原件。

最终,您会得到一个 PDF,其中包含一个正确引用10 0 R的图像和一个引用的图像,该图像引用了20 0 R一个不再存在的对象,因为您删除了对象编号为 20 的图像。

这解释了您遇到的问题,您将其描述为:

“结果已损坏”。第一张图片没问题,但第二张完全消除了,Adobe 阅读器显示有关此损坏文件的错误消息。

于 2012-12-06T14:13:48.167 回答