[]
        
(Showing Draft Content)

Layouts

Layouts in DsImaging use a constraint‑based layout model. Instead of calculating coordinates manually, you define relationships between rectangles: for example aligning edges, maintaining spacing, or distributing available space proportionally. The layout engine evaluates these constraints and calculates the final position and size of each element.

This approach follows the same principles used by modern UI layout systems such as Android ConstraintLayout and Apple Auto Layout.

Layouts Overview

The layout engine positions rectangles based on constraints. Each rectangle defines rules that describe how its position and size relate to other rectangles or to the layout view.

Instead of setting coordinates directly, you specify constraints such as:

• align the left side of one rectangle to another

• keep a fixed distance between two sides

• maintain proportional widths

The layout engine evaluates all constraints and calculates the final coordinates.

Layout Objects

The layout system is built around three main classes that work together to define and calculate layouts.

LayoutHost - LayoutHost is the root object of the layout system. It manages the layout engine and is responsible for calculating rectangle positions. A LayoutHost creates LayoutView objects and performs layout evaluation when the PerformLayout method is called.

LayoutView - LayoutView defines the coordinate space in which layout rectangles are placed. Each view has a fixed width and height and may have a transformation matrix applied. A LayoutView acts as a container for LayoutRect objects and provides the reference area for layout constraints.

LayoutRect - LayoutRect represents a rectangle whose position and size are determined by constraints. Each rectangle is defined by its corner points, and the layout engine calculates their coordinates when layout is performed.

image

The layout engine calculates the coordinates of these points for every rectangle based on the constraints defined in the layout.

Basic Layout Workflow

The typical workflow when using layouts is:

  1. Create a LayoutHost.

  2. Create a LayoutView that defines the layout area.

  3. Create LayoutRect objects.

  4. Define constraints between rectangles.

  5. Call PerformLayout to calculate positions.

  6. Draw the rectangles or other graphics using the calculated transformations.

Simple Position Constraints

Position constraints define the location or size of a rectangle relative to another rectangle, the layout view, or a fixed value.

Common constraints include:

• Left, Right, Top, Bottom

• Width and Height

• HorizontalCenter and VerticalCenter

Use methods such as SetLeft, SetTop, SetWidth, or SetHeight to add constraints. Each constraint defines how a specific parameter of a rectangle is calculated.

For example, the following code creates a rectangle positioned relative to another rectangle:

var marginRect = view.CreateRect();
// marginRect.SetLeft(null, AnchorParam.Left, 36);
// marginRect.SetTop(null, AnchorParam.Top, 36);
// marginRect.SetRight(null, AnchorParam.Right, -36);
// marginRect.SetBottom(null, AnchorParam.Bottom, -36);
marginRect.AnchorDeflate(null, 36);

var redRect = view.CreateRect();
redRect.SetLeft(marginRect, AnchorParam.Left, 60);
redRect.SetTop(marginRect, AnchorParam.Top, 40);
redRect.SetWidth(180);
redRect.SetHeight(70);

host.PerformLayout();

DrawRect(marginRect, Color.Green);
DrawRect(redRect, Color.Red);

image

Helper methods can define several constraints at once. For example, AnchorDeflate creates a rectangle that's inset from another rectangle by the specified distance on all sides.

Chained Position Constraints

In some layouts, the position of adjacent rectangles depends on both rectangles rather than a single dependency. This situation occurs when widths or heights are calculated dynamically.

Chained constraints connect two sides without specifying which rectangle depends on the other. Use the following methods to create these constraints:

SetLeftAndOpposite

SetRightAndOpposite

SetTopAndOpposite

SetBottomAndOpposite

Star constraints allow proportional sizing. A rectangle can specify a relative width or height using SetStarWidth or SetStarHeight. The layout engine distributes the available space according to the star values.

Example

using System.Drawing;
using GrapeCity.Documents.Drawing;
using GrapeCity.Documents.Layout;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Text;

const float pageWidth = 600;
const float pageHeight = 400;

var doc = new GcPdfDocument();
var page = doc.NewPage();
page.Size = new SizeF(pageWidth, pageHeight);

var g = page.Graphics;
var baseTransform = g.Transform;

