2011-12-13 49 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图像?那么也许这里有人可以帮助一点点改善。