C#からpdfファイルの作成

C#からpdfファイルを作成するためのライブラリは様々ありますが、PDFSharpを使った方法をご紹介。PDFSharpはWindowsForms用(GDI+)用とWPF用とが分かれていますが、今回はWindowsForms用を使ってみます。

WindowsFormsアプリケーションを作成後、Nugetから

PM> install-package pdfsharp

としてライブラリをインストールします。PDFSharpを使うサンプルをアレコレ説明しようかと思ったのですが、PDFSharpのページに色々あります。線描画や矩形描画、画像の張り付けも簡単です。

ただこのPDFSharp、日本語対応できていません。そのためDrawStringというAPIで日本語文字を書こうとしても文字化けするのです。なんてこった。そこで文字列を画像として作成して、張り付けるという乱暴な簡単な回避方法をご紹介。

 PdfSharpJapaneseExtension.cs 

namespace PdfByCsharpWinFormsApp
{
    using System;
    using System.Drawing;
    using System.IO;
    using System.Drawing.Imaging;
    using PdfSharp.Drawing;

    public static class PdfSharpJapaneseExtension
    {
        public static void DrawJapanese(this XGraphics target, String text, Font font,
                                        Brush brush, Rectangle drawRect)
        {
            Size drawSize = GetTextDrawingBmpSize(text, font);

            // Graphicsオブジェクトを使うにはBitmapの場合24bppor32bppが必要です。
            using(Bitmap tempImage = new Bitmap(drawSize.Width, drawSize.Height, PixelFormat.Format24bppRgb))
            {
                // 一時Bitmapに描画
                using (Graphics graphics = Graphics.FromImage(tempImage))
                {
                    graphics.FillRectangle(Brushes.White, new Rectangle(Point.Empty, drawSize));
                    graphics.DrawString(text, font, brush, PointF.Empty);
                }
                // 保存
                string tempImageFileName = Path.GetTempFileName() + ".bmp";
                tempImage.Save(tempImageFileName, ImageFormat.Bmp);

                // 画像ファイルからPDFへ描画
                XImage image = XImage.FromFile(tempImageFileName);
                target.DrawImage(image, new Point(drawRect.X, drawRect.Y));
            }
        }

        private static Size GetTextDrawingBmpSize(String text, Font font)
        {
            using(Bitmap graphicsImage = new Bitmap(10, 10, PixelFormat.Format24bppRgb))
            using(Graphics graphics = Graphics.FromImage(graphicsImage))
            {
                SizeF imageSize = graphics.MeasureString(text, font);
                return new Size((int)(Math.Ceiling(imageSize.Width)), (int)(Math.Ceiling(imageSize.Height)));
            }
        }
    }
}

まず文書内容とフォントから画像として張り付ける時のサイズを算出します。そのあとそれを一旦一時ファイルとして保存し、ファイルからPDFSharpの画像描画APIを使って画像を貼り付けます。これを使うコードは以下のような感じ。

Form1.cs(ボタンが1つあるだけのForm)

namespace PdfByCsharpWinFormsApp
{
    using System;
    using System.Drawing;
    using System.Diagnostics;
    using System.Windows.Forms;
    using PdfSharp.Drawing;
    using PdfSharp.Pdf;

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void testButton_Click(object sender, EventArgs e)
        {
            PdfDocument document = new PdfDocument();
            PdfPage page = document.AddPage();
            XGraphics graphics = XGraphics.FromPdfPage(page);

            // 文字列描画(PDFSharpのAPI)
            graphics.DrawString("日本語は文字化けします!", new XFont("MS UI ゴシック", 20.0f), XBrushes.Black,
                new XRect(0, 0, page.Width, page.Height), XStringFormat.TopLeft);

            // 文字列描画(今回の方法)
            graphics.DrawJapanese("日本語も文字化けしない!", new Font("MS UI ゴシック", 20.0f), Brushes.Black,
                new Rectangle(0, 100, (int)page.Width, (int)page.Height));

            const string filename = "DrawJapaneseSample.pdf";
            document.Save(filename);
            Process.Start(filename);
        }
    }
}

これを実行した結果はこんな感じ。

test_pdf_by_csharp

PDFSharpのDrawStringでは日本語文字が文字化けするのに対して、今回の方法では画像として張り付けてるだけなので文字化けしません。拡張メソッドとして作ったので似たような使い方ができています。

ただ今回の方法にはいろいろ問題が・・。拡張メソッド内で一時ファイルを削除しようとしてもロックされており削除できません。また画像として張り付けているので背景があるような場合はどうなるのか、、今回は背景を白固定で文字を描画しましたが背景に文字等があった場合は画像領域がべたで上書きされてしまいそうです。・・・怖くて確認できない。

C#からpdfファイルの作成」への1件のフィードバック

  1. おーとおもって参考にさせてもらいました。
    ちなみにstreamでやったらどうでしょうか?
    イメージ的にはこんなかんじです。

    public void DrawJapanese(XGraphics target, String text, Font font,
    Brush brush, RectangleF drawRect)
    {
    Size drawSize = GetTextDrawingBmpSize(text, font);

    using (Bitmap tempImage = new Bitmap(drawSize.Width, drawSize.Height, PixelFormat.Format24bppRgb))
    {
    // 一時Bitmapに描画
    using (Graphics graphics = Graphics.FromImage(tempImage))
    {
    graphics.FillRectangle(Brushes.White, new Rectangle(Point.Empty, drawSize));
    graphics.DrawString(text, font, brush, PointF.Empty);

    }

    using (MemoryStream stream = new MemoryStream())
    {
    tempImage.Save(stream, ImageFormat.Bmp);
    using (Image image = Image.FromStream(stream, true, true))
    using (Bitmap bmp = new Bitmap(image))
    using (XImage ximage = XImage.FromGdiPlusImage(bmp))
    {
    target.DrawImage(ximage, new Point((int)drawRect.X, (int)drawRect.Y));
    }
    }
    }
    }

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中