var fmt = new TextFormat
{
    FontName = "Calibri",
    FontSize = 16
};

var host = new LayoutHost();
var view = host.CreateView(pageWidth, pageHeight);

var marginRect = view.CreateRect("margin");
marginRect.AnchorDeflate(null, 36);

var redRect = view.CreateRect("red");
redRect.AnchorLeftTopBottom(marginRect, 10, 50, 150);
redRect.SetStarWidth(20);

var blueRect = view.CreateRect("blue");
blueRect.AnchorTopBottom(marginRect, 50, 180);
blueRect.SetStarWidth(100);

var orangeRect = view.CreateRect("orange");
orangeRect.AnchorTopBottom(marginRect, 50, 120);
orangeRect.SetWidth(70);

var purpleRect = view.CreateRect("purple");
purpleRect.AnchorRightTopBottom(marginRect, 10, 50, 150);
purpleRect.SetStarWidth(35);

blueRect.SetLeftAndOpposite(redRect, AnchorParam.Right, 10);
orangeRect.SetLeftAndOpposite(blueRect, AnchorParam.Right, 10);
purpleRect.SetLeftAndOpposite(orangeRect, AnchorParam.Right, 10);

host.PerformLayout();

DrawRect(marginRect, Color.Green);
DrawRect(redRect, Color.Red);
DrawRect(blueRect, Color.Blue);
DrawRect(orangeRect, Color.Orange);
DrawRect(purpleRect, Color.Purple);

void DrawRect(LayoutRect rect, Color color)
{
    g.Transform = rect.Transform.Multiply(baseTransform);
    g.DrawRectangle(rect.AsRectF(), new Pen(color));
    g.DrawString((string)rect.Tag, fmt, new PointF(3, 0));
}

g.Transform = baseTransform;
doc.Save("doc3.pdf");

image

Minimum or Maximum Position Constraints

Minimum and maximum position constraints limit how far a rectangle can move relative to other elements.

Use the following methods to add these constraints:

AppendMinTop

AppendMaxBottom

AppendMinLeft

AppendMaxRight

You can append multiple constraints to the same parameter. The layout engine evaluates all constraints and selects the value that satisfies them.

Example

using System.Drawing;
using GrapeCity.Documents.Drawing;
using GrapeCity.Documents.Layout;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Text;

const float pageWidth = 600;
const float pageHeight = 400;

var doc = new GcPdfDocument();
var page = doc.NewPage();
page.Size = new SizeF(pageWidth, pageHeight);

var g = page.Graphics;
var baseTransform = g.Transform;

var fmt = new TextFormat
{
    FontName = "Calibri",
    FontSize = 16
};

var host = new LayoutHost();
var view = host.CreateView(pageWidth, pageHeight);

var marginRect = view.CreateRect("margin");
marginRect.AnchorDeflate(null, 36);

var redRect = view.CreateRect("red");
redRect.AnchorLeftTopBottom(marginRect, 10, 50, 150);
redRect.SetStarWidth(20);

var blueRect = view.CreateRect("blue");
blueRect.AnchorTopBottom(marginRect, 50, 180);
blueRect.SetStarWidth(100);

var orangeRect = view.CreateRect("orange");
orangeRect.AnchorTopBottom(marginRect, 50, 120);
orangeRect.SetWidth(70);

var purpleRect = view.CreateRect("purple");
purpleRect.AnchorRightTopBottom(marginRect, 10, 50, 150);
purpleRect.SetStarWidth(35);

blueRect.SetLeftAndOpposite(redRect, AnchorParam.Right, 10);
orangeRect.SetLeftAndOpposite(blueRect, AnchorParam.Right, 10);
purpleRect.SetLeftAndOpposite(orangeRect, AnchorParam.Right, 10);

var rect1 = view.CreateRect("1");
rect1.SetBottom(marginRect, AnchorParam.Bottom, -40);
rect1.AppendMinTop(redRect, AnchorParam.Bottom, 20);
rect1.AppendMinTop(blueRect, AnchorParam.Bottom, 20);
rect1.AppendMinTop(orangeRect, AnchorParam.Bottom, 20);
rect1.AppendMinTop(purpleRect, AnchorParam.Bottom, 20);
rect1.SetLeft(redRect, AnchorParam.HorizontalCenter);
rect1.SetStarWidth(1);

