Преобразование файла изображения в формат PDF - PullRequest
8 голосов
/ 05 декабря 2011

Я создаю процедуру для написания простого PDF-документа на основе информации этого документа от Adobe. Создание потока для текста и фигур оказалось простым, но я застрял при вставке изображения.

Может ли кто-нибудь дать простое объяснение того, как преобразовать файл (любой формат изображения, например, gif, bmp, jpg и т. Д.) В поток PDF? Обратите внимание, что я не хочу создавать весь файл PDF, просто поток внутри файла.

С доступными мне приложениями невозможно посмотреть, как это делается в другом месте, потому что весь поток кодируется от начала до конца, и именно этот метод кодирования я пытаюсь отработать.

Несмотря на то, что я не хочу заново изобретать все колесо создания файла PDF, я хочу понять, как работает эта конкретная часть, поэтому не хочу использовать библиотеку (поэтому я не упоминаю язык, на котором я использую).

Ответы [ 2 ]

10 голосов
/ 06 декабря 2011

Вам необходимо использовать оператор Do в потоке контента.Например,

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

Im1 относится к ресурсу XObject в словаре ресурсов страницы

Например,

In the page dictionary ...
<< 

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

Object 2 0 R будет изображением XObject:

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 (использовать / Filter = / JPXDecode) или данные растрового пикселя (пропустите фильтр)

  • Раздел спецификации, в котором есть все это, составляет 8,9

  • Я не экспериментировал с декодированием LZW - я думаю, это могло бы работать для GIF

  • Вы обычно помещаете графическое состояние в стек при отображенииобраз.например,

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

Операторы q и Q выдвигают и выводят графическое состояние (важно, что оператор cm!)

5 голосов
/ 30 сентября 2013

Простая программа на C # для создания pdf из jpg на основе вышеупомянутого может быть найдена здесь.

Обратите внимание, что слово "поток" и фактический 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-04-07:

Вот более поздняя версия с комментариями, поддержкой масштабирования и страниц с несколькими 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);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...