Một đơn giản C# chương trình để tạo ra một pdf từ jpg dựa trên trên có thể tìm thấy ở đây.
Lưu ý chi tiết rằng từ "luồng" và luồng jpg thực tế PHẢI được phân tách bằng \ n (hoặc \ r \ n) !!!
Trân trọng 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:
Đây là một phiên bản mới hơn với ý kiến, hỗ trợ để mở rộng và trang đa JPG và một đầy đủ chương trình wrapper chính (các chức năng bổ sung rất dễ dàng để thêm vào, nó sẽ là một điều đáng tiếc để bỏ qua ...)
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);
}
}
}
Cảm ơn bạn rất nhiều, bạn là một ngôi sao! Câu trả lời của bạn chính xác là những gì tôi cần. – blankabout
không có probs, vui mừng tôi có thể giúp – Jimmy
Khá một câu hỏi muộn; nhưng làm cách nào để bạn tự lấy dữ liệu luồng từ một hình ảnh? Khi bạn nói dữ liệu bitmap pixel, ý của bạn là gì? – user1810737