2011-12-05 20 views
7

Estoy creando una rutina para escribir un documento PDF simple basado en la información en this document de Adobe. Crear una secuencia para texto y formas ha resultado sencillo, pero estoy atascado al insertar una imagen.Image File to PDF Stream Conversion

¿Alguien podría proporcionar una explicación simple de cómo convertir un archivo (cualquier formato de imagen como gif, bmp, jpg, etc. estaría bien) a un flujo de PDF? Tenga en cuenta que no quiero crear un archivo PDF completo, solo una secuencia dentro del archivo.

Con las aplicaciones que tengo disponibles no es posible ver cómo se hace en otro lugar porque toda la secuencia está codificada de principio a fin y este es el método de codificación que estoy tratando de resolver.

Si bien no quiero volver a inventar todo el documento creando una rueda de archivo PDF, quiero entender cómo funciona esta parte en particular, así que no quiero usar una biblioteca (por lo tanto, la razón para no mencionar el idioma Estoy usando).

Respuesta

10

Debe utilizar el operador Do dentro de la secuencia de contenido. P.ej.

.... /Im1 Do ....... 

Im1 se refiere a un recurso XObject en el diccionario de recursos de la página

Por ejemplo,

In the page dictionary ... 
<< 

... 
/Contents 1 0 R 
/Resources << /XObject << /Im1 2 0 R >> >> 
... 
>> 

Objeto 2 0 R será un XObject imagen:

2 0 obj << /Type /XObject /Subtype /Image /Width 100 /Height 100 /ColorSpace /DeviceRGB /BitsPerComponent 8 /Length 10000 /Filter /DCTDecode >> 
stream 
JPEG DATA HERE 
endstream 
endobj 

A algunas notas: - para posicionar y escalar la imagen debe establecer la matriz de gráficos actual utilizando el operador cm. Por ejemplo

150 0 0 150 100 100 cm 

será posicionar la imagen en (100,100) y hacer que la imagen 150 ancho y 150 alta.

  • Usted no está limitado a los archivos JPEG - se puede utilizar JPEG2000s (uso/Filtro =/JPXDecode) o datos de mapa de bits de píxeles (omitir el filtro)

  • La sección de la especificación que tiene todo esto en 8.9 es

  • no he experimentado con decodificación LZW - supongo que podría funcionar para GIF

  • que normalmente empuja el estado de los gráficos en la pila cuando se muestra una imagen. p.ej.

    q a b c d e f cm /Im1 Do Q

La Q y Q operadores de inserción y extracción del estado de los gráficos (importante, el operador cm!)

+0

Muchas gracias, eres una estrella! Tu respuesta fue exactamente lo que necesitaba. – blankabout

+0

sin problemas, me alegro de poder ayudar – Jimmy

+1

Toda una pregunta tardía; pero, ¿cómo obtienes los datos de la transmisión de una imagen? Cuando dice datos de píxeles de mapa de bits, ¿qué quiere decir? – user1810737

5

Un simple programa en C# para crear un PDF a partir de un archivo JPG en base a lo anterior se puede encontrar aquí.

Tenga en cuenta el detalle de que la palabra "stream" y la actual jpg-stream DEBEN estar separadas por \ n (o \ r \ n) !!!

