DrawSlantedText.cs
//
// 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 System.Collections.Generic;
using System.Linq;
using GrapeCity.Documents.Drawing;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Imaging;
using GCTEXT = GrapeCity.Documents.Text;
using GCDRAW = GrapeCity.Documents.Drawing;

namespace DsImagingWeb.Demos
{
    // This demo illustrates the results of using the GcGraphics.DrawSlantedText() method
    // with different combinations of arguments.
    // The DrawSlantedText() method draws text slanted within a specified rectangle
    // similar to how rotated text is drawn in MS Excel cells with borders.
    // See also the DrawRotatedText_0 demo.
    public class DrawSlantedText
    {
        static string[] s_names = new []
        {
            "Horizontal Stacking 1",
            "Horizontal Stacking 2",
            "Horizontal Stacking 3",
            "Horizontal Stacking 4",
            "Horizontal Stacking 5",
            "Horizontal Stacking 6",
            "Horizontal Stacking 7",
            "Horizontal Stacking 8",

            "Vertical Stacking 1",
            "Vertical Stacking 2",
            "Vertical Stacking 3",
            "Vertical Stacking 4",
        };

        public GcBitmap GenerateImage(Size pixelSize, float dpi, bool opaque, string[] sampleParams = null)
        {
            sampleParams ??= GetSampleParamsList()[0];

            var bmp = new GcBitmap(pixelSize.Width, pixelSize.Height, opaque, dpi, dpi);
            using var g = bmp.CreateGraphics(Color.White);
            // Set up some values to manage layout:
            var margin = g.Resolution;
            var gap = g.Resolution;
            var w = pixelSize.Width * 0.3f;
            var h = w;

            if (sampleParams[0] == s_names[0])
            {
                // Various text alignments with SlantedTextAlignment.BelowRotatedInside:
                Draw(g, q0(), angle: -70, false, SlantedTextAlignment.BelowRotatedInside, TextAlignment.Leading);
                Draw(g, q1(), angle: -70, false, SlantedTextAlignment.BelowRotatedInside, TextAlignment.Trailing);
                Draw(g, q2(), angle: -70, false, SlantedTextAlignment.BelowRotatedInside, TextAlignment.Center);
                Draw(g, q3(), angle: -70, false, SlantedTextAlignment.BelowRotatedInside, TextAlignment.Distributed);
            }
            else if (sampleParams[0] == s_names[1])
            {
                // Various text alignments with SlantedTextAlignment.BelowRotatedOutside:
                Draw(g, q0(), angle: -70, false, SlantedTextAlignment.BelowRotatedOutside, TextAlignment.Leading);
                Draw(g, q1(), angle: -70, false, SlantedTextAlignment.BelowRotatedOutside, TextAlignment.Trailing);
                Draw(g, q2(), angle: -70, false, SlantedTextAlignment.BelowRotatedOutside, TextAlignment.Center);
                Draw(g, q3(), angle: -70, false, SlantedTextAlignment.BelowRotatedOutside, TextAlignment.Distributed);
            }
            else if (sampleParams[0] == s_names[2])
            {
                // Various text alignments with SlantedTextAlignment.AboveRotatedInside:
                Draw(g, q0(), angle: -70, false, SlantedTextAlignment.AboveRotatedInside, TextAlignment.Leading);
                Draw(g, q1(), angle: -70, false, SlantedTextAlignment.AboveRotatedInside, TextAlignment.Trailing);
                Draw(g, q2(), angle: -70, false, SlantedTextAlignment.AboveRotatedInside, TextAlignment.Center);
                Draw(g, q3(), angle: -70, false, SlantedTextAlignment.AboveRotatedInside, TextAlignment.Distributed);
            }
            else if (sampleParams[0] == s_names[3])
            {
                // Various text alignments with SlantedTextAlignment.AboveRotatedOutside:
                Draw(g, q0(), angle: -70, false, SlantedTextAlignment.AboveRotatedOutside, TextAlignment.Leading);
                Draw(g, q1(), angle: -70, false, SlantedTextAlignment.AboveRotatedOutside, TextAlignment.Trailing);
                Draw(g, q2(), angle: -70, false, SlantedTextAlignment.AboveRotatedOutside, TextAlignment.Center);
                Draw(g, q3(), angle: -70, false, SlantedTextAlignment.AboveRotatedOutside, TextAlignment.Distributed);
            }
            else if (sampleParams[0] == s_names[4])
            {
                // Various text alignments with SlantedTextAlignment.CenterInsideOutside:
                Draw(g, q0(), angle: -70, false, SlantedTextAlignment.CenterInsideOutside, TextAlignment.Leading);
                Draw(g, q1(), angle: -70, false, SlantedTextAlignment.CenterInsideOutside, TextAlignment.Trailing);
                Draw(g, q2(), angle: -70, false, SlantedTextAlignment.CenterInsideOutside, TextAlignment.Center);
                Draw(g, q3(), angle: -70, false, SlantedTextAlignment.CenterInsideOutside, TextAlignment.Distributed);
            }
            else if (sampleParams[0] == s_names[5])
            {
                // Various text alignments with SlantedTextAlignment.CenterOutsideInside:
                Draw(g, q0(), angle: -70, false, SlantedTextAlignment.CenterOutsideInside, TextAlignment.Leading);
                Draw(g, q1(), angle: -70, false, SlantedTextAlignment.CenterOutsideInside, TextAlignment.Trailing);
                Draw(g, q2(), angle: -70, false, SlantedTextAlignment.CenterOutsideInside, TextAlignment.Center);
                Draw(g, q3(), angle: -70, false, SlantedTextAlignment.CenterOutsideInside, TextAlignment.Distributed);
            }
            else if (sampleParams[0] == s_names[6])
            {
                // Examples with positive rotation angle using SlantedTextAlignment.BelowRotatedInside:
                Draw(g, q0(), angle: 70, false, SlantedTextAlignment.BelowRotatedInside, TextAlignment.Leading);
                Draw(g, q1(), angle: 70, false, SlantedTextAlignment.BelowRotatedInside, TextAlignment.Trailing);
                Draw(g, q2(), angle: 70, false, SlantedTextAlignment.BelowRotatedInside, TextAlignment.Center);
                Draw(g, q3(), angle: 70, false, SlantedTextAlignment.BelowRotatedInside, TextAlignment.Distributed);
            }
            else if (sampleParams[0] == s_names[7])
            {
                // Examples with positive rotation angle using SlantedTextAlignment.AboveRotatedInside:
                Draw(g, q0(), angle: 70, false, SlantedTextAlignment.AboveRotatedInside, TextAlignment.Leading);
                Draw(g, q1(), angle: 70, false, SlantedTextAlignment.AboveRotatedInside, TextAlignment.Trailing);
                Draw(g, q2(), angle: 70, false, SlantedTextAlignment.AboveRotatedInside, TextAlignment.Center);
                Draw(g, q3(), angle: 70, false, SlantedTextAlignment.AboveRotatedInside, TextAlignment.Distributed);
            }
            // Vertical stacking:
            else if (sampleParams[0] == s_names[8])
            {
                // Vertically stacked text rotated to a negative angle using SlantedTextAlignment.BelowRotatedInside:
                Draw(g, q0(), angle: -20, true, SlantedTextAlignment.BelowRotatedInside, TextAlignment.Leading);
                Draw(g, q1(), angle: -20, true, SlantedTextAlignment.BelowRotatedInside, TextAlignment.Trailing);
                Draw(g, q2(), angle: -20, true, SlantedTextAlignment.BelowRotatedInside, TextAlignment.Center);
                Draw(g, q3(), angle: -20, true, SlantedTextAlignment.BelowRotatedInside, TextAlignment.Distributed);
            }
            else if (sampleParams[0] == s_names[9])
            {
                // Vertically stacked text rotated to a negative angle using SlantedTextAlignment.BelowRotatedOutside:
                Draw(g, q0(), angle: -20, true, SlantedTextAlignment.BelowRotatedOutside, TextAlignment.Leading);
                Draw(g, q1(), angle: -20, true, SlantedTextAlignment.BelowRotatedOutside, TextAlignment.Trailing);
                Draw(g, q2(), angle: -20, true, SlantedTextAlignment.BelowRotatedOutside, TextAlignment.Center);
                Draw(g, q3(), angle: -20, true, SlantedTextAlignment.BelowRotatedOutside, TextAlignment.Distributed);
            }
            else if (sampleParams[0] == s_names[10])
            {
                // Vertically stacked text rotated to a positive angle using SlantedTextAlignment.AboveRotatedOutside:
                Draw(g, q0(), angle: 20, true, SlantedTextAlignment.AboveRotatedOutside, TextAlignment.Leading);
                Draw(g, q1(), angle: 20, true, SlantedTextAlignment.AboveRotatedOutside, TextAlignment.Trailing);
                Draw(g, q2(), angle: 20, true, SlantedTextAlignment.AboveRotatedOutside, TextAlignment.Center);
                Draw(g, q3(), angle: 20, true, SlantedTextAlignment.AboveRotatedOutside, TextAlignment.Distributed);
            }
            else if (sampleParams[0] == s_names[11])
            {
                // Vertically stacked text rotated to a positive angle using SlantedTextAlignment.CenterOutsideInside:
                Draw(g, q0(), angle: 20, true, SlantedTextAlignment.CenterOutsideInside, TextAlignment.Leading);
                Draw(g, q1(), angle: 20, true, SlantedTextAlignment.CenterOutsideInside, TextAlignment.Trailing);
                Draw(g, q2(), angle: 20, true, SlantedTextAlignment.CenterOutsideInside, TextAlignment.Center);
                Draw(g, q3(), angle: 20, true, SlantedTextAlignment.CenterOutsideInside, TextAlignment.Distributed);
            }
            return bmp;

            RectangleF q0()
            {
                return new RectangleF(margin, margin, w, h);
            }
            RectangleF q1()
            {
                return new RectangleF(pixelSize.Width - margin - w, margin, w, h);
            }
            RectangleF q2()
            {
                return new RectangleF(margin, margin + h + gap, w, h);
            }
            RectangleF q3()
            {
                return new RectangleF(pixelSize.Width - margin - w, margin + h + gap, w, h);
            }
        }

