2012-12-06 18 views
0

我試圖用PDF文件中的可用圖像替換重複圖像,但結果已損壞。 PdfReader.KillIndirect無效複製圖像,但writer.AddDirectImageSimple不會將其替換爲先前可用圖像的參考。這裏有什麼問題?替換PDF文件中的重複圖像

下面是代碼:

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。我不想使用它們。

+0

你在看什麼?什麼是輸出? – plinth

+0

我說過,「結果已損壞」。第一個圖像是好的,但第二個圖像完全消失,Adobe閱讀器顯示關於此損壞文件的錯誤消息。 – VahidN

+0

請解釋你爲什麼不想使用'PdfSmartCopy'。 –

回答

2

如果你想刪除冗餘信息(重複的圖像,複製XObject的,重複的字體,......),請不要嘗試使用iTextSharp的的低級別的功能推倒重來。改爲使用PdfSmartCopy,它將爲您完成所有困難的工作。

你的代碼的主要問題是你刪除了重複的圖像,但你永遠不會更新這些圖像的引用。通過這樣做,你可以打破PDF。

假設你有一個包含相同(按字​​節)兩個圖像PDF文件,並使用冗餘存儲(相同字節的PDF兩次)。假設對象具有以下參考文獻:10 0 R(第一幅圖像)和20 0 R(第二幅圖像)。

您遍歷每個頁面的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號到PDF,這是違法的(每個對象號是唯一的)。 iText將忽略該行;它將僅返回用於圖像的原始PdfName

最終,你最終與具有圖像的PDF是正確引用10 0 R和引用20 0 R,其指向不再存在,因爲你刪除與對象編號的圖像物件的影像20

這解釋了您遇到的問題,並且您描述爲:

「結果已損壞」。第一個圖像是好的,但第二個圖像完全消失,Adobe閱讀器顯示關於此損壞文件的錯誤消息。