TextRendering.cs
//
// This code is part of Document Solutions for PDF demos.
// Copyright (c) MESCIUS inc. All rights reserved.
//
using System;
using System.IO;
using System.Drawing;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Drawing;

namespace DsPdfWeb.Demos.Basics
{
    // 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
    {
        public int CreatePDF(Stream stream)
        {
            var doc = new GcPdfDocument();
            var page = doc.NewPage();
            var g = page.Graphics;
            // By default, DsPdf uses 72dpi:
            const float In = 72;

            // TextFormat class is used throughout all DsPdf text rendering to specify
            // font and other character formatting:
            var tf = new TextFormat() { 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(In, In));

            // 2.
            // Another overload taking a rectangle instead, plus alignment and wrapping
            // options, is also available and provides a bit more flexibility.
            // The parameters are:
            // - the text string;
            // - the text format;
            // - the layout rectangle;
            // - (optional) text alignment (the default is leading, left for LTR languages);
            // - (optional) paragraph alignment (the default is near, top for top-to-bottom flow);
            // - (optional) word wrapping (the default is true):
            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(In, In * 2, In * 4, In),
                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:
            string tstr3 = "3. Test string to demo MeasureString() used with DrawString().";
            // Available layout size:
            SizeF layoutSize = new SizeF(In * 3, In * 0.8f);
            SizeF s = g.MeasureString(tstr3, tf, layoutSize, out int fitCharCount);
            // Show the passed in size in red, the measured size in blue,
            // and draw the string within the returned size as bounds:
            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.
            // 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).
            var 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);
            // Add a line break, effectively starting a new paragraph:
            tl.AppendLine();
            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) { 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 - In * 2;
            // Paragraph formatting can also be set, here we set first line offset,
            // spacing between paragraphs and line spacing:
            tl.FirstLineIndent = In * 0.5f;
            tl.ParagraphSpacing = In * 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(In, In * 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(In, In * 5));

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