Saludos Eske Rahn

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.IO; 
using System.Drawing; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void WriStr(FileStream Out, string s) 
     { 
      Out.Write(System.Text.Encoding.ASCII.GetBytes(s), 0, s.Length); 
     } 
     static void Main(string[] args) 
     { 

      string InJpg = @"InFile.JPG"; 
      string OutPdf = @"OutFile.pdf"; 

      byte[] buffer = new byte[8192]; 
      var stream = File.OpenRead(InJpg); // The easiest way to get the metadata is to temporaryly load it as a BMP 
      Bitmap bmp = (Bitmap)Bitmap.FromStream(stream); 
      int w = bmp.Width; String wf = (w * 72/bmp.HorizontalResolution).ToString().Replace(",", "."); 
      int h = bmp.Height; ; string hf = (h * 72/bmp.VerticalResolution).ToString().Replace(",", "."); 
      stream.Close(); 

      FileStream Out = File.Create(OutPdf); 

      var lens = new List<long>(); 

      WriStr(Out, "%PDF-1.5\r\n"); 

      lens.Add(Out.Position); 
      WriStr(Out, lens.Count.ToString() + " 0 obj " + "<</Type /Catalog\r\n/Pages 2 0 R>>\r\nendobj\r\n"); 

      lens.Add(Out.Position); 
      WriStr(Out, lens.Count.ToString() + " 0 obj " + "<</Count 1/Kids [ <<\r\n" + 
         "/Type /Page\r\n" + 
         "/Parent 2 0 R\r\n" + 
         "/MediaBox [0 0 " + wf + " " + hf + "]\r\n" + 
         "/Resources<< /ProcSet [/PDF /ImageC]\r\n /XObject <</Im1 4 0 R >> >>\r\n" + 
         "/Contents 3 0 R\r\n" + 
         ">>\r\n ]\r\n" + 
         ">>\r\nendobj\r\n"); 

      string X = "\r\n" + 
       "q\r\n" + 
       "" + wf + " 0 0 " + hf + " 0 0 cm\r\n" + 
       "/Im1 Do\r\n" + 
       "Q\r\n"; 
      lens.Add(Out.Position); 
      WriStr(Out, lens.Count.ToString() + " 0 obj " + "<</Length " + X.Length.ToString() + ">>" + 
         "stream" + X + "endstream\r\n" + 
         "endobj\r\n"); 
      lens.Add(Out.Position); 
      WriStr(Out, lens.Count.ToString() + " 0 obj " + "<</Name /Im1" + 
         "/Type /XObject\r\n" + 
         "/Subtype /Image\r\n" + 
         "/Width " + w.ToString() + 
         "/Height " + h.ToString() + 
         "/Length 5 0 R\r\n" + 
         "/Filter /DCTDecode\r\n" + 
         "/ColorSpace /DeviceRGB\r\n" + 
         "/BitsPerComponent 8\r\n" + 
         ">> stream\r\n"); 
      long Siz = Out.Position; 
      var in1 = File.OpenRead(InJpg); 
      while (true) 
      { 
       var len = in1.Read(buffer, 0, buffer.Length); 
       if (len != 0) Out.Write(buffer, 0, len); else break; 
      } 
      in1.Close(); 
      Siz = Out.Position - Siz; 
      WriStr(Out, "\r\nendstream\r\n" + 
         "endobj\r\n"); 

      lens.Add(Out.Position); 
      WriStr(Out, lens.Count.ToString() + " 0 obj " + Siz.ToString() + " endobj\r\n"); 

      long startxref = Out.Position; 

      WriStr(Out, "xref\r\n" + 
         "0 " + (lens.Count + 1).ToString() + "\r\n" + 
         "0000000000 65535 f\r\n"); 
      foreach (var L in lens) 
       WriStr(Out, (10000000000 + L).ToString().Substring(1) + " 00000 n\r\n"); 
      WriStr(Out, "trailer\r\n" + 
         "<<\r\n" + 
         " /Size " + (lens.Count + 1).ToString() + "\r\n" + 
         " /Root 1 0 R\r\n" + 
         ">>\r\n" + 
         "startxref\r\n" + 
         startxref.ToString() + "\r\n%%EOF"); 
      Out.Close(); 
     } 
    } 
} 

AÑADIR 07/04/2016:

Aquí está una versión posterior con los comentarios, apoyo para ampliar y páginas de múltiples JPG y una programa completo main-wrapper (la funcionalidad adicional era tan fácil de agregar, que sería una pena omitir ...)

using System; 
using System.Collections.Generic; 
//using System.Linq; 
using System.Text; 
using System.Drawing; 
using System.IO; 

namespace Jpg2Pdfdir 
{ 
    class Program 
    { 
     static void WriStr(FileStream Out, string s, params object[] args) 
     { 
      s = string.Format(s, args); 
      Out.Write(System.Text.Encoding.ASCII.GetBytes(s), 0, s.Length); 
     } 
     //Combined from http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf 

