TextRendering.cs
// 完毕:
using System;
using System.IO;
using System.Drawing;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Drawing;

namespace DsPdfWeb.Demos.Basics
{
    // 此示例演示了在 GcDocs.Pdf 中渲染文本的基础知识。
    // 两种主要方法是:
    // - 使用 MeasureString/DrawString 对,或者
    // - 直接使用TextLayout。
    // 虽然第一种方法在简单情况下可能更容易,
    // 第二种方法(使用 TextLayout)更强大
    // 一般来说,会产生更好的性能。
    // 请阅读下面代码中的注释以了解更多详细信息。
    // 另请参阅CharacterFormattingPaginatedTextParagraphAlign、
    // ParagraphFormattingTextAlign。
    public class TextRendering
    {
        public int CreatePDF(Stream stream)
        {
            var doc = new GcPdfDocument();
            var page = doc.NewPage();
            var g = page.Graphics;
            // 默认情况下,GcDocs.Pdf 使用 72dpi:
            const float In = 72;

            // TextFormat 类在所有 GcDocs.Pdf 文本渲染中使用来指定
            // 字体和其他字符格式:
            var tf = new TextFormat() { Font = StandardFonts.Times, FontSize = 12 };

            // 1.
            // 在页面上的任意位置呈现短字符串的最简单方法,
            // 当您 100% 确定字符串适合可用空间时,
            // 是使用 GcGraphics.DrawString() 重载接受点
            // 在哪里绘制字符串:
            g.DrawString("1.测试串。",
                tf, new PointF(In, In));

            // 2.
            // 另一种重载采用矩形,加上对齐和换行
            // 选项,也可用并提供更多的灵活性。
            // 参数为:
            // - 文本字符串;
            // - 文本格式;
            // - 布局矩形;
            // -(可选)文本对齐方式(默认为前导,LTR 语言为左对齐);
            // -(可选)段落对齐方式(默认为near、top,为从上到下的流程);
            // -(可选)自动换行(默认为true):
            g.DrawString("2. 更长的测试字符串,可能需要比分配的更多的字符串" +
                "",
                tf,
                new RectangleF(In, In * 2, In * 4, In),
                TextAlignment.Leading,
                ParagraphAlignment.Near,
                true);

            // 3.
            // 作为 DrawString 的补充,可以使用 MeasureString() 方法
            // (具有几种不同的重载),并且可以与
            // 当需要对文本布局进行更多控制时使用 DrawString:
            string tstr3 = "3. 测试字符串以演示 MeasureString() 与 DrawString() 的使用。";
            // 可用布局尺寸:
            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 实例表示一个或多个文本段落,其中
            // 相同的段落格式(字符格式可能不同,
            // 参见MultiFormattedText)。
            var tl = g.CreateTextLayout();
            // 要添加文本,请使用 Append() 或 AppendLine() 方法:
            tl.Append("4. 第一个测试字符串添加到 TextLayout。", tf);
            tl.Append("第二个测试字符串添加到 TextLayout,继续同一段落。", tf);
            // 添加换行符,有效地开始一个新段落:
            tl.AppendLine();
            tl.Append("第三个测试字符串添加到 TextLayout,一个新段落。", tf);
            tl.Append("第四个测试字符串,具有不同的字符格式。",
                new TextFormat(tf) { Font = StandardFonts.TimesBoldItalic, ForeColor = Color.DarkSeaGreen, });
            // 无需显式 TextFormat 即可将文本添加到 TextLayout:
            tl.Append("第五个测试字符串,使用 TextLayout 的默认格式。");
            // ...但在这种情况下,至少必须在
            // 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 需要计算渲染所需的字形
            // 文本,并进行布局。这可以通过
            // 单次调用:
            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.Append("5. 这是使用相同 TextLayout 重新呈现的文本。");
            tl.Append("添加到 TextLayout 的更多文本被重复使用,继续同一段落。", tf);
            tl.Append("最后,添加了一些更多的文字。", tf);
            // 计算字形并执行布局所需的调用:
            tl.PerformLayout(true);
            // 渲染文本:
            g.DrawTextLayout(tl, new PointF(In, In * 5));

            // 完毕:
            doc.Save(stream);
            return doc.Pages.Count;
        }
    }
}