2011-12-05 13 views
7

Sto creando una routine per scrivere un semplice documento PDF basato sulle informazioni in this document di Adobe. La creazione di uno stream per il testo e le forme si è dimostrata semplice ma sono bloccato sull'inserimento di un'immagine.File di immagine in PDF Stream Conversion

Qualcuno potrebbe fornire una semplice spiegazione su come convertire un file (qualsiasi formato di immagine come gif, bmp, jpg ecc. Andrebbe bene) in un flusso PDF? Si noti che non voglio creare un intero file PDF, ma solo uno stream all'interno del file.

Con le app che ho a disposizione non è possibile guardare come è fatto altrove perché l'intero stream è codificato dall'inizio alla fine ed è questo metodo di codifica che sto cercando di elaborare.

Anche se non voglio re-inventare l'intera creazione di una ruota di file PDF, voglio capire come funziona questa particolare parte quindi non voglio usare una libreria (quindi il motivo per non menzionare la lingua Sto usando).

risposta

10

È necessario utilizzare l'operatore Do all'interno del flusso di contenuti. Per esempio.

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

Im1 si riferisce ad una risorsa XObject nel dizionario risorse della pagina

Per esempio,

In the page dictionary ... 
<< 

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

Oggetto 2 0 R sarà un XObject immagine:

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 poche note: - per posizionare e ridimensionare l'immagine è necessario impostare la matrice grafica corrente utilizzando l'operatore cm. Ad esempio

150 0 0 150 100 100 cm 

posizionerà l'immagine a (100,100) e rendere l'immagine 150 larghezza e 150 alta.

  • non si è limitati a JPEG - è possibile utilizzare JPEG2000s (uso/Filter =/JPXDecode) o dati bitmap pixel (omettere il filtro)

  • La sezione della specifica che ha tutto questo in è 8.9

  • non ho sperimentato con LZW decodifica - Credo che potrebbe funzionare per GIF

  • in genere si preme lo stato grafico sullo stack quando si visualizza un'immagine. per esempio.

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

operatori Q e Q push e pop lo stato grafico (soprattutto, l'operatore cm!)

+0

Grazie mille, sei una stella! La tua risposta era esattamente ciò di cui avevo bisogno. – blankabout

+0

nessun problema, felice di poter aiutare – Jimmy

+1

Una domanda tardiva; ma come si ottengono i dati del flusso stesso da un'immagine? Quando dici dati pixel bitmap, cosa intendi? – user1810737

5

Un semplice programma C# per creare un PDF da un jpg base di quanto sopra può essere trovato qui.

Nota il dettaglio che la parola "stream" e il jpg-stream corrente DEVONO essere separati da \ n (o \ r \ n) !!!

migliori saluti 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(); 
     } 
    } 
} 

ADD 2016/04/07:

Ecco una versione successiva con i commenti, il supporto per il ridimensionamento e le pagine multi-JPG ed un main-wrapper del programma completo (la funzionalità aggiuntiva era così facile da aggiungere, che sarebbe un peccato omettere ...)

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

Codice molto utile! Sarebbe bello se tu commentassi i passaggi. – Indio

+0

Grazie. Codice utile Potrebbe essere utile per i visitatori indirizzarli a questo articolo che potrebbe essere utilizzato per creare codici di generazione PDF più complessi: http://www.codeproject.com/Articles/18623/Add-Images-and-Textboxes-to- PDF –

+0

Bel collegamento, grazie! Ho solo fatto quanto sopra per usare un'immagine come sfondo per una fattura fatta da un programma antico, quindi avevo solo bisogno di convertire un JPG in PDF, quindi non l'ho mai generalizzato, né ho fatto i commenti che avrebbero aiutato a decodificare quello che stavo facendo. .. * LOL * –