BalancedColumns.vb
''
'' This code is part of Document Solutions for PDF demos.
'' Copyright (c) MESCIUS inc. All rights reserved.
''
Imports System.IO
Imports System.Drawing
Imports GrapeCity.Documents.Pdf
Imports GrapeCity.Documents.Text

'' Creates a multi-column text layout with balanced columns.
'' The heart of this sample is the TextLayout.SplitAndBalance() method
'' which allows splitting a text between multiple columns,
'' AND balance those columns so that their heights are similar,
'' thus allowing to produce magazine- and newspaper-like text layouts.
Public Class BalancedColumns
    Function CreatePDF(ByVal stream As Stream) As Integer
        Dim doc = New GcPdfDocument()
        Dim font = StandardFonts.Times
        Dim fontSize = 12
        '' 1/2" margins all around (72 dpi is the default resolution used by DsPdf):
        Dim margin = 72 / 2
        Dim pageWidth = doc.PageSize.Width
        Dim pageHeight = doc.PageSize.Height
        Dim cW = pageWidth - margin * 2
        '' Text format for the chapter titles:
        Dim 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 = margin
        tlCaption.MarginTop = margin
        tlCaption.MarginRight = margin
        tlCaption.MarginBottom = margin
        tlCaption.TextAlignment = TextAlignment.Center
        '' Height of chapter caption (use a const for simplicity):
        Const captionH = 24.0F
        '' Text layout for main document body (default DsPdf resolution is 72dpi):
        Dim tl = New TextLayout(72)
        tl.DefaultFormat.Font = font
        tl.DefaultFormat.FontSize = fontSize
        tl.FirstLineIndent = 72 / 2
        tl.MaxWidth = pageWidth
        tl.MaxHeight = pageHeight
        tl.MarginLeft = margin
        tl.MarginRight = margin
        tl.MarginBottom = margin
        tl.MarginTop = margin + captionH
        tl.ColumnWidth = cW * 0.3F
        tl.TextAlignment = TextAlignment.Justified
        '' Array of PageSplitArea's which control additional columns (1st column is controlled by
        '' the 'main' TextLayout, for each additional one a PageSplitArea must be provided - 
        '' it will create and return a TextLayout that can then be used to render the column):
        Dim psas As PageSplitArea() = {
            New PageSplitArea(tl) With {.MarginLeft = tl.MarginLeft + (cW * 0.35F)},
            New PageSplitArea(tl) With {.ColumnWidth = -cW * 0.3F}
        }
        '' Split options to control splitting text between pages:
        Dim tso = New TextSplitOptions(tl) With {
            .RestMarginTop = margin,
            .MinLinesInFirstParagraph = 2,
            .MinLinesInLastParagraph = 2
        }
        '' Generate a number of "chapters", provide outline entry for each:
        Const NChapters = 20
        doc.Pages.Add()
        For i = 1 To NChapters
            '' Print chapter header across all columns:
            Dim chapter = $"Chapter {i}"
            tlCaption.Clear()
            tlCaption.Append(chapter)
            tlCaption.PerformLayout(True)
            doc.Pages.Last.Graphics.DrawTextLayout(tlCaption, PointF.Empty)
            '' Add outline node for the chapter:
            doc.Outlines.Add(New OutlineNode(chapter, New DestinationFitV(doc.Pages.Last, Nothing)))
            ''
            '' Clear last chapter's text and add new chapter:
            tl.FirstLineIsStartOfParagraph = True
            tl.LastLineIsEndOfParagraph = True
            tl.Clear()
            tl.Append(Util.LoremIpsum(5, 7, 9, 15, 25))
            tl.PerformLayout(True)
            '' Variable to hold last chapter end's bottom coord:
            Dim contentBottom = 0F
            '' Print the chapter:
            Dim tls = New TextLayoutSplitter(tl)
            While (True)
                Dim tlCol0 = tls.SplitAndBalance(psas, tso)
                Dim 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 Then
                    '' End of chapter, find out how much height left on page for next chapter:
                    contentBottom = tl.ContentY + tl.ContentHeight
                    contentBottom = Math.Max(contentBottom, psas(0).TextLayout.ContentRectangle.Bottom)
                    contentBottom = Math.Max(contentBottom, psas(1).TextLayout.ContentRectangle.Bottom)
                    '' Done printing chapter:
                    Exit While
                End If
                '' Continue printing chapter on new page:
                psas(0).MarginTop = margin
                psas(1).MarginTop = margin
                doc.Pages.Add()
            End While
            '' Next chapter - find out if we have enough space left on current page to start new chapter:
            If contentBottom + captionH < pageHeight * 0.8F Then
                '' Start new chapter on current page:
                contentBottom += pageHeight * 0.05F
                tlCaption.MarginTop = contentBottom
                tl.MarginTop = contentBottom + captionH
                psas(0).MarginTop = tl.MarginTop
                psas(1).MarginTop = tl.MarginTop
            ElseIf i < NChapters Then
                '' Start new chapter on new page:
                tlCaption.MarginTop = margin
                tl.MarginTop = margin + captionH
                psas(0).MarginTop = tl.MarginTop
                psas(1).MarginTop = tl.MarginTop
                doc.Pages.Add()
            End If
        Next
        ''
        '' Done:
        doc.Save(stream)
        Return doc.Pages.Count
    End Function
End Class