2009-04-29 2 views
19

Я использовал этот код с большим успехом, чтобы вытащить первое изображение, найденное на каждой странице PDF. Тем не менее, теперь он не работает с некоторыми новыми PDF-файлами по неизвестной причине. Я использовал другие инструменты (Datalogics и т. Д.), Которые отлично извлекают изображения с помощью этих новых PDF-файлов. Тем не менее, я не хочу покупать Datalogics или любой инструмент, если я могу использовать iTextSharp. Может кто-нибудь сказать мне, почему этот код не находит изображения в PDF?Извлечь изображения с помощью iTextSharp

Известны: у моих 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(); 
    } 
} 
+0

Вы можете получить правильное название изображения из Pdf, например: image1.png – techno

ответ

19

Я обнаружил, что моя проблема заключалась в том, что я не искал рекурсивно поиск внутри форм и групп изображений. В принципе, исходный код мог найти только изображения, которые были встроены в корень 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; 

    } 
+6

Где Utilities.GetImageEncoder ?? –

+0

Вы можете получить jpg-кодировщик из /System.Drawing.Imaging.ImageCodecInfo.GetEncoders()/ –

+1

Изображения в формате PDF НЕ относятся к стандартным форматам - они представляют собой «массивы значений цвета» в определенном цветовом пространстве с определенным количеством бит в компонент и потенциально обрабатывается одним или несколькими «фильтрами». Подробности описаны в ISO 32000-1. как упоминалось здесь: http://itext-general.2136553.n4.nabble.com/Extract-PDF-embedded-images-using-iText-td2172216.html –

3

Вышеупомянутое будет работать только с JPEG. Исключая встроенные изображения и встроенные файлы, вам нужно пройти через объекты IMAP подтипа, затем посмотреть на фильтр и предпринять соответствующие действия. Вот пример, предполагая, что у нас есть PdfObject подтипа IMAGE:

  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» и т. Д.

+0

Я ценю код. Но я не нахожу никаких подтипов IMAGE в своем PDF-файле. Все косвенно относится к типу XObject. Любые идеи о том, как я могу найти изображения? – Dave

+0

Ну, это косвенные xobjects; каждый поток должен быть непрямым, согласно стандарту. То, что я сделал, это пройти через объекты и искать потоки. Я должен был включить это в код. Я отредактирую его, чтобы добавить эту часть. –

+0

Итак, вы просматриваете объекты для потоков, а затем извлекаете его словарь. Тип будет потоком, а для изображений подтипом будет «/ Image». –

6

Следующий код включает в себя все 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

De C# версия:

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; 
    } 
+0

Mine потерпел неудачу в 'images.Add (System.Drawing.Image.FromStream (новый MemoryStream (bytes)));' потому что ** Параметр недействителен. ** ps. pdf не требуется, когда вы используете 'iTextSharp.text.pdf;' –

+0

У меня были проблемы с этим: процесс заблокирован на Marshal.Copy. –

7

Вот простое решение:

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

это сработало для меня. спасибо – Migs

+1

Я должен уточнить, что у меня возникла проблема с получением «Параметр недействителен», когда изображение было найдено, и это было потому, что это был формат TIFF, и я пытался сделать его растровым. Вышеописанный метод успешно преобразовал изображение в правильный формат. – Migs

3

Это просто еще один передел идей других, но тот, который работал для меня. Здесь я использую @ изображение Malco в захватывая фрагмент с зацикливанием 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; 
} 
Смежные вопросы