        static void Draw(GcGraphics g, RectangleF rect, int angle, bool verticalStacking,
            SlantedTextAlignment slantedAlign, TextAlignment textAlign)
        {
            // Draw a legend stating the current DrawRotatedText arguments' values:
            var tlLegend = g.CreateTextLayout();
            tlLegend.DefaultFormat.FontName = "Calibri";
            tlLegend.DefaultFormat.FontSize = 9;
            tlLegend.AppendLine($"Rotation angle: {angle}°");
            tlLegend.AppendLine($"Text alignment: {textAlign}");
            tlLegend.AppendLine($"Slanted text alignment: {slantedAlign}");
            tlLegend.AppendLine($"Is vertical stacking: {verticalStacking}");
            g.DrawTextLayout(tlLegend, rect.Location);

            // The target rectangle for the DrawRotatedText call:
            var d = tlLegend.ContentHeight + g.Resolution;
            rect.Y += d;
            rect.Height -= d;
            rect.Width -= d / 2;
            var x = rect.X;
            var y = rect.Y;
            var w = rect.Width;
            var h = rect.Height;

            // Draw the target rectangle:
            g.DrawRectangle(rect, new GCDRAW::Pen(Color.PaleGreen, 3));

            if (!verticalStacking)
            {
                float dx = (float)(h / Math.Tan(Math.PI * angle / -180.0));
                switch (slantedAlign)
                {
                    case SlantedTextAlignment.BelowRotatedInside:
                    case SlantedTextAlignment.AboveRotatedOutside:
                    case SlantedTextAlignment.CenterInsideOutside:
                        g.DrawPolygon(new[] {
                            new PointF(x + dx, y), new PointF(x + dx + w, y),
                            new PointF(x + w, y + h), new PointF(x, y + h)},
                            new GCDRAW::Pen(Color.Red, 1));
                        break;
                    case SlantedTextAlignment.BelowRotatedOutside:
                    case SlantedTextAlignment.AboveRotatedInside:
                    case SlantedTextAlignment.CenterOutsideInside:
                        g.DrawPolygon(new[] {
                            new PointF(x, y), new PointF(x + w, y),
                            new PointF(x - dx + w, y + h), new PointF(x - dx, y + h)},
                            new GCDRAW::Pen(Color.Red, 1));
                        break;
                }
            }
            else
            {
                float dy = (float)(w * Math.Tan(Math.PI * angle / 180.0));
                switch (slantedAlign)
                {
                    case SlantedTextAlignment.BelowRotatedInside:
                    case SlantedTextAlignment.AboveRotatedOutside:
                    case SlantedTextAlignment.CenterInsideOutside:
                        if (angle >= 0)
                            g.DrawPolygon(new[] {
                                new PointF(x, y), new PointF(x + w, y + dy), new PointF(x + w, y + dy + h), new PointF(x, y + h)},
                                new GCDRAW::Pen(Color.Red, 1));
                        else
                            g.DrawPolygon(new[] {
                                new PointF(x, y - dy), new PointF(x + w, y), new PointF(x + w, y + h), new PointF(x, y - dy + h)},
                                new GCDRAW::Pen(Color.Red, 1));
                        break;
                    case SlantedTextAlignment.BelowRotatedOutside:
                    case SlantedTextAlignment.AboveRotatedInside:
                    case SlantedTextAlignment.CenterOutsideInside:
                        if (angle >= 0)
                            g.DrawPolygon(new[] {
                                new PointF(x, y - dy), new PointF(x + w, y), new PointF(x + w, y + h), new PointF(x, y - dy + h)},
                                new GCDRAW::Pen(Color.Red, 1));
                        else
                            g.DrawPolygon(new[] {
                                new PointF(x, y), new PointF(x + w, y + dy), new PointF(x + w, y + dy + h), new PointF(x, y + h)},
                                new GCDRAW::Pen(Color.Red, 1));
                        break;
                }
            }
            // Draw slanted text:
            var tl = g.CreateTextLayout();
            tl.DefaultFormat.FontName = "Calibri";
            tl.DefaultFormat.FontSize = 12;
            tl.TextAlignment = textAlign;
            tl.Append("The quick brown fox jumps over the lazy dog. ");
            tl.Append("The quick brown fox jumps over the lazy dog.");
            g.DrawSlantedText(tl, angle, verticalStacking, rect, slantedAlign);
        }

