LayoutDemos.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 System.Numerics;
using GrapeCity.Documents.Drawing;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Imaging;
using GrapeCity.Documents.Layout;
using GrapeCity.Documents.Layout.Composition;
using GCTEXT = GrapeCity.Documents.Text;
using GCDRAW = GrapeCity.Documents.Drawing;
namespace DsImagingWeb.Demos
{
// This integrated demo shows how to use helper classes
// in the GrapeCity.Documents.Layout.Composition namespace
// to create complex and flexible constraint-based layouts
// with custom z-order and clipping.
public class LayoutDemos
{
static readonly Color
PageColor = Color.FromArgb(39, 41, 43),
BoxColor = Color.FromArgb(230, 230, 230),
CodeColor = Color.FromArgb(132, 211, 232),
RectColor = Color.FromArgb(64, 126, 148),
DescColor = Color.White;
delegate void DrawLayoutSample(GcGraphics g, Size pageSize);
static readonly Dictionary<string, DrawLayoutSample> c_samples = new Dictionary<string, DrawLayoutSample>()
{
{ "DrawSample1", DrawSample1 },
{ "DrawSample2", DrawSample2 },
{ "DrawSample3", DrawSample3 },
{ "DrawSample4", DrawSample4 },
{ "DrawSample5", DrawSample5 },
{ "DrawSample6", DrawSample6 },
{ "DrawSample7", DrawSample7 },
{ "DrawSample8", DrawSample8 },
};
public GcBitmap GenerateImage(Size pixelSize, float dpi, bool opaque, string[] sampleParams = null)
{
if (!c_samples.TryGetValue(sampleParams[3], out DrawLayoutSample drawSample))
throw new Exception($"Unknown parameterized sample: {sampleParams[3]}");
var bmp = new GcBitmap(pixelSize.Width, pixelSize.Height, opaque, dpi, dpi);
using var g = bmp.CreateGraphics(PageColor);
drawSample(g, pixelSize);
return bmp;
}
public static List<string[]> GetSampleParamsList()
{
return new List<string[]>()
{
// Strings are name, description, info. Rest are arbitrary strings:
new string[] { "Figures 1-4", "Figures 1-4: horizontal, vertical, alignment constraint and guidelines",
null,
"DrawSample1" },
new string[] { "Figure 5", "Figure 5: Barrier constraint anchored to two visuals",
null,
"DrawSample2" },
new string[] { "Figure 6", "Figure 6: Horizontal chain with weighted widths",
null,
"DrawSample3" },
new string[] { "Figure 7", "Figure 7: Horizontal chain with evenly distributed rectangles and margins",
null,
"DrawSample4" },
new string[] { "Figure 8", "Figure 8: Horizontal chain with evenly distributed rectangles, no margins",
null,
"DrawSample5" },
new string[] { "Figure 9", "Figure 9: Weighted horizontal chain without margins",
null,
"DrawSample6" },
new string[] { "Figure 10", "Figure 10: Horizontal chain with weighted margins and packed rectangles",
null,
"DrawSample7" },
new string[] { "Text Flow", "Text flow in and around non-rectangular contours",
null,
"DrawSample8" },
};
}
// Figures 1-4: horizontal, vertical, alignment constraint and guidelines.
static void DrawSample1(GcGraphics g, Size _)
{
g.Transform = Matrix3x2.CreateScale(1.4f);
// The Surface object can layout Visuals and draw them on the graphics.
var surf = new Surface();
// view1
// The View object represents a group of Visuals with a transformation matrix.
var view1 = surf.CreateView(250, 150).Translate(50, 40);
// Visual is an element with the associated LayoutRect used for its
// positioning/ and a delegate that draws its content on the graphics.
var v = view1.CreateVisual(DrawView);
v.Tag = new FigureCaption(1, "A horizontal constraint to the parent:",
"rA.SetLeft(null, AnchorParam.Left, 90);");
v.LayoutRect.AnchorExact(null);
v = view1.CreateVisual(DrawRect);
var rA = v.LayoutRect;
rA.SetLeft(null, AnchorParam.Left, 90);
rA.SetWidth(70);
rA.SetHeight(40);
rA.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "A";
var r = view1.CreateVisual(DrawLineWithLeftArrow).LayoutRect;
r.AnchorTopBottom(rA, 0, 0);
r.SetLeft(null, AnchorParam.Left);
r.SetRight(rA, AnchorParam.Left);
// view2
var view2 = surf.CreateView(250, 150).Translate(350, 40);
v = view2.CreateVisual(DrawView);
v.Tag = new FigureCaption(2, "An offset horizontal alignment constraint:",
"rB.SetLeft(rA, AnchorParam.Left, 40);");
v.LayoutRect.AnchorExact(null);
v = view2.CreateVisual(DrawRect);
rA = v.LayoutRect;
rA.AnchorTopLeft(null, 15, 70, 110, 60);
v.Tag = "A";
v = view2.CreateVisual(DrawRect);
var rB = v.LayoutRect;
rB.SetLeft(rA, AnchorParam.Left, 40);
rB.SetWidth(60);
rB.SetHeight(35);
rB.SetTop(rA, AnchorParam.Bottom, 20);
v.Tag = "B";
r = view2.CreateVisual(DrawVertLineWithLeftArrow).LayoutRect;
r.SetTop(rA, AnchorParam.VerticalCenter);
r.SetBottom(rB, AnchorParam.VerticalCenter);
r.SetLeft(rA, AnchorParam.Left);
r.SetRight(rB, AnchorParam.Left);
// view3
var view3 = surf.CreateView(250, 150).Translate(50, 260);
v = view3.CreateVisual(DrawView);
v.Tag = new FigureCaption(3, "Horizontal and vertical constraints:",
"rB.SetLeft(rA, AnchorParam.Right, 50);\n" +
"rC.SetTop(rA, AnchorParam.Bottom, 40);");
v.LayoutRect.AnchorExact(null);
v = view3.CreateVisual(DrawRect);
rA = v.LayoutRect;
rA.AnchorTopLeft(null, 15, 30, 70, 40);
v.Tag = "A";
v = view3.CreateVisual(DrawRect);
rB = v.LayoutRect;
rB.SetLeft(rA, AnchorParam.Right, 50);
rB.SetWidth(70);
rB.SetHeight(40);
rB.SetTop(rA, AnchorParam.Top);
v.Tag = "B";
v = view3.CreateVisual(DrawRect);
var rC = v.LayoutRect;
rC.SetTop(rA, AnchorParam.Bottom, 40);
rC.SetWidth(70);
rC.SetHeight(40);
rC.SetLeft(rA, AnchorParam.Left);
v.Tag = "C";
r = view3.CreateVisual(DrawLineWithLeftArrow).LayoutRect;
r.AnchorTopBottom(rA, 0, 0);
r.SetLeft(rA, AnchorParam.Right);
r.SetRight(rB, AnchorParam.Left);
r = view3.CreateVisual(DrawLineWithUpArrow).LayoutRect;
r.AnchorLeftRight(rA, 0, 0);
r.SetTop(rA, AnchorParam.Bottom);
r.SetBottom(rC, AnchorParam.Top);
// view4
var view4 = surf.CreateView(250, 150).Translate(350, 260);
var layoutView = view4.LayoutView;
v = view4.CreateVisual(DrawView);
v.Tag = new FigureCaption(4, "A rectangle constrained to a guideline:",
"var anchorPoint = layoutView.CreatePoint(0.25f, 0);\n" +
"rA.SetLeft(anchorPoint, 60);");
v.LayoutRect.AnchorExact(null);
// An anchor point can work as the guideline on the X or the Y axes.
var anchorPoint = layoutView.CreatePoint(0.25f, 0);
v = view4.CreateVisual(DrawVertGuideline);
var rG = v.LayoutRect;
rG.SetLeft(anchorPoint);
rG.AnchorVerticalLine(null);
v.Tag = "25 %";
v = view4.CreateVisual(DrawRect);
rA = v.LayoutRect;
rA.SetLeft(anchorPoint, 60);
rA.SetWidth(70);
rA.SetHeight(40);
rA.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "A";
r = view4.CreateVisual(DrawLineWithLeftArrow).LayoutRect;
r.AnchorTopBottom(rA, 0, 0);
r.SetLeft(rG, AnchorParam.Left);
r.SetRight(rA, AnchorParam.Left);
surf.Render(g);
}
// Figure 5: Barrier constraint anchored to two visuals.
static void DrawSample2(GcGraphics g, Size _)
{
g.Transform = Matrix3x2.CreateScale(2);
var surf = new Surface();
// View1
var view1 = surf.CreateView(350, 160).Translate(30, 30);
view1.CreateVisual(DrawView).LayoutRect.AnchorExact(null);
var v = view1.CreateVisual(DrawRect);
var rA = v.LayoutRect;
rA.AnchorTopLeft(null, 30, 50, 60, 40);
v.Tag = "A";
v = view1.CreateVisual(DrawRect);
var rB = v.LayoutRect;
rB.AnchorTopLeft(null, 90, 50, 90, 40);
v.Tag = "B";
v = view1.CreateVisual(DrawVertGuideline);
var rG = v.LayoutRect;
// Adding multiple MinLeft constraints to the same rectangle creates
// a barrier to be used as the base for other rectangles.
rG.AppendMinLeft(rA, AnchorParam.Right);
rG.AppendMinLeft(rB, AnchorParam.Right);
rG.AnchorVerticalLine(null);
v = view1.CreateVisual(DrawRect);
var rC = v.LayoutRect;
rC.AppendMinLeft(rA, AnchorParam.Right, 50);
rC.AppendMinLeft(rB, AnchorParam.Right, 50);
rC.SetWidth(70);
rC.SetHeight(40);
rC.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "C";
var r = view1.CreateVisual(DrawLineWithLeftArrow).LayoutRect;
r.AnchorTopBottom(rC, 0, 0);
r.SetLeft(rG, AnchorParam.Right);
r.SetRight(rC, AnchorParam.Left);
// View2
var view2 = surf.CreateView(350, 160).Translate(30, 210);
v = view2.CreateVisual(DrawView);
v.Tag = new FigureCaption(5, "C is constrained to a barrier, which moves based on\n" +
"the position and size of both A and B:",
"rC.AppendMinLeft(rA, AnchorParam.Right, 50);\n" +
"rC.AppendMinLeft(rB, AnchorParam.Right, 50);");
v.LayoutRect.AnchorExact(null);
v = view2.CreateVisual(DrawRect);
rA = v.LayoutRect;
rA.AnchorTopLeft(null, 30, 50, 130, 40);
v.Tag = "A";
v = view2.CreateVisual(DrawRect);
rB = v.LayoutRect;
rB.AnchorTopLeft(null, 90, 50, 90, 40);
v.Tag = "B";
v = view2.CreateVisual(DrawVertGuideline);
rG = v.LayoutRect;
rG.AppendMinLeft(rA, AnchorParam.Right);
rG.AppendMinLeft(rB, AnchorParam.Right);
rG.AnchorVerticalLine(null);
v = view2.CreateVisual(DrawRect);
rC = v.LayoutRect;
rC.AppendMinLeft(rA, AnchorParam.Right, 50);
rC.AppendMinLeft(rB, AnchorParam.Right, 50);
rC.SetWidth(70);
rC.SetHeight(40);
rC.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "C";
r = view2.CreateVisual(DrawLineWithLeftArrow).LayoutRect;
r.AnchorTopBottom(rC, 0, 0);
r.SetLeft(rG, AnchorParam.Right);
r.SetRight(rC, AnchorParam.Left);
surf.Render(g);
}
// Figure 6: Horizontal chain with weighted widths.
static void DrawSample3(GcGraphics g, Size _)
{
g.Transform = Matrix3x2.CreateScale(2);
var surf = new Surface();
var view = surf.CreateView(350, 120).Translate(30, 40);
var v = view.CreateVisual(DrawView);
v.Tag = new FigureCaption(6, "A horizontal chain with two rectangles having weighted\nwidths and a fixed space between them:",
"rA.SetLeft(null, AnchorParam.Left, 60);\n" +
"rA.SetStarWidth(1);\n" +
"rAB.SetLeftAndOpposite(rA, AnchorParam.Right);\n" +
"rAB.SetWidth(50);\n" +
"rB.SetLeftAndOpposite(rAB, AnchorParam.Right);\n" +
"rB.SetStarWidth(2);\n" +
"rB.SetRight(null, AnchorParam.Right, -60);");
v.LayoutRect.AnchorExact(null);
v = view.CreateVisual(DrawRect);
var rA = v.LayoutRect;
rA.SetHeight(40);
rA.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "A";
v = view.CreateVisual(DrawRect);
var rB = v.LayoutRect;
rB.SetHeight(40);
rB.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "B";
var rAB = view.CreateVisual(DrawLeftRightLines).LayoutRect;
rAB.AnchorTopBottom(rA, 0, 0);
// The SetStarWidth method sets the weight of width of that specific
// rectangle relative to the width of other rectangles that belong
// to the same chain and have the "star" width.
// SetLeftAndOpposite method makes a chain of rectangles that affect
// the position of each other in both directions.
rA.SetLeft(null, AnchorParam.Left, 60);
rA.SetStarWidth(1);
rAB.SetLeftAndOpposite(rA, AnchorParam.Right);
rAB.SetWidth(50);
rB.SetLeftAndOpposite(rAB, AnchorParam.Right);
rB.SetStarWidth(2);
rB.SetRight(null, AnchorParam.Right, -60);
var r = view.CreateVisual(DrawLineWithLeftArrow).LayoutRect;
r.AnchorTopBottom(rA, 0, 0);
r.SetLeft(null, AnchorParam.Left);
r.SetRight(rA, AnchorParam.Left);
r = view.CreateVisual(DrawLineWithRightArrow).LayoutRect;
r.AnchorTopBottom(rB, 0, 0);
r.SetLeft(rB, AnchorParam.Right);
r.SetRight(null, AnchorParam.Right);
surf.Render(g);
}
// Figure 7: Horizontal chain with evenly distributed rectangles and margins.
static void DrawSample4(GcGraphics g, Size _)
{
g.Transform = Matrix3x2.CreateScale(1.8f);
var surf = new Surface();
var view = surf.CreateView(430, 120).Translate(30, 40);
var v = view.CreateVisual(DrawView);
v.Tag = new FigureCaption(7, "A horizontal chain with three evenly distributed rectangles\nafter margins are accounted for:",
"rA.SetLeft(null, AnchorParam.Left, 60);\n" +
"rA.SetWidth(70);\n" +
"rAB.SetLeftAndOpposite(rA, AnchorParam.Right);\n" +
"rAB.SetStarWidth(1);\n" +
"rAB.SetRightAndOpposite(rB, AnchorParam.Left);\n" +
"rB.SetWidth(70);\n" +
"rBC.SetLeftAndOpposite(rB, AnchorParam.Right);\n" +
"rBC.SetStarWidth(1);\n" +
"rC.SetLeftAndOpposite(rBC, AnchorParam.Right);\n" +
"rC.SetWidth(70);\n" +
"rC.SetRight(null, AnchorParam.Right, -60);");
v.LayoutRect.AnchorExact(null);
v = view.CreateVisual(DrawRect);
var rA = v.LayoutRect;
rA.SetHeight(40);
rA.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "A";
v = view.CreateVisual(DrawRect);
var rB = v.LayoutRect;
rB.SetHeight(40);
rB.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "B";
v = view.CreateVisual(DrawRect);
var rC = v.LayoutRect;
rC.SetHeight(40);
rC.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "C";
var rAB = view.CreateVisual(DrawLeftRightLines).LayoutRect;
rAB.AnchorTopBottom(rA, 0, 0);
var rBC = view.CreateVisual(DrawLeftRightLines).LayoutRect;
rBC.AnchorTopBottom(rB, 0, 0);
rA.SetLeft(null, AnchorParam.Left, 60);
rA.SetWidth(70);
rAB.SetLeftAndOpposite(rA, AnchorParam.Right);
rAB.SetStarWidth(1);
rAB.SetRightAndOpposite(rB, AnchorParam.Left);
rB.SetWidth(70);
rBC.SetLeftAndOpposite(rB, AnchorParam.Right);
rBC.SetStarWidth(1);
rC.SetLeftAndOpposite(rBC, AnchorParam.Right);
rC.SetWidth(70);
rC.SetRight(null, AnchorParam.Right, -60);
var r = view.CreateVisual(DrawLineWithLeftArrow).LayoutRect;
r.AnchorTopBottom(rA, 0, 0);
r.SetLeft(null, AnchorParam.Left);
r.SetRight(rA, AnchorParam.Left);
r = view.CreateVisual(DrawLineWithRightArrow).LayoutRect;
r.AnchorTopBottom(rC, 0, 0);
r.SetLeft(rC, AnchorParam.Right);
r.SetRight(null, AnchorParam.Right);
surf.Render(g);
}
// Figure 8: Horizontal chain with evenly distributed rectangles, no margins.
static void DrawSample5(GcGraphics g, Size _)
{
g.Transform = Matrix3x2.CreateScale(2);
var surf = new Surface();
var view = surf.CreateView(340, 120).Translate(30, 40);
var v = view.CreateVisual(DrawView);
v.Tag = new FigureCaption(8, "Same as Figure 7, without accounting for the margins:",
"rA.SetLeft(null, AnchorParam.Left);\n" +
"rA.SetWidth(70);\n" +
"rA.SetRightAndOpposite(rAB, AnchorParam.Left);\n" +
"rAB.SetStarWidth(1);\n" +
"rB.SetLeftAndOpposite(rAB, AnchorParam.Right);\n" +
"rB.SetWidth(70);\n" +
"rB.SetRightAndOpposite(rBC, AnchorParam.Left);\n" +
"rBC.SetStarWidth(1);\n" +
"rC.SetLeftAndOpposite(rBC, AnchorParam.Right);\n" +
"rC.SetWidth(70);\n" +
"rC.SetRight(null, AnchorParam.Right);");
v.LayoutRect.AnchorExact(null);
v = view.CreateVisual(DrawRect);
var rA = v.LayoutRect;
rA.SetHeight(40);
rA.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "A";
v = view.CreateVisual(DrawRect);
var rB = v.LayoutRect;
rB.SetHeight(40);
rB.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "B";
v = view.CreateVisual(DrawRect);
var rC = v.LayoutRect;
rC.SetHeight(40);
rC.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "C";
var rAB = view.CreateVisual(DrawLeftRightLines).LayoutRect;
rAB.AnchorTopBottom(rA, 0, 0);
var rBC = view.CreateVisual(DrawLeftRightLines).LayoutRect;
rBC.AnchorTopBottom(rB, 0, 0);
rA.SetLeft(null, AnchorParam.Left);
rA.SetWidth(70);
rA.SetRightAndOpposite(rAB, AnchorParam.Left);
rAB.SetStarWidth(1);
rB.SetLeftAndOpposite(rAB, AnchorParam.Right);
rB.SetWidth(70);
rB.SetRightAndOpposite(rBC, AnchorParam.Left);
rBC.SetStarWidth(1);
rC.SetLeftAndOpposite(rBC, AnchorParam.Right);
rC.SetWidth(70);
rC.SetRight(null, AnchorParam.Right);
surf.Render(g);
}
// Figure 9: Weighted horizontal chain without margins.
static void DrawSample6(GcGraphics g, Size _)
{
g.Transform = Matrix3x2.CreateScale(2);
var surf = new Surface();
var view = surf.CreateView(340, 120).Translate(30, 40);
var v = view.CreateVisual(DrawView);
v.Tag = new FigureCaption(9, "A weighted horizontal chain without margins:",
"rA.SetLeft(null, AnchorParam.Left);\n" +
"rA.SetStarWidth(1);\n" +
"rA.SetRightAndOpposite(rB, AnchorParam.Left);\n" +
"rB.SetStarWidth(2);\n" +
"rB.SetRightAndOpposite(rC, AnchorParam.Left);\n" +
"rC.SetStarWidth(2);\n" +
"rC.SetRight(null, AnchorParam.Right);");
v.LayoutRect.AnchorExact(null);
v = view.CreateVisual(DrawRect);
var rA = v.LayoutRect;
rA.SetHeight(40);
rA.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "A";
v = view.CreateVisual(DrawRect);
var rB = v.LayoutRect;
rB.SetHeight(40);
rB.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "B";
v = view.CreateVisual(DrawRect);
var rC = v.LayoutRect;
rC.SetHeight(40);
rC.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "C";
rA.SetLeft(null, AnchorParam.Left);
rA.SetStarWidth(1);
rA.SetRightAndOpposite(rB, AnchorParam.Left);
rB.SetStarWidth(2);
rB.SetRightAndOpposite(rC, AnchorParam.Left);
rC.SetStarWidth(2);
rC.SetRight(null, AnchorParam.Right);
surf.Render(g);
}
// Figure 10: Horizontal chain with weighted margins and packed rectangles.
static void DrawSample7(GcGraphics g, Size _)
{
g.Transform = Matrix3x2.CreateScale(2);
var surf = new Surface();
var view = surf.CreateView(370, 120).Translate(30, 40);
var v = view.CreateVisual(DrawView);
v.Tag = new FigureCaption(10, "Rectangles are packed together after margins with weights\nare accounted for:",
"rBeforeA.SetLeft(null, AnchorParam.Left);\n" +
"rBeforeA.SetStarWidth(3);\n" +
"rA.SetLeftAndOpposite(rBeforeA, AnchorParam.Right);\n" +
"rA.SetWidth(70);\n" +
"rB.SetLeftAndOpposite(rA, AnchorParam.Right);\n" +
"rB.SetWidth(70);\n" +
"rC.SetLeftAndOpposite(rB, AnchorParam.Right);\n" +
"rC.SetWidth(70);\n" +
"rAfterC.SetLeftAndOpposite(rC, AnchorParam.Right);\n" +
"rAfterC.SetStarWidth(1);\n" +
"rAfterC.SetRight(null, AnchorParam.Right);");
v.LayoutRect.AnchorExact(null);
v = view.CreateVisual(DrawRect);
var rA = v.LayoutRect;
rA.SetHeight(40);
rA.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "A";
v = view.CreateVisual(DrawRect);
var rB = v.LayoutRect;
rB.SetHeight(40);
rB.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "B";
v = view.CreateVisual(DrawRect);
var rC = v.LayoutRect;
rC.SetHeight(40);
rC.SetVerticalCenter(null, AnchorParam.VerticalCenter);
v.Tag = "C";
var rBeforeA = view.CreateVisual(DrawLineWithLeftArrow).LayoutRect;
rBeforeA.AnchorTopBottom(rA, 0, 0);
var rAfterC = view.CreateVisual(DrawLineWithRightArrow).LayoutRect;
rAfterC.AnchorTopBottom(rC, 0, 0);
rBeforeA.SetLeft(null, AnchorParam.Left);
rBeforeA.SetStarWidth(3);
rA.SetLeftAndOpposite(rBeforeA, AnchorParam.Right);
rA.SetWidth(70);
rB.SetLeftAndOpposite(rA, AnchorParam.Right);
rB.SetWidth(70);
rC.SetLeftAndOpposite(rB, AnchorParam.Right);
rC.SetWidth(70);
rAfterC.SetLeftAndOpposite(rC, AnchorParam.Right);
rAfterC.SetStarWidth(1);
rAfterC.SetRight(null, AnchorParam.Right);
surf.Render(g);
}
// Text flow in and around non-rectangular contours.
static void DrawSample8(GcGraphics g, Size pageSize)
{
g.FillRectangle(new RectangleF(0, 0, pageSize.Width, pageSize.Height), Color.White);
var surf = new Surface();
// Contour objects can be referenced in constraints defined for text rectangles.
CreateFigure1(surf, out Contour c1_outer);
CreateFigure2(surf, out Contour c2_outer, out Contour c2_inner);
const float rowHeight = 24f;
const float lineSpacing = 5f;
const float paragraphSpacing = 10f;
const float fontSize = 18f;
// The main View object that displays horizontal text.
var view = surf.CreateView(pageSize.Width, pageSize.Height);
var rcMargin = view.CreateSpace().LayoutRect;
rcMargin.AnchorDeflate(null, 20);
var tf = new TextFormat
{
Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "calibri.ttf")),
FontSizeInGraphicUnits = true,
FontSize = fontSize,
FontFeatures = new FontFeature[] { new FontFeature(FeatureTag.liga, false) }
};
var noRects = new List<ObjectRect>();
TextLayout tl = null;
var tso = new TextSplitOptions
{
RestObjectRects = noRects,
AllowMovingAllToRest = true
};
LayoutRect rcPrevTop = null;
LayoutRect rcPrevLeft = null;
bool paragraphStarted = false;
while (true)
{
var r0 = view.CreateVisual(DrawTextFragment).LayoutRect;
if (rcPrevLeft != null)
r0.SetTop(rcPrevLeft, AnchorParam.Top);
else if (rcPrevTop != null)
r0.SetTop(rcPrevTop, AnchorParam.Bottom, paragraphStarted ? lineSpacing : paragraphSpacing);
else
r0.SetTop(rcMargin, AnchorParam.Top);
if (rcPrevLeft is null)
r0.SetLeft(rcMargin, AnchorParam.Left);
else
r0.SetLeft(rcPrevLeft, AnchorParam.Right);
r0.SetHeight(rowHeight);
r0.AppendMaxRight(rcMargin, AnchorParam.Right);
var r1 = view.CreateSpace().LayoutRect;
r1.SetTop(r0, AnchorParam.Top);
r1.SetHeight(rowHeight);
r1.SetLeft(r0, AnchorParam.Right);
r1.AppendMaxRight(rcMargin, AnchorParam.Right);
r0.AppendMaxRight(c1_outer, ContourPosition.FirstInOutside);
r0.AppendMaxRight(c2_outer, ContourPosition.FirstInOutside);
r1.AppendMaxRight(c1_outer, ContourPosition.NextOutOutside);
r1.AppendMaxRight(c2_outer, ContourPosition.NextOutOutside);
surf.PerformLayout();
if (!paragraphStarted)
{
// It is important to have the ObjectRects property set
// to a not null value (an empty list works well) and
// the AllowOverhangingWords property set to true.
tl = g.CreateTextLayout();
tl.ObjectRects = noRects;
tl.TextAlignment = TextAlignment.Justified;
tl.AllowOverhangingWords = true;
tl.FirstLineIndent = 40f;
tl.JustifiedTextExtension = 0.1f;
tl.Append(Common.Util.LoremIpsum(1), tf);
tl.MaxWidth = r0.Width;
tl.MaxHeight = r0.Height;
((Visual)r0.Tag).Tag = tl;
if (!tl.PerformLayout())
{
paragraphStarted = true;
}
}
else
{
tso.RestMaxWidth = r0.Width;
tso.RestMaxHeight = r0.Height;
var res = tl.Split(tso, out TextLayout rest);
if (res != SplitResult.CannotSplit)
{
tl = rest;
((Visual)r0.Tag).Tag = rest;
if (tl.PerformLayout())
{
paragraphStarted = false;
}
}
}
if (paragraphStarted && r1.Width > 0f)
rcPrevLeft = r1;
else
{
((Space)r1.Tag).Detach();
var spacing = paragraphStarted ? lineSpacing : paragraphSpacing;
if (rcMargin.P2Y - r0.P2Y < spacing + rowHeight)
{
break;
}
rcPrevLeft = null;
rcPrevTop = r0;
}
}
tl.Truncate(TrimmingGranularity.Word);
// The second View showing text in the ellipse.
var view2 = surf.CreateView(pageSize.Width / 3, pageSize.Height / 2).Translate(520, 260).Rotate(15);
tf = new TextFormat(tf)
{
ForeColor = Color.Purple,
FontSize = 14,
};
tl = g.CreateTextLayout();
tl.ObjectRects = noRects;
tl.TextAlignment = TextAlignment.Center;
tl.AllowOverhangingWords = true;
tl.Append(Common.Util.LoremIpsum(1), tf);
rcPrevTop = null;
paragraphStarted = false;
while (true)
{
var r0 = view2.CreateSpace().LayoutRect;
if (rcPrevTop != null)
r0.SetTop(rcPrevTop, AnchorParam.Bottom, 4);
else
r0.SetTop(null, AnchorParam.Top);
r0.SetLeft(null, AnchorParam.Left);
r0.SetHeight(18);
r0.AppendMaxRight(null, AnchorParam.Right);
var r1 = view2.CreateVisual(DrawTextFragment).LayoutRect;
r1.SetTop(r0, AnchorParam.Top);
r1.SetHeight(18);
r1.SetLeft(r0, AnchorParam.Right);
r1.AppendMaxRight(null, AnchorParam.Right);
r0.AppendMaxRight(c2_inner, ContourPosition.FirstInInside);
r1.AppendMaxRight(c2_inner, ContourPosition.NextOutInside);
surf.PerformLayout();
if (r1.Width == 0f)
{
((Space)r1.Tag).Detach();
if (paragraphStarted)
{
break;
}
}
else if (!paragraphStarted)
{
tl.MaxWidth = r1.Width;
tl.MaxHeight = r1.Height;
((Visual)r1.Tag).Tag = tl;
if (tl.PerformLayout())
{
break;
}
paragraphStarted = true;
}
else
{
tso.RestMaxWidth = r1.Width;
tso.RestMaxHeight = r1.Height;
var res = tl.Split(tso, out TextLayout rest);
if (res == SplitResult.FitAll)
{
((Space)r1.Tag).Detach();
break;
}
if (res == SplitResult.Split)
{
((Visual)r1.Tag).Tag = rest;
tl = rest;
}
}
rcPrevTop = r0;
}
tl.Truncate(TrimmingGranularity.Word);
surf.Render(g);
}
//
// Common utility classes and methods.
//
class FigureCaption
{
public FigureCaption(int number, string description, string code = null)
{
Number = number;
Description = description;
Code = code;
}
public int Number { get; }
public string Description { get; }
public string Code { get; }
}
static void DrawTextFragment(GcGraphics g, Visual v)
{
if (v.Tag is TextLayout tl)
{
g.DrawTextLayout(tl, new PointF(0, 0));
}
}
static void CreateFigure1(Surface surf, out Contour c_outer)
{
// A View for the yellow polygon.
var view = surf.CreateView(0, 0).Translate(120, -40).Rotate(40);
var lv = view.LayoutView;
var c = lv.CreateContour();
var v = view.CreateVisual(c, true, (g, v) =>
{
g.FillPolygon(v.Points, Color.LemonChiffon);
g.DrawPolygon(v.Points, Color.Green, 3);
});
var rect = v.LayoutRect;
rect.AnchorTopLeft(null, 100, 150, 170, 70);
c.AddPoints(new AnchorPoint[]
{
rect.CreatePoint(0, 0, -20, -20),
rect.CreatePoint(1, 0, 20, -20),
rect.CreatePoint(1, 0, 20, -120),
rect.CreatePoint(1, 0, 120, -120),
rect.CreatePoint(1, 1, 120, 20),
rect.CreatePoint(0, 1, -20, 20)
});
surf.PerformLayout();
var points = c.MapToView(lv);
// To make the outer offset from the Contour we convert
// the Contour to a GraphicsPath, then apply the Widen
// method with a thick pen. The resulting figure is
// used for creating the outer Contour.
var gp = new GraphicsPath(new FreeFormPolygon(points));
var gp2 = gp.Widen(new GCDRAW.Pen(Color.White, 7 * 2));
var fig_outer = gp2.Figures[0];
fig_outer.Flatten();
var outer_points = fig_outer.TransformedPoints;
c_outer = lv.CreateContour();
for (int i = 0; i < outer_points.Length; i++)
{
var p = outer_points[i];
c_outer.AddPoint(lv.CreatePoint(0f, 0f, p.X, p.Y));
}
}
static void CreateFigure2(Surface surf, out Contour c_outer, out Contour c_inner)
{
// A View for the rotated ellipse.
var view = surf.CreateView(360, 150).Translate(450, 530).Rotate(-40);
var lv = view.LayoutView;
var c = lv.CreateContour();
view.CreateVisual(c, false, (g, v) =>
{
g.DrawPolygon(v.Points, Color.DeepPink, 3);
});
// To make the inner and the outer offsets from the ellipse
// we convert the elliptic figure to an array of points which
// is used later for creating a GraphicsPath. Then, we call
// the GraphicsPath.Widen method with a thick pen.
// The resulting outer and inner figures can be converted
// into new contours for later use in constraints.
IFigure ef = new EllipticFigure(lv.AsRectF());
ef.Flatten();
var points = ef.TransformedPoints;
int count = points.Length;
var list = new List<AnchorPoint>(count);
for (int i = 0; i < count; i++)
{
var p = points[i];
list.Add(lv.CreatePoint(0, 0, p.X, p.Y));
}
c.AddPoints(list);
var gp = new GraphicsPath(ef);
var gp2 = gp.Widen(new GCDRAW.Pen(Color.White, 7 * 2));
var fig_outer = gp2.Figures[0];
var fig_inner = gp2.Figures[1];
fig_outer.Flatten();
var outer_points = fig_outer.TransformedPoints;
c_outer = lv.CreateContour();
for (int i = 0; i < outer_points.Length; i++)
{
var p = outer_points[i];
c_outer.AddPoint(lv.CreatePoint(0f, 0f, p.X, p.Y));
}
fig_inner.Flatten();
var inner_points = fig_inner.TransformedPoints;
c_inner = lv.CreateContour();
for (int i = 0; i < inner_points.Length; i++)
{
var p = inner_points[i];
c_inner.AddPoint(lv.CreatePoint(0f, 0f, p.X, p.Y));
}
}
static readonly TextFormat FormatBox = new TextFormat()
{
Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "segoeui.ttf")),
FontSize = 14,
FontSizeInGraphicUnits = true,
ForeColor = BoxColor
};
static readonly TextFormat FormatDesc = new TextFormat(FormatBox)
{
FontSize = 12,
ForeColor = DescColor
};
static readonly TextFormat FormatCaption = new TextFormat(FormatDesc)
{
FontBold = true
};
static readonly TextFormat FormatCode = new TextFormat(FormatDesc)
{
Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "cour.ttf")),
ForeColor = CodeColor
};
static void DrawView(GcGraphics g, Visual v)
{
var rect = v.AsRectF();
g.FillRectangle(rect, Color.FromArgb(31, 82, 100));
g.DrawRectangle(rect, new GCDRAW.Pen(Color.FromArgb(200, 200, 200), 1)
{
DashPattern = new float[] { 7, 3 }
});
if (v.Tag is FigureCaption fc)
{
var tl = g.CreateTextLayout();
tl.Append($"Figure {fc.Number}. ", FormatCaption);
tl.AppendLine(fc.Description, FormatDesc);
if (fc.Code != null)
{
tl.Append(fc.Code, FormatCode);
}
g.DrawTextLayout(tl, new PointF(0, v.Height + 5));
}
}
static void DrawVertGuideline(GcGraphics g, Visual v)
{
g.DrawLine(new PointF(0, 0), new PointF(0, v.Height), new GCDRAW.Pen(BoxColor, 1)
{
DashPattern = new float[] { 1, 2 }
});
if (v.Tag is string s)
{
var tl = g.CreateTextLayout();
tl.Append(s, FormatDesc);
tl.PerformLayout();
var rect = tl.ContentRectangle;
rect.X = -rect.Width * 0.5f;
rect.Y = 10;
rect.Inflate(3, 1);
g.FillRoundRect(rect, 3, RectColor);
g.DrawTextLayout(tl, new PointF(rect.X + 3, rect.Y + 1));
}
}
static void DrawRect(GcGraphics g, Visual v)
{
var rect = v.AsRectF();
g.FillRectangle(rect, RectColor);
g.DrawRectangle(rect, new GCDRAW.Pen(BoxColor, 1));
if (v.Tag is string s)
{
var tl = g.CreateTextLayout();
tl.MaxWidth = v.Width;
tl.MaxHeight = v.Height;
tl.TextAlignment = TextAlignment.Center;
tl.ParagraphAlignment = ParagraphAlignment.Center;
tl.Append(s, FormatBox);
g.DrawTextLayout(tl, new PointF(0, 0));
}
}
static void DrawLineWithLeftArrow(GcGraphics g, Visual v)
{
var y = v.Height * 0.5f;
g.DrawLine(new PointF(5, y), new PointF(v.Width, y), new GCDRAW.Pen(BoxColor, 0.7f));
DrawLeftArrow(g, 0, y);
}
static void DrawLineWithRightArrow(GcGraphics g, Visual v)
{
var y = v.Height * 0.5f;
g.DrawLine(new PointF(0, y), new PointF(v.Width - 5, y), new GCDRAW.Pen(BoxColor, 0.7f));
DrawRightArrow(g, v.Width, y);
}
static void DrawLeftRightLines(GcGraphics g, Visual v)
{
var y = v.Height * 0.33f;
g.DrawLine(new PointF(5, y), new PointF(v.Width, y), new GCDRAW.Pen(BoxColor, 0.7f));
DrawLeftArrow(g, 0, y);
y = v.Height * 0.66f;
g.DrawLine(new PointF(0, y), new PointF(v.Width - 5, y), new GCDRAW.Pen(BoxColor, 0.7f));
DrawRightArrow(g, v.Width, y);
}
static void DrawVertLineWithLeftArrow(GcGraphics g, Visual v)
{
var x = v.Width * 0.5f;
g.DrawLines(new PointF[]
{
new PointF(5, 0),
new PointF(x, 0),
new PointF(x, v.Height),
new PointF(v.Width, v.Height)
}, new GCDRAW.Pen(BoxColor, 0.7f));
DrawLeftArrow(g, 0, 0);
}
static void DrawLineWithUpArrow(GcGraphics g, Visual v)
{
var x = v.Width * 0.5f;
g.DrawLine(new PointF(x, 5), new PointF(x, v.Height), new GCDRAW.Pen(BoxColor, 0.7f));
DrawUpArrow(g, x, 0);
}
static void DrawLeftArrow(GcGraphics g, float x, float y)
{
var pts = new PointF[]
{
new PointF(x, y),
new PointF(x + 8, y - 2.5f),
new PointF(x + 8, y + 2.5f),
};
g.FillPolygon(pts, BoxColor);
}
static void DrawRightArrow(GcGraphics g, float x, float y)
{
var pts = new PointF[]
{
new PointF(x, y),
new PointF(x - 8, y + 2.5f),
new PointF(x - 8, y - 2.5f),
};
g.FillPolygon(pts, BoxColor);
}
static void DrawUpArrow(GcGraphics g, float x, float y)
{
var pts = new PointF[]
{
new PointF(x, y),
new PointF(x + 2.5f, y + 8),
new PointF(x - 2.5f, y + 8),
};
g.FillPolygon(pts, BoxColor);
}
}
}