var rect2 = view.CreateRect("2");
rect2.SetAngle(rect1, 90);
rect2.SetRight(marginRect, AnchorParam.Bottom, -40);
rect2.AppendMinLeft(redRect, AnchorParam.Bottom, 20);
rect2.AppendMinLeft(blueRect, AnchorParam.Bottom, 20);
rect2.AppendMinLeft(orangeRect, AnchorParam.Bottom, 20);
rect2.AppendMinLeft(purpleRect, AnchorParam.Bottom, 20);
rect2.SetBottomAndOpposite(rect1, AnchorParam.Right, -10);
rect2.SetStarHeight(1);

var rect3 = view.CreateRect("3");
rect3.SetAngle(rect2, 90);
rect3.SetTop(marginRect, AnchorParam.Bottom, 40);
rect3.AppendMaxBottom(redRect, AnchorParam.Bottom, -20);
rect3.AppendMaxBottom(blueRect, AnchorParam.Bottom, -20);
rect3.AppendMaxBottom(orangeRect, AnchorParam.Bottom, -20);
rect3.AppendMaxBottom(purpleRect, AnchorParam.Bottom, -20);
rect3.SetRightAndOpposite(rect2, AnchorParam.Top, -10);
rect3.SetStarWidth(1);

var rect4 = view.CreateRect("4");
rect4.SetAngle(rect3, 90);
rect4.SetLeft(marginRect, AnchorParam.Bottom, 40);
rect4.AppendMaxRight(redRect, AnchorParam.Bottom, -20);
rect4.AppendMaxRight(blueRect, AnchorParam.Bottom, -20);
rect4.AppendMaxRight(orangeRect, AnchorParam.Bottom, -20);
rect4.AppendMaxRight(purpleRect, AnchorParam.Bottom, -20);
rect4.SetTopAndOpposite(rect3, AnchorParam.Left, 10);
rect4.SetStarHeight(1);

var rect5 = view.CreateRect("5");
rect5.SetAngle(rect4, 90);
rect5.SetBottom(marginRect, AnchorParam.Bottom, -40);
rect5.AppendMinTop(redRect, AnchorParam.Bottom, 20);
rect5.AppendMinTop(blueRect, AnchorParam.Bottom, 20);
rect5.AppendMinTop(orangeRect, AnchorParam.Bottom, 20);
rect5.AppendMinTop(purpleRect, AnchorParam.Bottom, 20);
rect5.SetLeftAndOpposite(rect4, AnchorParam.Bottom, 10);
rect5.SetStarWidth(1);
rect5.SetRight(purpleRect, AnchorParam.HorizontalCenter);

host.PerformLayout();

DrawRect(marginRect, Color.Green);
DrawRect(redRect, Color.Red);
DrawRect(blueRect, Color.Blue);
DrawRect(orangeRect, Color.Orange);
DrawRect(purpleRect, Color.Purple);
DrawRect(rect1, Color.BurlyWood);
DrawRect(rect2, Color.YellowGreen);
DrawRect(rect3, Color.PaleVioletRed);
DrawRect(rect4, Color.DimGray);
DrawRect(rect5, Color.CornflowerBlue);

void DrawRect(LayoutRect rect, Color color)
{
    g.Transform = rect.Transform.Multiply(baseTransform);
    g.DrawRectangle(rect.AsRectF(), new Pen(color));
    g.DrawString((string)rect.Tag, fmt, new PointF(3, 0));
}

g.Transform = baseTransform;
doc.Save("doc4.pdf");

image

Anchor Points

Anchor points define relative positions inside a rectangle or layout view. They're useful when you want to position elements based on percentages rather than absolute coordinates.

You can create an anchor point using the CreatePoint method. The parameters represent the relative width and height factors inside the rectangle or view.

Example

using System.Drawing;
using GrapeCity.Documents.Drawing;
using GrapeCity.Documents.Layout;
using GrapeCity.Documents.Pdf;

const float pageWidth = 600;
const float pageHeight = 400;