        public static List<string[]> GetSampleParamsList()
        {
            return new List<string[]>()
            {
                // Strings are name, description, info. Rest are arbitrary strings:
                new string[] { s_names[0], "Various text alignments with SlantedTextAlignment.BelowRotatedInside", null},
                new string[] { s_names[1], "Various text alignments with SlantedTextAlignment.BelowRotatedOutside", null},
                new string[] { s_names[2], "Various text alignments with SlantedTextAlignment.AboveRotatedInside", null},
                new string[] { s_names[3], "Various text alignments with SlantedTextAlignment.AboveRotatedOutside", null},
                new string[] { s_names[4], "Various text alignments with SlantedTextAlignment.CenterInsideOutside", null},
                new string[] { s_names[5], "Various text alignments with SlantedTextAlignment.CenterOutsideInside", null},
                new string[] { s_names[6], "Examples with positive rotation angle using SlantedTextAlignment.BelowRotatedInside", null},
                new string[] { s_names[7], "Examples with positive rotation angle using SlantedTextAlignment.AboveRotatedInside", null},
                new string[] { s_names[8], "Vertically stacked text rotated to a negative angle using SlantedTextAlignment.BelowRotatedInside", null},
                new string[] { s_names[9], "Vertically stacked text rotated to a negative angle using SlantedTextAlignment.BelowRotatedOutside", null},
                new string[] { s_names[10], "Vertically stacked text rotated to a positive angle using SlantedTextAlignment.AboveRotatedOutside", null},
                new string[] { s_names[11], "Vertically stacked text rotated to a positive angle using SlantedTextAlignment.CenterOutsideInside", null},
            };
        }
    }
}