     /// <summary> 
     /// Create a pdf from a list of jpgs, optionally stretching&compressing them. (Note the scaling is a display&print thing only, the jpg_stream itself is included unchanged) 
     /// </summary> 
     /// <param name="InJpgs">List of Jpg (full)names</param> 
     /// <param name="OutPdf">Name of the pdf to create</param> 
     /// <param name="StretchWs">For each jpg the width-scaling factor, fall back to the last given, and if none to 1.0</param> 
     /// <param name="StretchHs">For each jpg the height scalling, none positive or missing value is replaced with the width scale value (to keep aspect ratio)</param> 
     static void JpgToPdf(List<string> InJpgs, string OutPdf, List<Double> StretchWs, List<Double> StretchHs) 
     { 
      if (StretchWs==null || StretchWs.Count==0)StretchWs=new List<double>{1.0}; //default to unchanged 
      if (StretchHs==null)StretchHs=new List<double>{}; //Default to all with same aspect ratio 

      byte[] buffer = new byte[8192]; 
      int[] ws = new int[InJpgs.Count]; 
      int[] hs = new int[InJpgs.Count]; 
      string[] wfs = new string[InJpgs.Count]; 
      string[] hfs = new string[InJpgs.Count]; 
      for (int i=0;i<InJpgs.Count;i++) { 
       double StretchW=i<StretchWs.Count?StretchWs[i]:StretchWs[StretchWs.Count-1]; // Fall back to the last 
       double StretchH=i<StretchHs.Count && 0<StretchHs[i]?StretchHs[i]:StretchW; //Fall back to same X-Y scale. 
       System.IO.FileStream stream = File.OpenRead(InJpgs[i]); 
       // The easiest way to get the metadata is to temporaryly load the file, ignoring the ImageData! 
       using (Image Img = Image.FromStream(stream,false, false)) { //Last parameter: vaildateImageData=FALSE 
        ws[i] = Img.Width ; wfs[i] = (ws[i] * StretchW * 72/Img.HorizontalResolution).ToString(System.Globalization.CultureInfo.InvariantCulture); 
        hs[i] = Img.Height; hfs[i] = (hs[i] * StretchH * 72/Img.VerticalResolution ).ToString(System.Globalization.CultureInfo.InvariantCulture); 
       } 
       stream.Close(); 
      } 

      FileStream Out = File.Create(OutPdf); 

      //Holds the object-positions (Or lengths before) 
      var lens = new List<long>(); 

      //Must have header 
      WriStr(Out, "%PDF-1.5\r\n"); 

      //Obj 1 The catalog, pointing to the pages in object 2 
      lens.Add(Out.Position); 
      WriStr(Out, "{0} 0 obj " + "<</Type /Catalog\r\n/Pages 2 0 R>>\r\nendobj\r\n", lens.Count); 

      //Obj 2 The pageS, with inline object for the Kids object of type Page 
      //Note the size in the MediaBox, The resource for the image in object 4 (Streams can not be inline objects) 
      //And the Contents in object 3, and that the Parent of the Page points back to object 2 self. 
      lens.Add(Out.Position); 
      String Pages = ""; 
      for (int i = 0; i < InJpgs.Count; i++) { 
       Pages+= "<<\r\n"+ 
         "/Type /Page\r\n" + 
         "/Parent 2 0 R\r\n" + 
         "/MediaBox [0 0 " + wfs[i] + " " + hfs[i] + "]\r\n" + 
         "/Resources << /XObject <</Im"+(1+i).ToString()+" "+(4+3*i).ToString()+" 0 R >> >>\r\n" + 
         "/Contents "+(3+3*i).ToString()+" 0 R\r\n" + 
         ">>\r\n"; 
      } 
      WriStr(Out, "{0} 0 obj <</Type /Pages /Count {1} /Kids [{2}]\r\n" + 
         ">>\r\nendobj\r\n", lens.Count, InJpgs.Count, Pages); 

      for (int i = 0; i < InJpgs.Count; i++) { 

       // Obj 3+3i. The command stream to do the image Im# in a string, so the length can be evaluated. Note this is WITHOUT the leading and trailing CRLF 
       string X = 
        "q\r\n" + 
        "" + wfs[i] + " 0 0 " + hfs[i] + " 0 0 cm\r\n" + 
        "/Im"+(1+i).ToString()+" Do\r\n" + 
        "Q"; 
       lens.Add(Out.Position); 
       WriStr(Out, lens.Count.ToString() + " 0 obj <</Length {0}>> stream\r\n" + 
          "{1}\r\n" + 
          "endstream\r\n" + 
          "endobj\r\n", X.Length, X); 

       // Obj 4+3i of type XObject containing the jpg-stream, and with a reference to the length that will be stored in object 5 when known 
       lens.Add(Out.Position); 
       WriStr(Out, "{0} 0 obj <</Name /Im{1}" + 
          "/Type /XObject\r\n" + 
          "/Subtype /Image\r\n" + 
          "/Width {2}"+ 
          "/Height {3}"+ 
          "/Length {4} 0 R\r\n" + 
          "/Filter /DCTDecode\r\n" + 
          "/ColorSpace /DeviceRGB\r\n" + 
          "/BitsPerComponent 8\r\n" + 
          ">> stream\r\n", lens.Count, 1+i, ws[i], hs[i], 5+3*i); 
       long Siz = Out.Position; 
       var in1 = File.OpenRead(InJpgs[i]); 
       while (true) 
       { 
        var len = in1.Read(buffer, 0, buffer.Length); 
        if (len != 0) Out.Write(buffer, 0, len); else break; 
       } 
       in1.Close(); 
       Siz = Out.Position - Siz; // The difference is the stream-length 
       WriStr(Out, "\r\nendstream\r\n" + 
          "endobj\r\n"); 

       // Obj 5+3i the stream length (not known at the time of the begining of object 4 
       lens.Add(Out.Position); 
       WriStr(Out, "{0} 0 obj {1} endobj\r\n",lens.Count ,Siz); 

      } 
      //Pointer for XREF-table saved 
      long startxref = Out.Position; 

      //The XREF table, note the zero'th object, it is the free-object-list not used here 
      WriStr(Out, "xref\r\n" + 
         "0 {0}\r\n" + 
         "0000000000 65535 f\r\n", lens.Count+1); 
      //Position of each object saved entered in the XREF 
      foreach (var L in lens) 
       WriStr(Out, (10000000000 + L).ToString().Substring(1) + " 00000 n\r\n"); 
      //The trailer, pointing to object 1 as the Root 
      //and the saved startxref last, judt before the %%EOF marker 
      WriStr(Out, "trailer\r\n" + 
         "<<\r\n" + 
         " /Size {0}\r\n" + 
         " /Root 1 0 R\r\n" + 
         ">>\r\n" + 
         "startxref\r\n", lens.Count+1); 
      WriStr(Out, startxref.ToString() + "\r\n" + 
         "%%EOF"); 
      Out.Close(); 
     } 



