【问题标题】:Extract images using iTextSharp使用 iTextSharp 提取图像
【发布时间】:2010-10-22 13:59:40
【问题描述】:

我一直在使用此代码非常成功地提取出 PDF 每一页中的第一张图片。但是,由于未知原因,它现在无法处理一些新的 PDF。我使用了其他工具(Datalogics 等),这些工具可以很好地使用这些新的 PDF 提取图像。但是,如果我可以使用 iTextSharp,我不想购买 Datalogics 或任何工具。谁能告诉我为什么这段代码找不到 PDF 中的图像?

已知:我的 PDF 每页只有 1 张图片,没有其他内容。

using iTextSharp.text;
using iTextSharp.text.pdf;
...
public static void ExtractImagesFromPDF(string sourcePdf, string outputPath)
{
    // NOTE:  This will only get the first image it finds per page.
    PdfReader pdf = new PdfReader(sourcePdf);
    RandomAccessFileOrArray raf = new iTextSharp.text.pdf.RandomAccessFileOrArray(sourcePdf);

    try
    {
        for (int pageNumber = 1; pageNumber <= pdf.NumberOfPages; pageNumber++)
        {
            PdfDictionary pg = pdf.GetPageN(pageNumber);
            PdfDictionary res = (PdfDictionary)PdfReader.GetPdfObject(pg.Get(PdfName.RESOURCES));

            PdfDictionary xobj = (PdfDictionary)PdfReader.GetPdfObject(res.Get(PdfName.XOBJECT));
            if (xobj != null)
            {
                foreach (PdfName name in xobj.Keys)
                {
                    PdfObject obj = xobj.Get(name);
                    if (obj.IsIndirect())
                    {
                        PdfDictionary tg = (PdfDictionary)PdfReader.GetPdfObject(obj);
                        PdfName type = (PdfName)PdfReader.GetPdfObject(tg.Get(PdfName.SUBTYPE));
                        if (PdfName.IMAGE.Equals(type))
                        {
                            int XrefIndex = Convert.ToInt32(((PRIndirectReference)obj).Number.ToString(System.Globalization.CultureInfo.InvariantCulture));
                            PdfObject pdfObj = pdf.GetPdfObject(XrefIndex);
                            PdfStream pdfStrem = (PdfStream)pdfObj;
                            byte[] bytes = PdfReader.GetStreamBytesRaw((PRStream)pdfStrem);
                            if ((bytes != null))
                            {
                                using (System.IO.MemoryStream memStream = new System.IO.MemoryStream(bytes))
                                {
                                    memStream.Position = 0;
                                    System.Drawing.Image img = System.Drawing.Image.FromStream(memStream);
                                    // must save the file while stream is open.
                                    if (!Directory.Exists(outputPath))
                                        Directory.CreateDirectory(outputPath);

                                    string path = Path.Combine(outputPath, String.Format(@"{0}.jpg", pageNumber));
                                    System.Drawing.Imaging.EncoderParameters parms = new System.Drawing.Imaging.EncoderParameters(1);
                                    parms.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Compression, 0);
                                    System.Drawing.Imaging.ImageCodecInfo jpegEncoder = Utilities.GetImageEncoder("JPEG");
                                    img.Save(path, jpegEncoder, parms);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    catch
    {
        throw;
    }
    finally
    {
        pdf.Close();
        raf.Close();
    }
}

【问题讨论】:

  • 你能从pdf中得到正确的图片名称吗?例如:image1.png

标签: itextsharp


【解决方案1】:

以上内容仅适用于 JPEG。排除内联图像和嵌入文件,您需要遍历子类型 IMAGE 的对象,然后查看过滤器并采取适当的操作。下面是一个示例,假设我们有一个 IMAGE 子类型的 PdfObject:

            PdfReader pdf = new PdfReader("c:\\temp\\exp0.pdf");
        int xo=pdf.XrefSize;
        for (int i=0;i<xo;i++)
        {
            PdfObject obj=pdf.GetPdfObject(i);
            if (obj!=null && obj.IsStream())
            {
                PdfDictionary pd=(PdfDictionary)obj;
                if (pd.Contains(PdfName.SUBTYPE) && pd.Get(PdfName.SUBTYPE).ToString()=="/Image")
                {
                    string filter=pd.Get(PdfName.FILTER).ToString();
                    string width=pd.Get(PdfName.WIDTH).ToString();
                    string height=pd.Get(PdfName.HEIGHT).ToString();
                    string bpp=pd.Get(PdfName.BITSPERCOMPONENT).ToString();
                    string extent=".";
                    byte [] img=null;
                    switch (filter)
                    {
                        case "/FlateDecode":
                            byte[] arr=PdfReader.FlateDecode(PdfReader.GetStreamBytesRaw((PRStream)obj),true);
                            Bitmap bmp=new Bitmap(Int32.Parse(width),Int32.Parse(height),PixelFormat.Format24bppRgb);
                            BitmapData bmd=bmp.LockBits(new Rectangle(0,0,Int32.Parse(width),Int32.Parse(height)),ImageLockMode.WriteOnly,
                                PixelFormat.Format24bppRgb);
                            Marshal.Copy(arr,0,bmd.Scan0,arr.Length);
                            bmp.UnlockBits(bmd);
                            bmp.Save("c:\\temp\\bmp1.png",ImageFormat.Png);
                            break;
                        default:
                            break;
                    }
                }
            }
        }

当然,由于 Microsoft BGR,这会弄乱颜色,但我想保持简短。为“/CCITTFaxDecode”等做类似的事情。

【讨论】:

  • 我很欣赏代码。但我在我的 PDF 中没有找到任何 IMAGE 子类型。一切都是 XObject 类型的间接。关于如何找到图像的任何想法?
  • 嗯,它们是间接的xobjects;根据标准,每个流都必须是间接的。我所做的是遍历对象并寻找流。我应该将其包含在代码中。我将对其进行编辑以添加该部分。
  • 因此,您在对象中查找流,然后获取其字典。类型将是流,对于图像,子类型将是“/Image”。
【解决方案2】:

我发现我的问题是我没有在表单和组中递归搜索图像。基本上,原始代码只会找到嵌入在 pdf 文档根目录中的图像。这是修改后的方法加上一个递归搜索页面中图像的新方法 (FindImageInPDFDictionary)。注意:仅支持 JPEG 和非压缩图像的缺陷仍然存在。有关修复这些缺陷的选项,请参阅 R Ubben 的代码。 HTH 某人。

    public static void ExtractImagesFromPDF(string sourcePdf, string outputPath)
    {
        // NOTE:  This will only get the first image it finds per page.
        PdfReader pdf = new PdfReader(sourcePdf);
        RandomAccessFileOrArray raf = new iTextSharp.text.pdf.RandomAccessFileOrArray(sourcePdf);

        try
        {
            for (int pageNumber = 1; pageNumber <= pdf.NumberOfPages; pageNumber++)
            {
                PdfDictionary pg = pdf.GetPageN(pageNumber);

                // recursively search pages, forms and groups for images.
                PdfObject obj = FindImageInPDFDictionary(pg);
                if (obj != null)
                {

                    int XrefIndex = Convert.ToInt32(((PRIndirectReference)obj).Number.ToString(System.Globalization.CultureInfo.InvariantCulture));
                    PdfObject pdfObj = pdf.GetPdfObject(XrefIndex);
                    PdfStream pdfStrem = (PdfStream)pdfObj;
                    byte[] bytes = PdfReader.GetStreamBytesRaw((PRStream)pdfStrem);
                    if ((bytes != null))
                    {
                        using (System.IO.MemoryStream memStream = new System.IO.MemoryStream(bytes))
                        {
                            memStream.Position = 0;
                            System.Drawing.Image img = System.Drawing.Image.FromStream(memStream);
                            // must save the file while stream is open.
                            if (!Directory.Exists(outputPath))
                                Directory.CreateDirectory(outputPath);

                            string path = Path.Combine(outputPath, String.Format(@"{0}.jpg", pageNumber));
                            System.Drawing.Imaging.EncoderParameters parms = new System.Drawing.Imaging.EncoderParameters(1);
                            parms.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Compression, 0);
                            System.Drawing.Imaging.ImageCodecInfo jpegEncoder = Utilities.GetImageEncoder("JPEG");
                            img.Save(path, jpegEncoder, parms);
                        }
                    }
                }
            }
        }
        catch
        {
            throw;
        }
        finally
        {
            pdf.Close();
            raf.Close();
        }


    }

     private static PdfObject FindImageInPDFDictionary(PdfDictionary pg)
    {
        PdfDictionary res =
            (PdfDictionary)PdfReader.GetPdfObject(pg.Get(PdfName.RESOURCES));


        PdfDictionary xobj =
          (PdfDictionary)PdfReader.GetPdfObject(res.Get(PdfName.XOBJECT));
        if (xobj != null)
        {
            foreach (PdfName name in xobj.Keys)
            {

                PdfObject obj = xobj.Get(name);
                if (obj.IsIndirect())
                {
                    PdfDictionary tg = (PdfDictionary)PdfReader.GetPdfObject(obj);

                    PdfName type =
                      (PdfName)PdfReader.GetPdfObject(tg.Get(PdfName.SUBTYPE));

                    //image at the root of the pdf
                    if (PdfName.IMAGE.Equals(type))
                    {
                        return obj;
                    }// image inside a form
                    else if (PdfName.FORM.Equals(type))
                    {
                        return FindImageInPDFDictionary(tg);
                    } //image inside a group
                    else if (PdfName.GROUP.Equals(type))
                    {
                        return FindImageInPDFDictionary(tg);
                    }

                }
            }
        }

        return null;

    }

【讨论】:

  • 可以从/System.Drawing.Imaging.ImageCodecInfo.GetEncoders()/获取jpg编码器
  • PDF 图像不是标准格式——它们是特定颜色空间中的“颜色值数组”,每个组件具有一定数量的位,并且可能使用一个或多个“过滤器”进行处理。详细信息在 ISO 32000-1 中进行了描述。正如这里提到的:itext-general.2136553.n4.nabble.com/…
  • System.Drawing.Image.FromStream(memStream);发生异常:参数无效。
  • var 编码器 = ImageCodecInfo.GetImageEncoders(); System.Drawing.Imaging.ImageCodecInfo jpegEncoder = encoders.FirstOrDefault(p => p.CodecName == "内置 JPEG 编解码器");
【解决方案3】:

下面的代码结合了 Dave 和 R Ubben 的上述所有想法,此外它还返回了所有图像的完整列表,并且还处理了多个位深度。不过,我不得不将它转换为 VB 用于我正在从事的项目,对此感到抱歉......

Private Sub getAllImages(ByVal dict As pdf.PdfDictionary, ByVal images As List(Of Byte()), ByVal doc As pdf.PdfReader)
    Dim res As pdf.PdfDictionary = CType(pdf.PdfReader.GetPdfObject(dict.Get(pdf.PdfName.RESOURCES)), pdf.PdfDictionary)
    Dim xobj As pdf.PdfDictionary = CType(pdf.PdfReader.GetPdfObject(res.Get(pdf.PdfName.XOBJECT)), pdf.PdfDictionary)

    If xobj IsNot Nothing Then
        For Each name As pdf.PdfName In xobj.Keys
            Dim obj As pdf.PdfObject = xobj.Get(name)
            If (obj.IsIndirect) Then
                Dim tg As pdf.PdfDictionary = CType(pdf.PdfReader.GetPdfObject(obj), pdf.PdfDictionary)
                Dim subtype As pdf.PdfName = CType(pdf.PdfReader.GetPdfObject(tg.Get(pdf.PdfName.SUBTYPE)), pdf.PdfName)
                If pdf.PdfName.IMAGE.Equals(subtype) Then
                    Dim xrefIdx As Integer = CType(obj, pdf.PRIndirectReference).Number
                    Dim pdfObj As pdf.PdfObject = doc.GetPdfObject(xrefIdx)
                    Dim str As pdf.PdfStream = CType(pdfObj, pdf.PdfStream)
                    Dim bytes As Byte() = pdf.PdfReader.GetStreamBytesRaw(CType(str, pdf.PRStream))

                    Dim filter As String = tg.Get(pdf.PdfName.FILTER).ToString
                    Dim width As String = tg.Get(pdf.PdfName.WIDTH).ToString
                    Dim height As String = tg.Get(pdf.PdfName.HEIGHT).ToString
                    Dim bpp As String = tg.Get(pdf.PdfName.BITSPERCOMPONENT).ToString

                    If filter = "/FlateDecode" Then
                        bytes = pdf.PdfReader.FlateDecode(bytes, True)
                        Dim pixelFormat As System.Drawing.Imaging.PixelFormat
                        Select Case Integer.Parse(bpp)
                            Case 1
                                pixelFormat = Drawing.Imaging.PixelFormat.Format1bppIndexed
                            Case 24
                                pixelFormat = Drawing.Imaging.PixelFormat.Format24bppRgb
                            Case Else
                                Throw New Exception("Unknown pixel format " + bpp)
                        End Select
                        Dim bmp As New System.Drawing.Bitmap(Int32.Parse(width), Int32.Parse(height), pixelFormat)
                        Dim bmd As System.Drawing.Imaging.BitmapData = 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)
                        Using ms As New MemoryStream
                            bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png)
                            bytes = ms.GetBuffer
                        End Using
                    End If
                    images.Add(bytes)
                ElseIf pdf.PdfName.FORM.Equals(subtype) Or pdf.PdfName.GROUP.Equals(subtype) Then
                    getAllImages(tg, images, doc)
                End If
            End If
        Next
    End If
End Sub

【讨论】:

    【解决方案4】:

    十二月版本:

    private IList<System.Drawing.Image> GetImagesFromPdfDict(PdfDictionary dict, PdfReader doc){
            List<System.Drawing.Image> images = new List<System.Drawing.Image>();
            PdfDictionary res = (PdfDictionary)(PdfReader.GetPdfObject(dict.Get(PdfName.RESOURCES)));
            PdfDictionary xobj = (PdfDictionary)(PdfReader.GetPdfObject(res.Get(PdfName.XOBJECT)));
    
            if (xobj != null)
            {
                foreach (PdfName name in xobj.Keys)
                {
                    PdfObject obj = xobj.Get(name);
                    if (obj.IsIndirect())
                    {
                        PdfDictionary tg = (PdfDictionary)(PdfReader.GetPdfObject(obj));
                        pdf.PdfName subtype = (pdf.PdfName)(pdf.PdfReader.GetPdfObject(tg.Get(pdf.PdfName.SUBTYPE)));
                        if (pdf.PdfName.IMAGE.Equals(subtype))
                        {
                            int xrefIdx = ((pdf.PRIndirectReference)obj).Number;
                            pdf.PdfObject pdfObj = doc.GetPdfObject(xrefIdx);
                            pdf.PdfStream str = (pdf.PdfStream)(pdfObj);
                            byte[] bytes = pdf.PdfReader.GetStreamBytesRaw((pdf.PRStream)str);
    
                            string filter = tg.Get(pdf.PdfName.FILTER).ToString();
                            string width = tg.Get(pdf.PdfName.WIDTH).ToString();
                            string height = tg.Get(pdf.PdfName.HEIGHT).ToString();
                            string bpp = tg.Get(pdf.PdfName.BITSPERCOMPONENT).ToString();
    
                            if (filter == "/FlateDecode")
                            {
                                bytes = pdf.PdfReader.FlateDecode(bytes, true);
                                System.Drawing.Imaging.PixelFormat pixelFormat;
                                switch (int.Parse(bpp))
                                {
                                    case 1:
                                        pixelFormat = System.Drawing.Imaging.PixelFormat.Format1bppIndexed;
                                        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);
                                using (var ms = new MemoryStream())
                                {
                                    bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
                                    bytes = ms.GetBuffer();
                                }
                            }
                            images.Add(System.Drawing.Image.FromStream(new MemoryStream(bytes)));
                        }
                        else if (pdf.PdfName.FORM.Equals(subtype) || pdf.PdfName.GROUP.Equals(subtype))
                        {
                            images.AddRange(GetImagesFromPdfDict(tg, doc));
                        }
                    }
                }
            }
            return images;
         }
    

    【讨论】:

    • 我在 images.Add(System.Drawing.Image.FromStream(new MemoryStream(bytes))); 失败,因为 参数无效。 ps。当你有using iTextSharp.text.pdf; 时不需要pdf
    • 我遇到了问题:进程锁定在 Marshal.Copy 上。
    • 至少在 'case 24:' 中有错误。 BitsPerComponent 表示每个颜色的位数,而不是每个像素。
    【解决方案5】:

    这是一个更简单的解决方案:

    iTextSharp.text.pdf.parser.PdfImageObject pdfImage = 
                                new iTextSharp.text.pdf.parser.PdfImageObject(imgPRStream);
                            System.Drawing.Image img = pdfImage.GetDrawingImage();
    

    【讨论】:

    • 我应该澄清一下,当找到图像时,我遇到了“参数无效”的问题,这是因为它是 TIFF 格式,我试图将其设为位图。上面概述的方法成功地将图像转换为正确的格式。
    【解决方案6】:

    这只是对他人想法的又一次重述,但对我有用。这里我使用@Malco 的图像抓取sn-p 和R Ubben 的循环:

    private IList<System.Drawing.Image> GetImagesFromPdfDict(PdfDictionary dict, PdfReader doc)
    {
        List<System.Drawing.Image> images = new List<System.Drawing.Image>();
        PdfDictionary res = (PdfDictionary)(PdfReader.GetPdfObject(dict.Get(PdfName.RESOURCES)));
        PdfDictionary xobj = (PdfDictionary)(PdfReader.GetPdfObject(res.Get(PdfName.XOBJECT)));
    
        if (xobj != null)
        {
            foreach (PdfName name in xobj.Keys)
            {
                PdfObject obj = xobj.Get(name);
                if (obj.IsIndirect())
                {
                    PdfDictionary tg = (PdfDictionary)(PdfReader.GetPdfObject(obj));
                    PdfName subtype = (PdfName)(PdfReader.GetPdfObject(tg.Get(PdfName.SUBTYPE)));
                    if (PdfName.IMAGE.Equals(subtype))
                    {
                        int xrefIdx = ((PRIndirectReference)obj).Number;
                        PdfObject pdfObj = doc.GetPdfObject(xrefIdx);
                        PdfStream str = (PdfStream)(pdfObj);
    
                        iTextSharp.text.pdf.parser.PdfImageObject pdfImage =
                            new iTextSharp.text.pdf.parser.PdfImageObject((PRStream)str);
                        System.Drawing.Image img = pdfImage.GetDrawingImage();
    
                        images.Add(img);
                    }
                    else if (PdfName.FORM.Equals(subtype) || PdfName.GROUP.Equals(subtype))
                    {
                        images.AddRange(GetImagesFromPdfDict(tg, doc));
                    }
                }
            }
        }
    
        return images;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-22
      • 1970-01-01
      • 1970-01-01
      • 2019-08-07
      • 1970-01-01
      • 2016-02-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多