TextRendering.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
Imports GrapeCity.Documents.Drawing

'' This sample demonstrates the basics of rendering text in DsPdf.
'' The two main approaches are:
'' - using the MeasureString/DrawString pair, or
'' - using the TextLayout directly.
'' While the first approach may be easier in simple cases,
'' the second approach (using TextLayout) is much more powerful
'' and generally speaking yields better performance.
'' Please read the comments in code below for more details.
'' See also CharacterFormatting, PaginatedText, ParagraphAlign,
'' ParagraphFormatting, TextAlign.
Public Class TextRendering
    Function CreatePDF(ByVal stream As Stream) As Integer
        Dim doc = New GcPdfDocument()
        Dim page = doc.NewPage()
        Dim g = page.Graphics
        '' By default, DsPdf uses 72dpi:
        Const Inch = 72.0F

        '' TextFormat class is used throughout all DsPdf text rendering to specify
        '' font and other character formatting:
        Dim tf = New TextFormat() With {.Font = StandardFonts.Times, .FontSize = 12}

        '' 1.
        '' The easiest way to render a short string on a page at an arbitrary location,
        '' when you are 100% sure that the string will fit in the available space,
        '' is to use the GcGraphics.DrawString() overload accepting jus the point
        '' at which to draw the string:
        g.DrawString("1. Test string. Please read the extensive comments in this sample's code." +
            "(Note that line breaks are allowed even in the simplest DrawString overload.)", tf, New PointF(Inch, Inch))

        '' 2.
        '' Another overload taking a rectangle instead, plus alignment and wrapping
        '' options, is also available and provides a bit more flexibility.
        '' The parameters are:
        ''  - text
        ''  - format
        ''  - the layout rectangle
        ''  The rest 3 args are optional, passing defaults here for illustration:
        ''  - leading (left for LTR languages) text align
        ''  - near (top for top-to-bottom flow) para align
        ''  - word wrap
        g.DrawString("2. A longer test string which will probably need more than the allocated" +
            "4 inches so quite possibly will wrap to show that DrawString can do that.",
            tf,
            New RectangleF(Inch, Inch * 2, Inch * 4, Inch),
            TextAlignment.Leading,
            ParagraphAlignment.Near,
            True)

        '' 3.
        '' Complementary to DrawString, a MeasureString() method is available
        '' (with several different overloads), and can be used in pair with
        '' DrawString when more control over text layout is needed:
        Dim tstr3 = "3. Test string to demo MeasureString() used with DrawString()."

        Dim layoutSize = New SizeF(Inch * 3, Inch * 0.8F) '' available size
        Dim fitCharCount As Integer
        Dim s = g.MeasureString(tstr3, tf, layoutSize, fitCharCount)
        '' Show the passed in size in red, the measured size in blue,
        '' and draw the string within the returned size as bounds:
        Dim pt = New PointF(Inch, Inch * 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.
        '' A much more powerful and with better performance, way to render text
        '' is to use TextLayout. (TextLayout is used anyway by DrawString/MeasureString,
        '' so when you use TextLayout directly, you basically cut the work in half.)
        '' A TextLayout instance represents one or more paragraphs of text, with 
        '' the same paragraph formatting (character formats may be different,
        '' see {MultiFormattedText}).
        Dim tl = g.CreateTextLayout()
        '' To add text, use Append() or AppendLine() methods:
        tl.Append("4. First test string added to TextLayout. ", tf)
        tl.Append("Second test string added to TextLayout, continuing the same paragraph. ", tf)
        tl.AppendLine() '' Add a line break, effectively starting a new paragraph
        tl.Append("Third test string added to TextLayout, a new paragraph. ", tf)
        tl.Append("Fourth test string, with a different char formatting. ",
            New TextFormat(tf) With {.Font = StandardFonts.TimesBoldItalic, .ForeColor = Color.DarkSeaGreen})
        '' Text can be added to TextLayout without explicit TextFormat:
        tl.Append("Fifth test string, using the TextLayout's default format.")
        '' ...but in that case at least the Font must be specified on the
        '' TextLayout's DefaultFormat, otherwise PerformLayout (below) will fail:
        tl.DefaultFormat.Font = StandardFonts.TimesItalic

        '' Specify the layout, such as max available size etc.
        '' Here we only provide the max width, but many more parameters can be set:
        tl.MaxWidth = page.Size.Width - Inch * 2
        '' Paragraph formatting can also be set, here we set first line offset,
        '' spacing between paragraphs and line spacing:
        tl.FirstLineIndent = Inch * 0.5F
        tl.ParagraphSpacing = Inch * 0.05F
        tl.LineSpacingScaleFactor = 0.8F

        '' When all text has been added, and layout options specified,
        '' the TextLayout needs to calculate the glyphs needed to render
        '' the text, and perform the layout. This can be done with a 
        '' single call:
        tl.PerformLayout(True)

        '' Now we can draw it on the page:
        pt = New PointF(Inch, Inch * 4)
        g.DrawTextLayout(tl, pt)
        '' TextLayout provides info about the text including the measured bounds
        '' and much more. Here we draw the bounding box in orange red:
        g.DrawRectangle(New RectangleF(pt, tl.ContentRectangle.Size), Color.OrangeRed)

        '' 5.
        '' TextLayout can be re-used to draw different paragraph(s), this can be useful
        '' when you need to render a different text with the same paragraph formatting.
        '' The Clear() call removes the text but preserves paragraph formatting:
        tl.Clear()
        tl.Append("5. This is text rendered re-using the same TextLayout. ")
        tl.Append("More text added to TextLayout being re-used, continuing the same paragraph. ", tf)
        tl.Append("And finally, some more text added.", tf)
        '' The necessary call to calculate the glyphs and perform layout:
        tl.PerformLayout(True)
        '' Render the text:
        g.DrawTextLayout(tl, New PointF(Inch, Inch * 5))
        ''
        '' Done:
        doc.Save(stream)
        Return doc.Pages.Count
    End Function
End Class