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

namespace DsPdfWeb.Demos.Basics
{
    // 创建具有平衡列的多列文本布局。
    // 此示例的核心是 TextLayout.SplitAndBalance() 方法
    // 它允许在多列之间拆分文本,
    // 并平衡这些柱子,使它们的高度相似,
    // 从而使您能够制作类似杂志和报纸的文本布局。
    public class BalancedColumns
    {
        public int CreatePDF(Stream stream)
        {
            var doc = new GcPdfDocument();
            var font = StandardFonts.Times;
            var fontSize = 12;
            // 周围 1/2" 边距(72 dpi 是 GcDocs.Pdf 使用的默认分辨率):
            var margin = 72 / 2;
            var pageWidth = doc.PageSize.Width;
            var pageHeight = doc.PageSize.Height;
            var cW = pageWidth - margin * 2;
            // 章节标题的文本格式:
            var tlCaption = new TextLayout(72);
            tlCaption.DefaultFormat.Font = font;
            tlCaption.DefaultFormat.FontSize = fontSize + 4;
            tlCaption.DefaultFormat.Underline = true;
            tlCaption.MaxWidth = pageWidth;
            tlCaption.MaxHeight = pageHeight;
            tlCaption.MarginLeft = tlCaption.MarginTop = tlCaption.MarginRight = tlCaption.MarginBottom = margin;
            tlCaption.TextAlignment = TextAlignment.Center;
            // 章节标题的高度(为简单起见,使用常量):
            const float captionH = 24;
            // 主文档正文的文本布局(默认 GcDocs.Pdf 分辨率为 72dpi):
            var tl = new TextLayout(72);
            tl.DefaultFormat.Font = font;
            tl.DefaultFormat.FontSize = fontSize;
            tl.FirstLineIndent = 72 / 2;
            tl.MaxWidth = pageWidth;
            tl.MaxHeight = pageHeight;
            tl.MarginLeft = tl.MarginRight = tl.MarginBottom = margin;
            tl.MarginTop = margin + captionH;
            tl.ColumnWidth = cW * 0.3f;
            tl.TextAlignment = TextAlignment.Justified;
            // 控制附加列的 PageSplitArea 数组(第一列由
            // “主”TextLayout,对于每个附加的 TextLayout,必须提供一个 PageSplitArea -
            // 它将创建并返回一个 TextLayout,然后可用于呈现该列):
            var psas = new PageSplitArea[]
            {
                new PageSplitArea(tl) { MarginLeft = tl.MarginLeft + (cW * 0.35f) },
                new PageSplitArea(tl) { ColumnWidth = -cW * 0.3f }
            };
            // 用于控制页面之间拆分文本的拆分选项:
            var tso = new TextSplitOptions(tl)
            {
                RestMarginTop = margin,
                MinLinesInFirstParagraph = 2,
                MinLinesInLastParagraph = 2
            };
            // 生成多个“章节”,为每个章节提供大纲条目:
            const int NChapters = 20;
            doc.Pages.Add();
            for (int i = 0; i < NChapters; ++i)
            {
                // 打印所有列的章节标题:
                string chapter = $"Chapter {i + 1}";
                tlCaption.Clear();
                tlCaption.Append(chapter);
                tlCaption.PerformLayout(true);
                doc.Pages.Last.Graphics.DrawTextLayout(tlCaption, PointF.Empty);
                // 为章节添加大纲节点:
                doc.Outlines.Add(new OutlineNode(chapter, new DestinationFitV(doc.Pages.Last, null)));
                //
                // 清除上一章的文本并添加新的章节:
                tl.FirstLineIsStartOfParagraph = true;
                tl.LastLineIsEndOfParagraph = true;
                tl.Clear();
                tl.Append(Common.Util.LoremIpsum(5, 7, 9, 15, 25));
                tl.PerformLayout(true);
                // 用于保存最后一章结尾底部坐标的变量:
                float contentBottom = 0f;
                // 打印章节:
                var tls = new TextLayoutSplitter(tl);
                while (true)
                {
                    var tlCol0 = tls.SplitAndBalance(psas, tso);
                    var g = doc.Pages.Last.Graphics;
                    g.DrawTextLayout(tlCol0, PointF.Empty);
                    g.DrawTextLayout(psas[0].TextLayout, PointF.Empty);
                    g.DrawTextLayout(psas[1].TextLayout, PointF.Empty);
                    if (tls.SplitResult != SplitResult.Split)
                    {
                        // 章节结束,找出下一章的页面剩余高度:
                        contentBottom = tl.ContentY + tl.ContentHeight;
                        contentBottom = Math.Max(contentBottom, psas[0].TextLayout.ContentRectangle.Bottom);
                        contentBottom = Math.Max(contentBottom, psas[1].TextLayout.ContentRectangle.Bottom);
                        // 打印完成章节:
                        break;
                    }
                    // 在新页面上继续打印章节:
                    psas[0].MarginTop = psas[1].MarginTop = margin;
                    doc.Pages.Add();
                }
                // 下一章 - 查明当前页面是否有足够的空间来开始新的章节:
                if (contentBottom + captionH < pageHeight * 0.8f)
                {
                    // 在当前页面开始新章节:
                    contentBottom += pageHeight * 0.05f;
                    tlCaption.MarginTop = contentBottom;
                    tl.MarginTop = psas[0].MarginTop = psas[1].MarginTop = contentBottom + captionH;
                }
                else if (i < NChapters - 1)
                {
                    // 在新的一页上开始新的篇章:
                    tlCaption.MarginTop = margin;
                    tl.MarginTop = psas[0].MarginTop = psas[1].MarginTop = margin + captionH;
                    doc.Pages.Add();
                }
            }
            // 完毕:
            doc.Save(stream);
            return doc.Pages.Count;
        }
    }
}