var doc = new GcPdfDocument();
var page = doc.NewPage();
page.Size = new SizeF(pageWidth, pageHeight);
var g = page.Graphics;
var baseTransform = g.Transform;

var host = new LayoutHost();
var view = host.CreateView(pageWidth, pageHeight);

var marginRect = view.CreateRect();
marginRect.AnchorDeflate(null, 36);

var ap1 = marginRect.CreatePoint(0.25f, 0.25f);
var ap2 = marginRect.CreatePoint(0.75f, 0.25f);
var ap3 = marginRect.CreatePoint(0.25f, 0.75f);
var ap4 = marginRect.CreatePoint(0.75f, 0.75f);

var r1 = view.CreateRect();
AnchorCenter(r1, ap1);

var r2 = view.CreateRect();
AnchorCenter(r2, ap2);

var r3 = view.CreateRect();
AnchorCenter(r3, ap3);

var r4 = view.CreateRect();
AnchorCenter(r4, ap4);

void AnchorCenter(LayoutRect r, AnchorPoint ap)
{
    r.SetHorizontalCenter(ap);
    r.SetVerticalCenter(ap);
    r.SetWidth(10);
    r.SetHeight(10);
}

var redRect = view.CreateRect();
redRect.SetLeft(ap1);
redRect.SetTop(ap1);
redRect.SetRight(ap4);
redRect.SetBottom(ap4);

host.PerformLayout();

DrawRect(marginRect, Color.Green);
DrawRect(r1, Color.CornflowerBlue);
DrawRect(r2, Color.CornflowerBlue);
DrawRect(r3, Color.CornflowerBlue);
DrawRect(r4, Color.CornflowerBlue);
DrawRect(redRect, Color.Red);

void DrawRect(LayoutRect rect, Color color)
{
    g.Transform = rect.Transform.Multiply(baseTransform);
    g.DrawRectangle(rect.AsRectF(), new Pen(color));
}

g.Transform = baseTransform;
doc.Save("doc5.pdf");

image

Constraints based on other LayoutView

Rectangles normally reference other rectangles within the same LayoutView. However, anchor points allow constraints to reference elements from different views.

This makes it possible to coordinate layouts across multiple coordinate systems.

Example

using System.Drawing;
using System.Numerics;
using GrapeCity.Documents.Common;
using GrapeCity.Documents.Drawing;
using GrapeCity.Documents.Imaging;
using GrapeCity.Documents.Layout;

var host = new LayoutHost();

var view1 = host.CreateView(10, 10, Matrix.CreateRotation(Math.PI / 6));
var rc1 = view1.CreateRect();
rc1.AnchorTopLeft(null, -20, 220, 300, 200);

var ap1 = rc1.CreatePoint(0, 0);
var ap2 = rc1.CreatePoint(1, 0);
var ap3 = rc1.CreatePoint(1, 1);
var ap4 = rc1.CreatePoint(0, 1);

var view2 = host.CreateView(10, 10, Matrix.CreateRotation(-Math.PI / 9));
var rc2 = view2.CreateRect();

rc2.SetTop(ap1, -20);
rc2.SetRight(ap2, 20);
rc2.SetBottom(ap3, 20);
rc2.SetLeft(ap4, -20);

host.PerformLayout();

using var bmp = new GcBitmap(600, 550, true);
using var g = bmp.CreateGraphics(Color.White);
var m = Matrix3x2.CreateTranslation(20, 20);
var pen = new Pen(Color.Coral, 2);

DrawRect(rc1, Color.CornflowerBlue);
DrawRect(rc2, Color.Green);

DrawPoint(ap1);
DrawPoint(ap2);
DrawPoint(ap3);
DrawPoint(ap4);

void DrawRect(LayoutRect r, Color c)
{
    g.Transform = r.Transform.Multiply(m);
    g.DrawRectangle(r.AsRectF(), new Pen(c, 2));
}

void DrawPoint(AnchorPoint ap)
{
    g.Transform = ap.Transform.Multiply(m);
    g.DrawEllipse(new RectangleF(-5, -5, 10, 10), pen);
}

bmp.SaveAsPng("img1.png");

image

