2011-12-13 25 views
4

當通過iTextSharp解碼PDF中的圖像爲FlateDecode時,圖像失真,我似乎無法弄清楚原因。爲什麼使用iTextSharp解碼FlateDecode時圖像失真?

公認的bpp是Format1bppIndexed。如果我將PixelFormat修改爲Format4bppIndexed,則圖像在某種程度上可識別(收縮,着色關閉但可讀),並以水平方式複製4次。如果我將像素格式調整爲Format8bppIndexed,則它在某種程度上也可以識別,並以水平方式重複8次。

下面的圖片是在Format1bppIndexed像素格式的方法之後。不幸的是,由於安全限制,我無法顯示其他人。

distorted image

的代碼被認爲是下面基本上是單一的解決方案,我所遇到的周圍既SO和web散落。

int xrefIdx = ((PRIndirectReference)obj).Number; 
PdfObject pdfObj = doc.GetPdfObject(xrefIdx); 
PdfStream str = (PdfStream)(pdfObj); 
byte[] bytes = PdfReader.GetStreamBytesRaw((PRStream)str); 

string filter = ((PdfArray)tg.Get(PdfName.FILTER))[0].ToString(); 
string width = tg.Get(PdfName.WIDTH).ToString(); 
string height = tg.Get(PdfName.HEIGHT).ToString(); 
string bpp = tg.Get(PdfName.BITSPERCOMPONENT).ToString(); 

if (filter == "/FlateDecode") 
{ 
    bytes = PdfReader.FlateDecode(bytes, true); 

    System.Drawing.Imaging.PixelFormat pixelFormat; 
    switch (int.Parse(bpp)) 
    { 
     case 1: 
     pixelFormat = System.Drawing.Imaging.PixelFormat.Format1bppIndexed; 
     break; 
     case 8: 
     pixelFormat = System.Drawing.Imaging.PixelFormat.Format8bppIndexed; 
     break; 
     case 24: 
     pixelFormat = System.Drawing.Imaging.PixelFormat.Format24bppRgb; 
     break; 
     default: 
     throw new Exception("Unknown pixel format " + bpp); 
    } 

    var bmp = new System.Drawing.Bitmap(Int32.Parse(width), Int32.Parse(height), pixelFormat); 
    System.Drawing.Imaging.BitmapData bmd = bmp.LockBits(new System.Drawing.Rectangle(0, 0, Int32.Parse(width), 
      Int32.Parse(height)), System.Drawing.Imaging.ImageLockMode.WriteOnly, pixelFormat); 
    Marshal.Copy(bytes, 0, bmd.Scan0, bytes.Length); 
    bmp.UnlockBits(bmd); 
    bmp.Save(@"C:\temp\my_flate_picture-" + DateTime.Now.Ticks.ToString() + ".png", ImageFormat.Png); 
} 

什麼我需要做什麼來讓自己的圖像提取的作品與FlateDecode打交道時所需?

注意:我不想用另一個庫來提取圖像。我正在尋找一個解決方案,利用只有 iTextSharp和.NET FW。如果一個解決方案通過Java(iText)存在,並且很容易移植到.NET FW位,那麼這個位就足夠了。

UPDATEImageMask屬性設置爲true,這意味着沒有顏色空間,因此隱含黑色和白色。在bpp進入1時,PixelFormat應該是Format1bppIndexed,如前所述,產生上面看到的嵌入式圖像。

UPDATE:要使用Acrobat X Pro將圖像大小提取出來,此特定示例的圖像大小列爲2403x3005。通過iTextSharp提取時,大小被列爲2544x3300。我在調試器中修改了鏡像大小,以鏡像2403x3005,但是在調用Marshal.Copy(bytes, 0, bmd.Scan0, bytes.Length);時,我收到了一個異常。

試圖讀取或寫入受保護的內存。這通常是指示其他內存已損壞的 。

我的假設是,這是由於修改了大小,因此不再對應於正在使用的字節數據。

