TextRendering.cs
// 
// このコードは、DioDocs for PDF のサンプルの一部として提供されています。
// © MESCIUS inc. All rights reserved.
// 
using System;
using System.IO;
using System.Drawing;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Drawing;

namespace DsPdfWeb.Demos.Basics
{
    // このサンプルは、DsPdf におけるテキストの描画の基本を示します。
    // 2つの主なアプローチは次のとおりです:
    // - MeasureString/DrawString ペアを使用する。
    // - TextLayout を直接使用する。
    // 単純なケースでは最初のアプローチが簡単かもしれませんが、2番目の
    // アプローチ(TextLayout を使用)ははるかに強力で一般的に言えば
    // より良いパフォーマンスが得られます。
    // 詳細については、以下のコードのコメントを参照してください。
    // CharacterFormattingPaginatedTextParagraphAlign、
    // ParagraphFormattingTextAlign
    public class TextRendering
    {
        public int CreatePDF(Stream stream)
        {
            var doc = new GcPdfDocument();
            var page = doc.NewPage();
            var g = page.Graphics;
            // デフォルトでは、DsPdf は 72dpi を使用します。
            const float In = 72;

            // TextFormat クラスは、DsPdf におけるすべてのテキスト描画で使用され、
            // フォントやその他の文字書式を指定します。
            var tf = new TextFormat() { FontName = "Yu Gothic", FontSize = 12 };

            // 1.
            // 文字列が利用可能なスペースに収まることを 100% 確信しているとき、
            // ページ上の任意の場所に短い文字列を描画する最も簡単な方法は、
            // 文字列を描画する位置だけを受け取る GcGraphics.DrawString()
            // のオーバーライドを使用することです。
            g.DrawString("1. これはテスト文字列です。 このサンプルコードのコメントに詳細を記載しています。\n" +
                "(注意:単純なDrawStringのオーバーロードでも改行が許可されます。)",
                tf, new PointF(In, In));

            // 2.
            // 代わりに矩形を受け取り、加えて配置およびワードラップをオプションで受け取る別の
            // オーバーロードを利用することができ、柔軟性が少し向上します。
            // パラメータは次のとおりです。
            // - テキスト文字列。
            // - テキスト形式。
            // - レイアウト矩形。
            // - (オプション)テキストの配置(デフォルトは Leading、LTR 言語の場合は左揃えです)。
            // - (オプション)段落の配置(デフォルトは Near、上から下へのフローの場合は上揃えです)。
            // - (オプション)単語の折り返し(デフォルトは true です)。
            g.DrawString("2. 割り当てられた4インチ以上を必要とするより長いテスト文字列は、" +
                "DrawStringの実行を確認するために折り返して表示します。",
                tf,
                new RectangleF(In, In * 2, In * 4, In), //レイアウトの長方形
                //残りの3つの引数は省略可能で、イラストレーションのためにここにデフォルトを渡します:
                TextAlignment.Leading,   // leading(LTR 言語の場合は left)text align
                ParagraphAlignment.Near, // near(上から下へのフローの場合は top)para align
                true); // ワードラップ

            // 3.
            // DrawString と相補的な MeasureString() メソッドが利用できます
            // (いくつかの異なるオーバーロードがあります)。
            // テキストレイアウトの制御が必要な場合は、DrawString と組み合わせて使用できます。
            string tstr3 = "3. DrawString() メソッドで使用される MeasureString() メソッドの動作を、テスト文字列を使って確認します。";
            // 利用可能なレイアウトサイズ。
            SizeF layoutSize = new SizeF(In * 3, In * 0.8f); //利用可能なサイズ
            SizeF s = g.MeasureString(tstr3, tf, layoutSize, out int fitCharCount);
            // 渡されたサイズを赤で表示し、測定サイズを青で表示し、
            // 返されたサイズ内の文字列を境界として描画します。
            PointF pt = new PointF(In, In * 3);
            g.DrawRectangle(new RectangleF(pt, layoutSize), Color.Red);
            g.DrawRectangle(new RectangleF(pt, s), Color.Blue);
            g.DrawString(tstr3, tf, new RectangleF(pt, s));

            // 4.
            // はるかに強力かつ優れたパフォーマンスにてテキストを描画する方法は
            // TextLayout を使用することです(TextLayout は、いずれにしろ
            // DrawString/MeasureString で使用されるため、TextLayout を直接使用
            // すると作業量は半分にカットされます)。
            // TextLayoutインスタンスは、段落の書式が同じである1つ以上の段落を
            // 表します(文字の書式は、MultiFormattedText を参照してください)。
            var tl = g.CreateTextLayout();
            // テキストを追加するには、Append() または AppendLine() メソッドを使用します。
            tl.Append("4. 1番目のテスト文字列が TextLayout に追加されました。 ", tf);
            tl.Append("2番目のテスト文字列が TextLayout に追加され、同じ段落が続きます。 ", tf);
            // 改行を追加し、効果的に新しい段落を開始します。
            tl.AppendLine();
            tl.Append("3番目のテスト文字列が新しい段落の TextLayout に追加されました。 ", tf);
            tl.Append("4番目のテスト文字列で、異なる文字書式が使用されています。 ",
                new TextFormat(tf) { Font = StandardFonts.TimesBoldItalic, ForeColor = Color.DarkSeaGreen, });
            // テキストは明示的な TextFormat なしで TextLayout に追加できます。
            tl.Append("5番目のテスト文字列で TextLayout のデフォルト形式を使用しています。 ");
            // ..その場合、少なくとも Font は TextLayout の DefaultFormat で指定する必要があります。
            // そうしないと PerformLayout(以下)は失敗します。
            tl.DefaultFormat.Font = StandardFonts.TimesItalic;
            // 使用可能な最大サイズなどのレイアウトを指定します。
            // ここでは、最大幅のみを指定しますが、さらに多くのパラメータを設定できます。
            tl.MaxWidth = page.Size.Width - In * 2;
            // 段落書式を設定することもできます。ここでは、最初の行オフセット、
            // 段落と行間の間隔を設定します。
            tl.FirstLineIndent = In * 0.5f;
            tl.ParagraphSpacing = In * 0.05f;
            tl.LineSpacingScaleFactor = 0.8f;

            // すべてのテキストが追加され、レイアウトオプションが指定されると、
            // TextLayout はテキストの描画に必要なグリフを計算し、レイアウトを
            // 実行する必要があります。
            // これは、1回の呼び出しで行うことができます。
            tl.PerformLayout(true);

            // これでページに描画できます。
            pt = new PointF(In, In * 4);
            g.DrawTextLayout(tl, pt);
            // TextLayout は、測定された境界などを含むテキストに関する情報を提供します。
            // ここでは、境界ボックスをオレンジ色の赤色で描画します。
            g.DrawRectangle(new RectangleF(pt, tl.ContentRectangle.Size), Color.OrangeRed);

            // 5。
            // TextLayout を再利用して異なる段落を描画することができます。これは、
            // 同じ段落書式で別のテキストを描画する必要がある場合に便利です。
            // Clear() を呼び出すと、テキストは削除されますが、段落の書式は保持されます。
            tl.Clear();
            tl.AppendLine(); // 改行を追加し、効果的に新しい段落を開始する
            tl.Append("5. これは、同じ TextLayout を再利用してレンダリングされたテキストです。 ");
            tl.Append("TextLayout に追加されたテキストが再利用され、同じ段落が続きます。 ", tf);
            tl.Append("最後に、さらにテキストを追加しました。", tf);
            // グリフを計算してレイアウトを実行するために必要な呼び出しです。
            tl.PerformLayout(true);
            // テキストを描画します。
            g.DrawTextLayout(tl, new PointF(In, In * 5));

            // PDF ドキュメントを保存します。
            doc.Save(stream);
            return doc.Pages.Count;
        }
    }
}