Dependent Views and Transformations

A LayoutHost can contain multiple LayoutView objects. You can also position a view relative to another view by using a transformation matrix.

When the transformation of a parent view changes, dependent views are updated automatically when the layout is recalculated.

Example

using System.Drawing;
using System.Numerics;
using GrapeCity.Documents.Common;
using GrapeCity.Documents.Drawing;
using GrapeCity.Documents.Imaging;
using GrapeCity.Documents.Layout;

var host = new LayoutHost();

var view1 = host.CreateView(240, 300);
var rc1 = view1.CreateRect();
rc1.AnchorExact(null);

var view2 = host.CreateView(100, 150);
var rc2 = view2.CreateRect();
rc2.AnchorExact(null);

var view3 = host.CreateView(70, 50);
var rc3 = view3.CreateRect();
rc3.AnchorExact(null);

const double DegToRad = Math.PI / 180;

var m2 = Matrix.CreateRotation(DegToRad * 45);
view2.SetRelativeTransform(view1, m2.Translate(120, -100));

var m3 = Matrix.CreateRotation(DegToRad * -20);
view3.SetRelativeTransform(view2, m3.Translate(-23, 90));

host.PerformLayout();

using var bmp = new GcBitmap(850, 350, true);
using var g = bmp.CreateGraphics(Color.White);
var m = Matrix3x2.CreateTranslation(20, 20);

Draw();

view1.Transform = Matrix.CreateTranslation(350, 50).Scale(0.7).Rotate(DegToRad * 20);
host.PerformLayout();

Draw();

view1.Transform = Matrix.CreateTranslation(520, 200).Scale(0.8).Rotate(DegToRad * -70);
host.PerformLayout();

Draw();

void Draw()
{
    g.Transform = rc1.Transform.Multiply(m);
    g.DrawRectangle(rc1.AsRectF(), new Pen(Color.CornflowerBlue, 2));

    g.Transform = rc2.Transform.Multiply(m);
    g.DrawRectangle(rc2.AsRectF(), new Pen(Color.Orange, 2));

    g.Transform = rc3.Transform.Multiply(m);
    g.DrawRectangle(rc3.AsRectF(), new Pen(Color.Violet, 2));
}

bmp.SaveAsPng("img2.png");

image

Contours

Contours define closed shapes composed of anchor points. You can use a contour to constrain a rectangle so that it stays outside, inside, or intersects the contour.

You can create contours using CreateContour, and add anchor points using AddPoint or AddPoints.

Rectangle sides can then reference the contour using constraint methods such as:

AppendMinLeft

AppendMaxRight

AppendMinTop

AppendMaxBottom

Example

using System.Drawing;
using System.Numerics;
using GrapeCity.Documents.Drawing;
using GrapeCity.Documents.Imaging;
using GrapeCity.Documents.Layout;

var host = new LayoutHost();
LayoutView view = host.CreateView(800, 400);

var contour = view.CreateContour();
contour.AddPoints(new AnchorPoint[]
{
    view.CreatePoint(0, 0, 400, 0),
    view.CreatePoint(0, 0, 600, 400),
    view.CreatePoint(0, 0, 400, 200),
    view.CreatePoint(0, 0, 200, 400)
});

// first row

var rc11 = view.CreateRect(); // Blue (Outside)
rc11.AnchorLeftTopBottom(null, 0, 20, 310);
rc11.AppendMaxRight(contour, ContourPosition.FirstInOutside);

var rc12 = view.CreateRect(); // Blue (Outside)
rc12.AnchorRightTopBottom(null, 0, 20, 310);
rc12.AppendMinLeft(contour, ContourPosition.FirstInOutside);

// second row

var rc21 = view.CreateRect(); // Blue (Outside)
rc21.AnchorLeftTopBottom(null, 0, 120, 210);
rc21.AppendMaxRight(contour, ContourPosition.FirstInOutside);

var rc22 = view.CreateRect(); // Violet (Intersection)
rc22.AnchorTopBottom(null, 120, 210);
rc22.SetLeft(rc21, AnchorParam.Right);
rc22.AppendMaxRight(contour, ContourPosition.FirstInInside);

