2011-12-05 57 views
7

我正在创建一个例程来根据Adobe的this document中的信息编写一个简单的PDF文档。创建一个文本和形状的流已经证明直截了当,但我坚持插入图像。图像文件到PDF流转换

任何人都可以提供一个简单的解释如何将文件(任何图像格式,如gif,bmp,jpg等都可以)转换为PDF流吗?请注意,我不想创建整个PDF文件,只是文件中的一个流。

由于我有可用的应用程序,因此无法查看它是如何在其他地方完成的,因为整个流都是从头到尾进行编码的,而且这是我尝试解决的编码方法。

虽然我不想重新创建整个创建PDF文件轮,但我确实想了解它的这个特定部分如何工作,因此不想使用库(因此不提及语言的原因我在用)。

回答

10

您需要在内容流中使用Do运算符。例如。

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

Im1指的是在页面的资源字典

例如一个x对象资源,

In the page dictionary ... 
<< 

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

对象2 0 R将是一个图像x对象:

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 

甲几个注释: - 定位和缩放图像您必须使用cm运算符来设置当前的图形矩阵。例如

150 0 0 150 100 100 cm 

将在(100,100)的图像的位置和使图像150宽和高150

  • 你不限于JPEG文件 - 你可以使用JPEG2000s(使用/过滤=/JPXDecode)或位图的像素数据(省略过滤器)

  • 拥有这一切的规范的部分在8.9

  • 我还没有与LZW解码尝试 - 我想这可能会为GIF

  • 工作,你通常显示图像时,按下图形状态到堆栈。例如

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

的Q和Q操作符压入和弹出图形状态(重要的是,厘米操作者!)

+0

非常感谢您,您是明星!你的回答正是我需要的。 – blankabout

+0

没有probs,很高兴我可以帮助 – Jimmy

+1

一个很晚的问题;但是如何从图像中获取流数据本身?当你说位图像素数据时,你是什么意思? – user1810737

5

一个简单的C#程序来创建从基于上述一个jpg一个pdf可以在这里找到。

注意单词“流”和实际jpg流必须由\ n(或\ r \ n)分隔的细节!

问候 艾斯克拉恩

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

地址2016年4月7日:

这里是一个更新的版本与意见,支持缩放和多页JPG和完整的程序主包装(附加功能非常容易添加,因此可惜遗漏了...)

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

非常有用的代码!如果你发表评论的步骤,这将是很好的。 – Indio

+0

谢谢。有用的代码。对于访问者来说,将它们引用到可能用于创建更复杂的PDF生成代码的文章中可能会有所帮助: http://www.codeproject.com/Articles/18623/Add-Images-and-Textboxes-to- PDF –

+0

尼斯链接,谢谢!我只是将上面的图像用作古代程序发票的背景,所以只需将JPG转换为PDF,所以从未真正推广过它 - 也没有提供有助于解码我正在做的事情的评论。 .. * LOL * –