UPDATE:每Jimmy的建議,我已驗證主叫PdfReader.GetStreamBytes返回一個字節[]長度等於寬度高度/ 8由於GetStreamBytes應當調用FlateDecode。手動調用FlateDecode和調用PdfReader.GetStreamBytes都生成了一個byte []長度爲1049401,而寬度爲 height/8爲2544 * 3300/8或1049400,因此存在差異1.不確定這是否是根本原因或不是,一個一個;但是,如果確實如此,我不確定如何解決。

UPDATE:在試圖通過kuujinbo提到的方法,我遇到了一個IndexOutOfRangeException當我嘗試了RenderImage監聽器中調用renderInfo.GetImage();。在調用FlateDecode時,前面所述的寬度*高度/ 8與字節[]長度相比是1的事實使我認爲這些都是相同的;然而,解決方案仍然沒有解決。

at System.util.zlib.Adler32.adler32(Int64 adler, Byte[] buf, Int32 index, Int32 len) 
    at System.util.zlib.ZStream.read_buf(Byte[] buf, Int32 start, Int32 size) 
    at System.util.zlib.Deflate.fill_window() 
    at System.util.zlib.Deflate.deflate_slow(Int32 flush) 
    at System.util.zlib.Deflate.deflate(ZStream strm, Int32 flush) 
    at System.util.zlib.ZStream.deflate(Int32 flush) 
    at System.util.zlib.ZDeflaterOutputStream.Write(Byte[] b, Int32 off, Int32 len) 
    at iTextSharp.text.pdf.codec.PngWriter.WriteData(Byte[] data, Int32 stride) 
    at iTextSharp.text.pdf.parser.PdfImageObject.DecodeImageBytes() 
    at iTextSharp.text.pdf.parser.PdfImageObject..ctor(PdfDictionary dictionary, Byte[] samples) 
    at iTextSharp.text.pdf.parser.PdfImageObject..ctor(PRStream stream) 
    at iTextSharp.text.pdf.parser.ImageRenderInfo.PrepareImageObject() 
    at iTextSharp.text.pdf.parser.ImageRenderInfo.GetImage() 
    at cyos.infrastructure.Core.MyImageRenderListener.RenderImage(ImageRenderInfo renderInfo) 

UPDATE:試圖改變我原來的解決方案列在這裏,以及通過kuujinbo與PDF中的不同頁面所帶來的解決方案不同的方法產生的圖像;然而,當過濾器類型爲/FlateDecode並且沒有爲給定實例生成圖像時,問題始終顯示。

+0

圖像如何失真?你可以張貼截圖嗎?這聽起來像你在某個地方出現了錯誤,或者錯誤地將事情放大了。 – ChrisF 2011-12-13 17:35:52

+0

@ChrisF剛剛添加了一個圖片 – 2011-12-13 17:38:07

+0

這與這個問題有關嗎? HTTP://計算器。com/questions/757265/how-do-pdfs-bitspercomponent-translate-to-bits-per-pixel-for-images如果不是,我會盡量深入挖掘當我有機會 – 2011-12-13 19:32:34

回答

7

試着按行復制你的數據,也許它會解決問題。

int w = imgObj.GetAsNumber(PdfName.WIDTH).IntValue; 
int h = imgObj.GetAsNumber(PdfName.HEIGHT).IntValue; 
int bpp = imgObj.GetAsNumber(PdfName.BITSPERCOMPONENT).IntValue; 
var pixelFormat = PixelFormat.Format1bppIndexed; 

byte[] rawBytes = PdfReader.GetStreamBytesRaw((PRStream)imgObj); 
byte[] decodedBytes = PdfReader.FlateDecode(rawBytes); 
byte[] streamBytes = PdfReader.DecodePredictor(decodedBytes, imgObj.GetAsDict(PdfName.DECODEPARMS)); 
// byte[] streamBytes = PdfReader.GetStreamBytes((PRStream)imgObj); // same result as above 3 lines of code. 

