//
// This code is part of Document Solutions for Imaging demos.
// Copyright (c) MESCIUS inc. All rights reserved.
//
using System;
using System.IO;
using System.Drawing;
using GrapeCity.Documents.Imaging;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Drawing;
using GrapeCity.Documents.Imaging.Skia;
using GCTEXT = GrapeCity.Documents.Text;
using GCDRAW = GrapeCity.Documents.Drawing;
namespace DsImagingWeb.Demos
{
// This example is essentially the same as in TextRendering,
// but uses DsImaging.Skia library (GcSkiaBitmap / GcSkiaGraphics)
// to create and render the resulting image.
public class SkiaTextRendering
{
public static bool IsSkiaOnly => true;
public Stream GenerateImageStream(string targetMime, Size pixelSize, float dpi, bool opaque, string[] sampleParams = null)
{
switch (targetMime)
{
case Common.Util.MimeTypes.JPEG:
case Common.Util.MimeTypes.PNG:
case Common.Util.MimeTypes.WEBP:
break;
default:
throw new Exception("This sample uses Skia to create the image, and only supports JPEG, PNG and WEBP output formats.");
}
using var bmp = new GcSkiaBitmap(pixelSize.Width, pixelSize.Height, opaque);
var Inch = dpi;
const float fontSize = 14;
using (var g = bmp.CreateGraphics(Color.White))
{
// TextFormat class is used throughout all DsImaging text rendering to specify
// font and other character formatting:
var tf = new TextFormat()
{
Font = GCTEXT.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 just the point
// at which to draw the string:
g.DrawString(
"1. Test string. Please read the extensive comments in this sample's code.\r\n" +
"(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), // the layout rectangle
// The rest 3 args are optional, passing defaults here for illustration:
TextAlignment.Leading, // leading (left for LTR languages) text align
ParagraphAlignment.Near, // near (top for top-to-bottom flow) para align
true); // word wrap
// 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 string tstr3 = "3. Test string to demo MeasureString() used with DrawString().";
SizeF layoutSize = new SizeF(Inch * 3, Inch * 0.8f); // available size
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(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}).
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);
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)
{
Font = GCTEXT.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 = GCTEXT.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.PixelWidth, bmp.PixelHeight), Color.DarkSlateBlue, 4);
}
// Done:
var ms = new MemoryStream();
switch (targetMime)
{
case Common.Util.MimeTypes.JPEG:
bmp.SaveAsJpeg(ms);
break;
case Common.Util.MimeTypes.PNG:
bmp.SaveAsPng(ms);
break;
case Common.Util.MimeTypes.WEBP:
bmp.SaveAsWebp(ms);
break;
default:
System.Diagnostics.Debug.Assert(false);
break;
}
return ms;
}
}
}