33
44using System ;
55using System . Numerics ;
6+ using System . Runtime . CompilerServices ;
67using SixLabors . Fonts ;
78
89namespace 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}
0 commit comments