2009-04-29 32 views
19

Ho usato questo codice con grande successo per estrarre la prima immagine trovata in ogni pagina di un PDF. Tuttavia, ora non funziona con alcuni nuovi PDF per un motivo sconosciuto. Ho usato altri strumenti (Datalogics, ecc.) Che tirano fuori le immagini bene con questi nuovi PDF. Tuttavia, non desidero acquistare Datalogics o qualsiasi strumento se riesco a utilizzare iTextSharp. Qualcuno può dirmi perché questo codice non sta trovando le immagini nel PDF?Estrai immagini usando iTextSharp

Conosciuto: i miei PDF hanno solo 1 immagine per pagina e nient'altro.

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

si può ottenere il nome dell'immagine corretto dal pdf ad esempio: image1.png – techno

risposta

19

Ho scoperto che il mio problema era che non stavo ricorsivamente cercando all'interno di moduli e gruppi di immagini. Fondamentalmente, il codice originale trovava solo le immagini che erano incorporate nella radice del documento pdf. Ecco il metodo revisionato più un nuovo metodo (FindImageInPDFDictionary) che ricerca in modo ricorsivo le immagini nella pagina. NOTA: si applicano ancora i difetti di supporto delle immagini JPEG e non compresse. Vedi il codice di R Ubben per le opzioni per correggere questi difetti. HTH qualcuno.

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

Dove si trova Utilities.GetImageEncoder ?? –

+0

Puoi ottenere il codificatore jpg da /System.Drawing.Imaging.ImageCodecInfo.GetEncoders()/ –

+1

Le immagini PDF NON sono in un formato standard - sono "matrici di valori di colore" in uno spazio cromatico specifico con un certo numero di bit per componente e potenzialmente elaborati con uno o più "filtri". I dettagli sono descritti in ISO 32000-1. come indicato qui: http://itext-general.2136553.n4.nabble.com/Extract-PDF-embedded-images-using-iText-td2172216.html –

3

Quanto sopra funzionerà solo con i file JPEG. Escludendo le immagini incorporate e i file incorporati, è necessario passare attraverso gli oggetti del sottotipo IMAGE, quindi osservare il filtro e adottare l'azione appropriata. Ecco un esempio, ipotizzando che abbiamo un PdfObject del sottotipo 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; 
        } 
       } 
      } 
     } 

questo pasticcio volontà del colore a causa della Microsoft BGR, naturalmente, ma ho voluto essere breve. Fai qualcosa di simile a "/ CCITTFaxDecode", ecc.

+0

apprezzo il codice. Ma non sto trovando alcun sottotipo di IMMAGINE nel mio PDF. Tutto è indiretto di tipo XObject. Qualche idea su come posso trovare le immagini? – Dave

+0

Bene, sono oggetti X indiretti; ogni flusso deve essere indiretto, secondo lo standard. Quello che ho fatto è stato passare attraverso gli oggetti e cercare i flussi. Dovrei averlo incluso nel codice. Lo modificherò per aggiungere quella parte. –

+0

Quindi, si guarda attraverso gli oggetti per gli stream, quindi si preleva il suo dizionario. Il tipo sarà stream e, per le immagini, il sottotipo sarà "/ Image". –

6

Il codice seguente incorpora tutte Dave e R Ubben delle idee di cui sopra, più esso restituisce un elenco completo di tutte le immagini e si occupa anche di più profondità di bit. Ho dovuto convertirlo in VB per il progetto su cui sto lavorando, però, dispiace ...

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# versione:

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

Il mio tentativo non è riuscito a 'images.Add (System.Drawing.Image.FromStream (new MemoryStream (bytes)));' perché il parametro ** non è valido. ** ps. pdf non è necessario quando si usa 'iTextSharp.text.pdf;' –

+0

Ho avuto problemi con questo: il processo bloccato su Marshal.Copy. –

7

Ecco una soluzione più semplice:

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

questo ha funzionato per me. grazie – Migs

+1

Dovrei chiarire che stavo avendo un problema nel trovare "Parameter is not valid" quando l'immagine è stata trovata ed era perché era in formato TIFF e stavo cercando di renderla una bitmap. Il metodo descritto sopra ha convertito correttamente l'immagine nel formato corretto. – Migs

3

Questo è solo un altro rifacimento delle idee degli altri, ma quello che ha funzionato per me. Qui uso @ immagine di Malco afferrando frammento con il looping di 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; 
}