TextRendering.vb
''
'' This code is part of GrapeCity Documents for Imaging samples.
'' Copyright (c) GrapeCity, Inc. All rights reserved.
''
Imports System.IO
Imports System.Drawing
Imports System.Numerics
Imports GrapeCity.Documents.Drawing
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Imaging

'' Demonstrates the basics of rendering text in GcImaging.
'' 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.
Public Class TextRendering
    Function GenerateImage(
            Optional pixelWidth As Integer = 1024,
            Optional pixelHeight As Integer = 1024,
            Optional opaque As Boolean = True,
            Optional dpiX As Single = 96,
            Optional dpiY As Single = 96) As GcBitmap
        Dim bmp = New GcBitmap(pixelWidth, pixelHeight, True, dpiX, dpiY)
        Dim Inch = dpiX
        Const fontSize = 14
        Using g = bmp.CreateGraphics(Color.White)
            '' TextFormat class Is used throughout all GcImaging text rendering to specify
            '' font And other character formatting:
            Dim tf = New TextFormat() With
                {
                    .Font = Font.FromFile(Path.Combine("Resources", "Fonts", "times.ttf")),
                    .FontSize = fontSize
                }

            '' 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." + vbCrLf +
                    "(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:
            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
            Const 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 = Font.FromFile(Path.Combine("Resources", "Fonts", "timesbi.ttf")),
                    .FontSize = fontSize,
                    .FontBold = True,
                    .FontItalic = True,
                    .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 = Font.FromFile(Path.Combine("Resources", "Fonts", "timesi.ttf"))
            tl.DefaultFormat.FontSize = fontSize

            '' 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 = g.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))
            '' Draw border around the whole image:
            g.DrawRectangle(New RectangleF(0, 0, bmp.Width, bmp.Height), Color.DarkSlateBlue, 4)
        End Using
        '' Done
        Return bmp
    End Function
End Class