Help us understand the problem. What is going on with this article?

C# の PDF作成用ライブラリ

More than 1 year has passed since last update.

今日はいつもにもまして自分用 Tips.

PDFのドキュメントを作成する必要があって、ライブラリを調査してみた。今の希望は、特定のフォーマットの文章を簡単に作る事。

iTextSharp

ゼロからPDFをがっつりレイアウトして作れる。

にだいたい書いている。サンプル書いてみた。コードは見ての通りで、ゴリゴリに座標を指定して、テキストとかラインとかを置いていく感じ。

using iTextSharp.text;
using iTextSharp.text.pdf;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PDFSample
{
    class Program
    {
        static void Main(string[] args)
        {

            var doc = new Document(PageSize.A4.Rotate());
            var stream = new MemoryStream();

            var pw = PdfWriter.GetInstance(doc, stream);

            doc.Open();
            var pdfContentByte = pw.DirectContent;
            var bf = BaseFont.CreateFont(@"c:\windows\fonts\msgothic.ttc,0", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

            // A4 595x842 pt = 210x297  

            pdfContentByte.SetFontAndSize(bf, 24);
            ShowTextAligned(pdfContentByte, 20, 561, "Fitness Club Membership");

            DrawLine(pdfContentByte, 2, 551, 840, 551);

            pdfContentByte.SetFontAndSize(bf, 11);
            ShowTextAligned(pdfContentByte, 20, 530, "You can enojy Muscle Training and high-quality protein.");

            pdfContentByte.SetFontAndSize(bf, 18);
            ShowTextAligned(pdfContentByte, 20, 480, "Name: Mr.Tsuyoshi Ushio Age: 46");
            ShowTextAligned(pdfContentByte, 20, 457, "Membership: Gold");

            doc.Close();

            using (BinaryWriter w = new BinaryWriter(File.OpenWrite(@"result.pdf"))) {
                w.Write(stream.ToArray());
            }
            Console.WriteLine("See the result.pdf");
        }
        private static void ShowTextAligned(PdfContentByte pdfContentByte, float x, float y, string text, int alignment = Element.ALIGN_LEFT, float rotaion = 0)
        {
            pdfContentByte.BeginText();
            pdfContentByte.ShowTextAligned(alignment, text, x, y, rotaion);
            pdfContentByte.EndText();
        }
        private static void DrawLine(PdfContentByte pdfContentByte, float fromX, float fromY, float toX, float toY)
        {
            pdfContentByte.MoveTo(fromX, fromY);
            pdfContentByte.LineTo(toX, toY);
            pdfContentByte.ClosePathStroke();
        }
    }
}

難しくないけど、相当ゴリゴリ。わからなかったは、PDFの座標をどう考えるか?

このページに解説があった 1pt ≒ 0.352777mm のポイント単位で計算。

A4 の場合、約 595x842 pt ⇔ 210x297 mm

出来た。

2017-12-23_22h43_18.png

感想

めんどい。ざっくりとしたのを作りたいだけなのに、座標の指定とかフォントの指定とか面倒すぎる。ちなみに、私は、あるプロジェクトの検証用になんでもいいからPDFを生成するサンプルを書きたかっただけだ。もっと楽なアプローチはないだろうか?

CommonMark.NET + HtmlRenderer.PdfSharp

そうだ、誰かがレイアウトをしてくれたらいいじゃないの?ということは、MDから、PDFに変換してくれるようなものがあれば最高じゃね?と思って調べたのがこちら。

流石に一発変換では無理だけど、MD -> HTML, HTML -> PDF ならイケるとわかった。

コード

スパイクなので冗長なコードが入っているけど、こいつで、MD-> HTML変換。しかもこのライブラリは昔あったライブラリよりずっと高速らしい。

            var html = CommonMark.CommonMarkConverter.Convert(md);

これで、HTML -> PDF 変換。これは楽すぎる。

           using (MemoryStream ms = new MemoryStream())
            {
                var pdf = TheArtOfDev.HtmlRenderer.PdfSharp.PdfGenerator.GeneratePdf(html, PdfSharp.PageSize.A4);
                pdf.Save(ms);
                result = ms.ToArray();
            }
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MdToPDF
{
    class Program
    {
        static void Main(string[] args)
        {
            var md = @"
# Fitness club membership

## Mr.Tsuyoshi Ushio

You are the member of the muscle fitness.

* Zama branch membership
* Gold membershiop
* Protein service included

## Message from manger

You can enjoy muscle training with great equipments. Grow your muscle!
";
            var byteArray = Encoding.UTF8.GetBytes(md);
            var stream = new MemoryStream(byteArray);

            using (var reader = new System.IO.StreamReader(stream))
            using (var writer = new System.IO.StreamWriter("result.html"))
            {
                CommonMark.CommonMarkConverter.Convert(reader, writer);
            }

            var html = CommonMark.CommonMarkConverter.Convert(md);
            Console.WriteLine(html);
            Console.ReadLine();

            Byte[] result = null;
            using (MemoryStream ms = new MemoryStream())
            {
                var pdf = TheArtOfDev.HtmlRenderer.PdfSharp.PdfGenerator.GeneratePdf(html, PdfSharp.PageSize.A4);
                pdf.Save(ms);
                result = ms.ToArray();
            }
            using (BinaryWriter w = new BinaryWriter(File.OpenWrite(@"result.pdf")))
            {
                w.Write(result);
            }


        }
    }
}

結果

これは楽だ。これでいいんじゃない?

pdf.png

私はサンプルだから、こんなもんでいいけど、ガチだと、例えば、同じMD でも、GitHub みたいなもうちょっとカッコよいレイアウトにしたいとか思いそう。その場合は、wikiに書いてあったけど、HtmlFormatter.csを継承して、好きな部分をオーバーライドしてあげればいいみたい。

まとめ

HTMLに一旦変換してからやると、世界は広がって楽になった! ちなみに、HTMLからPDFのライブラリは、C#ネイティブでなくてよければ、wkhtmltopdfというのがあるらしい。ちなみに、印刷の実行に関しては PrinterSettings クラスで可能っぽい。サンプルがついている。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away