var rc23 = view.CreateRect(); // Orange (Inside)
rc23.AnchorTopBottom(null, 120, 210);
rc23.SetLeft(rc22, AnchorParam.Right);
rc23.AppendMaxRight(contour, ContourPosition.NextOutInside);

var rc24 = view.CreateRect(); // Violet (Intersection)
rc24.AnchorTopBottom(null, 120, 210);
rc24.SetLeft(rc23, AnchorParam.Right);
rc24.AppendMaxRight(contour, ContourPosition.NextOutOutside);

var rc25 = view.CreateRect(); // Blue (Outside)
rc25.SetLeft(rc24, AnchorParam.Right);
rc25.AnchorRightTopBottom(null, 0, 120, 210);

// third row

var rc31 = view.CreateRect(); // Blue (Outside)
rc31.AnchorRightTopBottom(null, 0, 220, 110);
rc31.AppendMinLeft(contour, ContourPosition.FirstInOutside);

var rc32 = view.CreateRect(); // Violet (Intersection)
rc32.AnchorTopBottom(null, 220, 110);
rc32.SetRight(rc31, AnchorParam.Left);
rc32.AppendMinLeft(contour, ContourPosition.LastOutOutside);

var rc33 = view.CreateRect(); // Blue (Outside)
rc33.SetRight(rc32, AnchorParam.Left);
rc33.AnchorLeftTopBottom(null, 0, 220, 110);

// fourth row

var rc41 = view.CreateRect(); // Blue (Outside)
rc41.AnchorLeftTopBottom(null, 0, 320, 10);
rc41.AppendMaxRight(contour, ContourPosition.FirstInOutside);

var rc42 = view.CreateRect(); // Violet (Intersection)
rc42.AnchorTopBottom(null, 320, 10);
rc42.SetLeft(rc41, AnchorParam.Right);
rc42.AppendMaxRight(contour, ContourPosition.NextOutOutside);

var rc43 = view.CreateRect(); // Blue (Outside)
rc43.AnchorTopBottom(null, 320, 10);
rc43.SetLeft(rc42, AnchorParam.Right);
rc43.AppendMaxRight(contour, ContourPosition.FirstInOutside);

var rc44 = view.CreateRect(); // Violet (Intersection)
rc44.AnchorTopBottom(null, 320, 10);
rc44.SetLeft(rc43, AnchorParam.Right);
rc44.AppendMaxRight(contour, ContourPosition.NextOutOutside);

var rc45 = view.CreateRect(); // Blue (Outside)
rc45.SetLeft(rc44, AnchorParam.Right);
rc45.AnchorRightTopBottom(null, 0, 320, 10);

host.PerformLayout();

using var bmp = new GcBitmap((int)(view.Width + 40), (int)(view.Height + 40), true);
using var g = bmp.CreateGraphics(Color.White);
var m = Matrix3x2.CreateTranslation(20, 20);
g.Transform = m;

DrawContour(contour);

DrawRect(rc11, Color.CornflowerBlue);
DrawRect(rc12, Color.CornflowerBlue);

DrawRect(rc21, Color.CornflowerBlue);
DrawRect(rc22, Color.Violet);
DrawRect(rc23, Color.Orange);
DrawRect(rc24, Color.Violet);
DrawRect(rc25, Color.CornflowerBlue);

DrawRect(rc31, Color.CornflowerBlue);
DrawRect(rc32, Color.Violet);
DrawRect(rc33, Color.CornflowerBlue);

DrawRect(rc41, Color.CornflowerBlue);
DrawRect(rc42, Color.Violet);
DrawRect(rc43, Color.CornflowerBlue);
DrawRect(rc44, Color.Violet);
DrawRect(rc45, Color.CornflowerBlue);

void DrawContour(Contour co)
{
    var pts = co.Points.Select(ap => ap.TransformedLocation).ToArray();
    g.DrawPolygon(pts, new Pen(Color.Green, 2));
}

void DrawRect(LayoutRect r, Color c)
{
    g.Transform = r.Transform.Multiply(m);
    g.DrawRectangle(r.AsRectF(), new Pen(c, 2));
}

bmp.SaveAsPng("img3.png");

image