0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PDFSharpでフォントを指定する

Posted at

初めに

C#でPDFを作成する際にPDFSharpを使用したのですが、フォントの指定で色々ありましたので記録しておきます。

実行環境

実行環境は以下の通りです。

  • .NET 8.0 - Windows
  • PDFSharp-WPF 6.1.1

PDFSharpは1.50で数年止まっていたのですが、2023年11月に.NET環境に対応した6.0.0が出て開発が再開されたようです。NuGetでインストールしましょう。

今回はPDFSharp(core)ではなくPDFSharp-WPFを使用します。
coreの方は自分でFontResolverを書く必要があります。
PDFSharp-WPFの方はWindows専用になりますがデフォルトのリゾルバでインストールされたフォントを参照することができます。
PDFSharp-GDIもWindows専用ですがPDFSharp-WPFよりも古い仕組みなのでおそらく本記事の後半の方法は使えないと思われます。

コード例

コード例を見た方が早いですね。

// リゾルバを自分で書く必要はない
//GlobalFontSettings.FontResolver = new MyFontResolver();

var document = new PdfDocument();
var page = document.AddPage();
page.Size = PageSize.A4;
page.Orientation = PageOrientation.Landscape;
XGraphics gfx = XGraphics.FromPdfPage(page);

var styles = new[] { XFontStyleEx.Regular, XFontStyleEx.Bold, XFontStyleEx.Italic, XFontStyleEx.BoldItalic };
var fonts = new[] { "IPAexMincho", "Noto Serif CJK JP", "Noto Serif CJK JP SemiBold", "Noto Serif Light", "Noto Serif Cond" };   // フォントは英語名で指定する


var text = "こんにちは、世界! Hello, world!";
int cnt = 0;
foreach(var fontname in fonts)
{
    foreach (var style in styles)
    {
        var font = new XFont(fontname, 20, style);
        var x = 50.0 + (cnt % 2) 400;
        var y = 50.0 + (cnt / 2) * 30;
        gfx.DrawString(text, font, XBrushes.Black, x, y);
        cnt++;
    }
}

document.Save("output.pdf");

ポイントはフォントを英語名で指定することです。
フォントファミリーの英語名の確認方法は以下の記事を参照

"*.ttc"のフォントは使用できません。指定してもエラーになります。"*.ttf", "*.otf"のフォントは概ね使えそうです。[設定]>[個人用設定]>[フォント]でフォントファイルの形式を確認してください。当たり前ですが日本語の文字が登録されていないフォントで日本語を表示することはできません。

このフォント指定方法はレガシーを引きずった形なので、Bold以外のウェイト指定はフォントファミリー名に含める必要があったりします。

PDFSharp はフォントを常にPDFファイルに埋め込みます。これによりPDFを表示する環境によってフォントが再現されないという事態は起きませんが、ファイルはその分大きくなります。特にOpenTypeフォントでPostScriptアウトラインを持つフォントの場合、(文書で使用した文字だけではなく)フォント全体を埋め込みます。そのためPDFファイルサイズが非常に大きくなることがあります。

このサンプルプログラムでは、Noto Serif CJK JPがPostScriptアウトラインを持つフォントのため、PDFファイルサイズが58MBにもなりました。

出力結果

出力結果は以下の通り。

image.png

以下、私の環境で
IPAexMincho は実際には1種類のフォントファイルしかありません。Regularフォントが加工されて太字や斜体が出力されています。

Noto Serif CJK JPはRegularに対応するBoldはありますがItalicはありません。しかし斜体も出力されています。

Noto Serif CJK JP SemiBoldは本来ならNoto Serif CJK JPのフォントファミリーに入るはずです。BoldやItalicを指定しても反映されないのはそのあたりが原因でしょう。

Noto Serif LightNoto Serif Condは英文フォントで本来Noto Serifフォントファミリーに入ります。
Noto Serif LightはItalicフォントファイルがあるのにBoldどころかItalicも反映されません。
Noto Serif Condはフォントファイルが揃っているからか、Regular Bold Italic BoldItalicとも反映されます。
Noto Serif LightでBold指定することはないでしょうがItalicが効かないのは意外でした。

Typefaceを使ったフォント指定

PDFSharp-WPFならpublic XFont(Typeface typeface, double emSize)を使えばもっと素直に文字ウェイトや文字幅を指定することができます。

var document = new PdfDocument();
var page = document.AddPage();
page.Size = PageSize.A4;
page.Orientation = PageOrientation.Landscape;
XGraphics gfx = XGraphics.FromPdfPage(page);

var fonts = new[] { "IPAexMincho", "Noto Serif CJK JP", "Noto Serif" };   // フォントは英語名で指定する
var styles = new[] { FontStyles.Normal, FontStyles.Italic };
var weights = new[] { FontWeights.Light, FontWeights.Normal, FontWeights.Bold };
var stretches = new[] { FontStretches.Normal, FontStretches.Condensed };

var text = "友達のWa!";
int cnt = 0;
foreach (var fontname in fonts)
{
    foreach (var style in styles)
    {
        foreach (var weight in weights)
        {
            foreach(var stretch in stretches)
            {
                var typeface = new Typeface(new FontFamily(fontname), style, weight, stretch);
                var font = new XFont(typeface, 20);
                var x = 50.0 + (cnt % 6) * 120;
                var y = 50.0 + (cnt / 6) * 30;
                gfx.DrawString(text, font, XBrushes.Black, x, y);
                cnt++;
            }
        }
    }
}

document.Save("output.pdf");

こちらのフォントファミリーも英語名で指定します。タイプフェイスの指定は新しい方法に沿ったものですので以下の記事を見た方が良いでしょう。

出力結果

image.png

以下、私の環境で
IPAexMincho は1種類のフォントファイルしかありません。LightウェイトやCondensed幅は表現されず、BoldウェイトとItalicスタイルは加工されたものが出力されています。

Noto Serif CJK JPもCondensed幅のフォントはなく、Normal幅で表現されます。ウェイトはLightもBoldもあります(LightとNormalの区別がつきにくいですが)。Italicはありませんが加工されたものが出力されています。

スタイル ウェイト 存在 表現
Normal Light Normal
Normal Light Condensed - -(Normal-Light-Normal)
Normal Normal Normal
Normal Normal Condensed - -(Normal-Normal-Normal)
Normal Bold Normal
Normal Bold Condensed - -(Normal-Bold-Normal)
Italic Light Normal -
Italic Light Condensed - -(Italic-Light-Normal)
Italic Normal Normal -
Italic Normal Condensed - -(Italic-Normal-Normal)
Italic Bold Normal -
Italic Bold Condensed - -(Italic-Bold-Normal)

Noto Serifは種類が揃っていて、フォントのない2種類以外は正しく表現されています。
フォントが無い場合のフォールバックはウェイト(weight)よりも幅(stretch)を優先して採用するようです。

スタイル ウェイト 存在 表現
Normal Light Normal
Normal Light Condensed - -(Normal-Normal-Condensed)
Normal Normal Normal
Normal Normal Condensed
Normal Bold Normal
Normal Bold Condensed
Italic Light Normal
Italic Light Condensed - -(Italic-Normal-Condensed)
Italic Normal Normal
Italic Normal Condensed
Italic Bold Normal
Italic Bold Condensed
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?