     static void Main(string[] args) 
     { 
      if (0==args.Length) { 
       Console.WriteLine("Call with {JpgName [ScaleXY | ScaleW ScaleH] } [OutputName] , OutputName defaults to first .jpg -> .pdf"); 
       return; 
      } 
      List<string> basejpgs = new List<string>(); 
      double WrkDouble; 
      List<double> ScaFacWs = new List<double>(); 
      List<double> ScaFacHs = new List<double>(); 
      int i = 0; 
      while(i<args.Length && System.IO.File.Exists(args[i]) && System.IO.Path.GetExtension(args[i]).ToLower()==".jpg") { 
       basejpgs.Add(args[i]); 
       i++; 
       if (i<args.Length && Double.TryParse(args[i], out WrkDouble)) { 
        i++; 
       } else { 
        WrkDouble=1.0; //Default to 1x 
       } 
       ScaFacWs.Add(WrkDouble); 
       if (i < args.Length && Double.TryParse(args[i], out WrkDouble)) 
       { 
        i++; 
       } else { 
        WrkDouble=-1; //Default to same x-y scale 
       } 
       ScaFacHs.Add(WrkDouble); 
      } 
      //if (basejpgs.Count==0) basejpgs.Add("Red16x16.jPg"); //####DEBUG#### 
      string destpdf = basejpgs[0]; 
      if (i<args.Length && (System.IO.Path.GetExtension(args[i]).ToLower()==".pdf" || System.IO.Path.GetExtension(args[i])=="")) { 
       destpdf=args[i]; 
       i++; 
      } 
      if (i<args.Length) { 
       Console.WriteLine("Too many arguments, or could not decode???"); 
      } 
      destpdf = System.IO.Path.ChangeExtension(destpdf, ".PDF"); 
      JpgToPdf(basejpgs, destpdf, ScaFacWs, ScaFacHs); 
     } 
    } 
} 
+2

¡Código muy útil! Sería bueno si comenta los pasos. – Indio

+0

Gracias. Código útil Puede ser útil para los visitantes referirlos a este artículo que podría usarse para crear códigos de generación de PDF más complejos: http://www.codeproject.com/Articles/18623/Add-Images-and-Textboxes-to- PDF –

+0

Buen enlace, gracias! Solo hice lo anterior para usar una imagen como fondo para una factura hecha por un programa antiguo, así que solo necesitaba convertir un JPG a PDF, por lo que nunca lo generalicé en general, ni hice los comentarios que ayudarían a decodificar lo que estaba haciendo. .. * LOL * –