Skip to content

Commit 91541c8

Browse files
"Fix" horizontal layout.
1 parent b054d68 commit 91541c8

3 files changed

Lines changed: 55 additions & 12 deletions

File tree

samples/DrawShapesWithImageSharp/Program.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,7 @@ private static void OutputStars()
5454
DrawText("Hello World");
5555
DrawText(
5656
"Hello World Hello World Hello World Hello World Hello World Hello World Hello World",
57-
new Path(new CubicBezierLineSegment(
58-
new Vector2(0, 0),
59-
new Vector2(150, -150),
60-
new Vector2(250, -150),
61-
new Vector2(400, 0))));
57+
new EllipsePolygon(PointF.Empty, 100));
6258
}
6359

6460
private static void DrawText(string text)
@@ -79,8 +75,12 @@ private static void DrawText(string text, IPath path)
7975
{
8076
WrappingLength = path.ComputeLength(),
8177
VerticalAlignment = VerticalAlignment.Top,
82-
HorizontalAlignment = HorizontalAlignment.Center,
78+
HorizontalAlignment = HorizontalAlignment.Left,
79+
80+
// Enable this to test vertical layout mode.
81+
LayoutMode = LayoutMode.VerticalLeftRight
8382
};
83+
8484
IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, path, textOptions);
8585

8686
glyphs.SaveImage("Text-Path", text + ".png");
@@ -236,7 +236,7 @@ public static void SaveImage(this IPathCollection shape, params string[] path)
236236

237237
using (var img = new Image<Rgba32>(width, height))
238238
{
239-
img.Mutate(i => i.Fill(Color.DarkBlue));
239+
img.Mutate(i => i.Fill(Color.DarkBlue).Draw(Color.HotPink, 3, new EllipsePolygon(width / 2F, height / 2F, 93)));
240240

241241
foreach (IPath s in shape)
242242
{

src/ImageSharp.Drawing/Shapes/Text/PathGlyphBuilder.cs

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Numerics;
6+
using System.Runtime.CompilerServices;
67
using SixLabors.Fonts;
78

89
namespace SixLabors.ImageSharp.Drawing.Text
@@ -14,13 +15,16 @@ internal sealed class PathGlyphBuilder : GlyphBuilder
1415
{
1516
private const float Pi = MathF.PI;
1617
private readonly IPathInternals path;
18+
private readonly bool isVerticalLayout;
19+
private float xOffset;
1720
private float yOffset;
1821

1922
/// <summary>
2023
/// Initializes a new instance of the <see cref="PathGlyphBuilder"/> class.
2124
/// </summary>
2225
/// <param name="path">The path to render the glyphs along.</param>
23-
public PathGlyphBuilder(IPath path)
26+
/// <param name="layoutMode">The mode to determine the layout of the text.</param>
27+
public PathGlyphBuilder(IPath path, LayoutMode layoutMode)
2428
{
2529
if (path is IPathInternals internals)
2630
{
@@ -30,22 +34,61 @@ public PathGlyphBuilder(IPath path)
3034
{
3135
this.path = new ComplexPolygon(path);
3236
}
37+
38+
this.isVerticalLayout = IsVertical(layoutMode);
3339
}
3440

3541
/// <inheritdoc/>
36-
protected override void BeginText(FontRectangle rect) => this.yOffset = rect.Height;
42+
protected override void BeginText(FontRectangle rect)
43+
{
44+
this.yOffset = rect.Height;
45+
this.xOffset = rect.Left;
46+
}
3747

3848
/// <inheritdoc/>
3949
protected override void BeginGlyph(FontRectangle rect)
4050
{
41-
SegmentInfo point = this.path.PointAlongPath(rect.Left);
51+
if (!this.isVerticalLayout)
52+
{
53+
this.TransformGlyphHorizontal(rect);
54+
}
55+
else
56+
{
57+
this.TransformGlyphVertical(rect);
58+
}
59+
}
4260

43-
Vector2 targetPoint = point.Point + new PointF(0, rect.Top - this.yOffset);
61+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
62+
private void TransformGlyphHorizontal(FontRectangle rect)
63+
{
64+
float halfWidth = rect.Width * .5F;
65+
SegmentInfo point = this.path.PointAlongPath(rect.Left + halfWidth - this.xOffset);
66+
Vector2 targetPoint = point.Point + new PointF(-halfWidth, rect.Top - this.yOffset);
4467

4568
// Due to how matrix combining works you have to combine this in the reverse order of operation
4669
// this one rotates the glype then moves it.
4770
Matrix3x2 matrix = Matrix3x2.CreateTranslation(targetPoint - rect.Location) * Matrix3x2.CreateRotation(point.Angle - Pi, point.Point);
4871
this.builder.SetTransform(matrix);
4972
}
73+
74+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
75+
private void TransformGlyphVertical(FontRectangle rect)
76+
{
77+
// TODO: Fix this. Vertical text should be rotated at the centre along a different axis.
78+
// https://svgwg.org/svg2-draft/text.html#TextpathLayoutRules
79+
float halfWidth = rect.Width * .5F;
80+
SegmentInfo point = this.path.PointAlongPath(rect.Left + halfWidth - this.xOffset);
81+
Vector2 targetPoint = point.Point + new PointF(-halfWidth, rect.Top - this.yOffset);
82+
83+
Matrix3x2 matrix = Matrix3x2.CreateTranslation(targetPoint - rect.Location) * Matrix3x2.CreateRotation(point.Angle - Pi, point.Point);
84+
this.builder.SetTransform(matrix);
85+
}
86+
87+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
88+
private static bool IsVertical(LayoutMode mode)
89+
{
90+
const LayoutMode vertical = LayoutMode.VerticalLeftRight | LayoutMode.VerticalRightLeft;
91+
return (mode & vertical) > 0;
92+
}
5093
}
5194
}

src/ImageSharp.Drawing/Shapes/Text/TextBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public static IPathCollection GenerateGlyphs(string text, TextOptions textOption
3636
/// <returns>The <see cref="IPathCollection"/></returns>
3737
public static IPathCollection GenerateGlyphs(string text, IPath path, TextOptions textOptions)
3838
{
39-
PathGlyphBuilder glyphBuilder = new(path);
39+
PathGlyphBuilder glyphBuilder = new(path, textOptions.LayoutMode);
4040
TextRenderer renderer = new(glyphBuilder);
4141

4242
renderer.RenderText(text, textOptions);

0 commit comments

Comments
 (0)