using (Bitmap bmp = new Bitmap(w, h, pixelFormat)) 
{ 
    var bmpData = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.WriteOnly, pixelFormat); 
    int length = (int)Math.Ceiling(w * bpp/8.0); 
    for (int i = 0; i < h; i++) 
    { 
     int offset = i * length; 
     int scanOffset = i * bmpData.Stride; 
     Marshal.Copy(streamBytes, offset, new IntPtr(bmpData.Scan0.ToInt32() + scanOffset), length); 
    } 
    bmp.UnlockBits(bmpData); 

    bmp.Save(fileName); 
} 
1

如果您可以使用最新版本(5.1.3),使用iTextSharp.text.pdf.parser名稱空間簡化了提取FlateDecode和其他圖像類型的API。基本上你使用PdfReaderContentParser來幫助你解析PDF文檔,然後你實現專門(在這種情況下)的接口IRenderListener來處理圖像。這裏有一個工作示例HTTP處理程序:

<%@ WebHandler Language="C#" Class="bmpExtract" %> 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Web; 
using iTextSharp.text; 
using iTextSharp.text.pdf; 
using iTextSharp.text.pdf.parser; 

public class bmpExtract : IHttpHandler { 
    public void ProcessRequest (HttpContext context) { 
    HttpServerUtility Server = context.Server; 
    HttpResponse Response = context.Response; 
    PdfReader reader = new PdfReader(Server.MapPath("./bmp.pdf")); 
    PdfReaderContentParser parser = new PdfReaderContentParser(reader); 
    MyImageRenderListener listener = new MyImageRenderListener(); 
    for (int i = 1; i <= reader.NumberOfPages; i++) { 
     parser.ProcessContent(i, listener); 
    } 
    for (int i = 0; i < listener.Images.Count; ++i) { 
     string path = Server.MapPath("./" + listener.ImageNames[i]); 
     using (FileStream fs = new FileStream(
     path, FileMode.Create, FileAccess.Write 
    )) 
     { 
     fs.Write(listener.Images[i], 0, listener.Images[i].Length); 
     } 
    }   
    } 
    public bool IsReusable { get { return false; } } 

    public class MyImageRenderListener : IRenderListener { 
    public void RenderText(TextRenderInfo renderInfo) { } 
    public void BeginTextBlock() { } 
    public void EndTextBlock() { } 

    public List<byte[]> Images = new List<byte[]>(); 
    public List<string> ImageNames = new List<string>(); 
    public void RenderImage(ImageRenderInfo renderInfo) { 
     PdfImageObject image = null; 
     try { 
     image = renderInfo.GetImage(); 
     if (image == null) return; 

     ImageNames.Add(string.Format(
      "Image{0}.{1}", renderInfo.GetRef().Number, image.GetFileType() 
     )); 
     using (MemoryStream ms = new MemoryStream(image.GetImageAsBytes())) { 
      Images.Add(ms.ToArray()); 
     } 
     } 
     catch (IOException ie) { 
/* 
* pass-through; image type not supported by iText[Sharp]; e.g. jbig2 
*/ 
     } 
    } 
    } 
} 

的iText的[夏普]開發團隊仍然工作的落實,所以我不能肯定地說,如果它會在你的情況工作。但它確實在this simple example PDF上工作。 (以上和我用位圖圖片嘗試過的其他一些PDF一起使用)

編輯:我一直在試驗新API,並在上面的代碼示例中犯了一個錯誤。應已初始化PdfImageObject爲空try..catch塊。以上更正。另外,當我在不支持的圖像類型上使用上述代碼(例如jbig2)時,我得到了一個不同的異常 - 「顏色深度XX不被支持」,其中「XX」是一個數字。而iTextSharp 確實支持FlateDecode在我試過的所有例子中。 (但這不是幫助你在這個的情況下,我知道)

PDF是由第三方軟件生成? (非Adobe)根據我在本書中讀到的內容,一些第三方供應商生產的PDF不完全符合規格,而iText [Sharp]無法處理其中一些PDF,而Adobe產品可以。 IIRC我已經看到特定於由iText郵件列表上的Crystal Reports生成的一些PDF導致問題的案例,here's one thread

有沒有什麼辦法可以用你使用的軟件生成一個PDF測試PDF和一些非敏感的FlateDecode圖像?那麼也許這裏有人可以幫助一點點改善。

相關問題