【お知らせ】プログラミング記事の投稿はQiitaに移行しました。

アーカイブ

C#で任意の数のJPEGを埋め込んだPDFを生成してみました。id:n7shi:20110205に掲載したPDFを複数ページに拡張して出力しています。以下にコードを掲載します(パブリックドメイン)。コードから呼び出しているGetJpegSizeはid:n7shi:20110204に掲載しています。

public static void MakePDF(string pdf, string[] jpgs)
{
    var sizes = new Size[jpgs.Length];
    for (int i = 0; i < jpgs.Length; i++)
    {
        sizes[i] = GetJpegSize(jpgs[i]);
    }

    using (var fs = new FileStream(pdf, FileMode.Create))
    using (var sw = new StreamWriter(fs) { AutoFlush = true })
    {
        var objp = new List<long>();
        var no_j = 3 + jpgs.Length * 2;

        sw.WriteLine("%PDF-1.2");

        sw.WriteLine();
        objp.Add(fs.Position);
        sw.WriteLine("1 0 obj");
        sw.WriteLine("<< /Type /Catalog /Pages 2 0 R >>");
        sw.WriteLine("endobj");

        sw.WriteLine();
        objp.Add(fs.Position);
        sw.WriteLine("2 0 obj");
        sw.WriteLine("<<");
        sw.WriteLine("  /Type /Pages /Count {0}", jpgs.Length);
        sw.WriteLine("  /Kids");
        sw.Write("  [");
        for (int i = 0; i < jpgs.Length; i++)
        {
            if ((i & 7) == 0)
            {
                sw.WriteLine();
                sw.Write("   ");
            }
            sw.Write(" {0} 0 R", 3 + i * 2);
        }
        sw.WriteLine();
        sw.WriteLine("  ]");
        sw.WriteLine(">>");
        sw.WriteLine("endobj");

        for (int i = 0; i < jpgs.Length; i++)
        {
            var no_p = 3 + i * 2;
            var no_c = no_p + 1;
            var name = "/Jpeg" + (i + 1);
            var sz = sizes[i];

            sw.WriteLine();
            objp.Add(fs.Position);
            sw.WriteLine("{0} 0 obj", no_p);
            sw.WriteLine("<<");
            sw.WriteLine("  /Type /Page /Parent 2 0 R /Contents {0} 0 R", no_c);
            sw.WriteLine("  /MediaBox [ 0 0 {0} {1} ]", sz.Width, sz.Height);
            sw.WriteLine("  /Resources");
            sw.WriteLine("  <<");
            sw.WriteLine("    /ProcSet [ /PDF /ImageB /ImageC /ImageI ]");
            sw.WriteLine("    /XObject << {0} {1} 0 R >>", name, no_j + i);
            sw.WriteLine("  >>");
            sw.WriteLine(">>");
            sw.WriteLine("endobj");

            sw.WriteLine();
            objp.Add(fs.Position);
            sw.WriteLine("{0} 0 obj", no_c);
            var st4 = string.Format("q {0} 0 0 {1} 0 0 cm {2} Do Q",
                sz.Width, sz.Height, name);
            sw.WriteLine("<< /Length {0} >>", st4.Length);
            sw.WriteLine("stream");
            sw.WriteLine(st4);
            sw.WriteLine("endstream");
            sw.WriteLine("endobj");
        }

        for (int i = 0; i < jpgs.Length; i++)
        {
            using (var fsj = new FileStream(jpgs[i], FileMode.Open))
            {
                var name = "/Jpeg" + (i + 1);
                var sz = sizes[i];

                sw.WriteLine();
                objp.Add(fs.Position);
                sw.WriteLine("{0} 0 obj", no_j + i);
                sw.WriteLine("<<");
                sw.WriteLine("  /Type /XObject /Subtype /Image /Name {0}", name);
                sw.WriteLine("  /Filter /DCTDecode /BitsPerComponent 8 /ColorSpace /DeviceRGB");
                sw.WriteLine("  /Width {0} /Height {1} /Length {2}", sz.Width, sz.Height, fsj.Length);
                sw.WriteLine(">>");
                sw.WriteLine("stream");
                var buf = new byte[4096];
                int len;
                while ((len = fsj.Read(buf, 0, buf.Length)) > 0)
                    fs.Write(buf, 0, len);
                sw.WriteLine();
                sw.WriteLine("endstream");
                sw.WriteLine("endobj");
            }
        }

        sw.WriteLine();
        var xref = fs.Position;
        sw.WriteLine("xref");
        var size = objp.Count + 1;
        sw.WriteLine("0 {0}", size);
        sw.WriteLine("{0:0000000000} {1:00000} f", 0, 65535);
        foreach (var p in objp)
            sw.WriteLine("{0:0000000000} {1:00000} n", p, 0);
        sw.WriteLine("trailer");
        sw.WriteLine("<< /Root 1 0 R /Size {0} >>", size);
        sw.WriteLine("startxref");
        sw.WriteLine("{0}", xref);
        sw.WriteLine("%%EOF");
    }
}

StreamWriterでAutoFlushをtrueに設定しているのがポイントです。これをやらないと書き込みがバッファリングされるため、オフセットが正確に求まりません。

サンプル

上のコードを使ったサンプルです。