ImageSharp.Drawing icon indicating copy to clipboard operation
ImageSharp.Drawing copied to clipboard

TextBuilder.GenerateGlyphs places the characters incorrectly with the specific word 'all'

Open AndrewShepherd opened this issue 2 years ago • 2 comments

Prerequisites

  • [ ] I have written a descriptive issue title
  • [ ] I have verified that I am running the latest version of Fonts
  • [ ] I have verified if the problem exist in both DEBUG and RELEASE mode
  • [ ] I have searched open and closed issues to ensure it has not already been reported

Description

On one particular Font, for one particular word, the text renderer is placing the letters incorrectly. The word is "all". It is placing the second 'l' to the left of the 'a'.

Steps to Reproduce

Here is my test application that demonstrates this. I have attached the font file I am using and also the resulting image.

using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;


var imgAll = new Image<Rgba32>(720, 720);
imgAll.Mutate(
	imageProcessingContext =>
	{
		imageProcessingContext.Fill(Color.White);
		var fontCollection = new FontCollection();
		FontFamily fontFamily = fontCollection.Add("./NettoOffc.ttf");
		Font font = new Font(fontFamily, 300, FontStyle.Regular);
		var textOptions = new TextOptions(font)
		{
			HorizontalAlignment = HorizontalAlignment.Left,
			VerticalAlignment = VerticalAlignment.Top,
		};
		var text = "all";
		var bounds = TextMeasurer.MeasureBounds(text, textOptions);
		var pathLine = new[] {
			new PointF(200, 224),
			new PointF(200 + bounds.Width, 224)
		};
		var path = new PathBuilder().AddLine(
			pathLine[0],
			pathLine[1]
		).Build();
		imageProcessingContext.DrawLines(new Pen(Color.Red, 30), pathLine);
		imageProcessingContext.Fill(
			new DrawingOptions { GraphicsOptions = { Antialias = true } },
			Brushes.Solid(Color.Black),
			TextBuilder.GenerateGlyphs(text, path, textOptions)
		);
	}
);
using FileStream? fileStream = File.OpenWrite("output.png");
imgAll.SaveAsPng(fileStream);
fileStream.Close();

Some other observations:

  • If I set the horizontal alignment to 'center' the problem goes away. It's only if it is left or right.
  • If I add an extra 100 pixels to the right part of the path then the problem goes away. But, as the red line indicates, there is enough space for the second 'l'

System Configuration

This is a .NET 6.0 application running on windows. Here are the packages I am including.

<ItemGroup>
   <PackageReference Include="SixLabors.ImageSharp" Version="2.1.2" />
   <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14.16" />
   <PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta17.8" />
</ItemGroup>

output

Other attachments include:

  • The Font File (NettoOffc.zip)
  • The entire project that demonstrates this (RenderWords.zip)

NettoOffc.zip RenderWords.zip

AndrewShepherd avatar Jun 29 '22 06:06 AndrewShepherd

Oooh this is an interesting one! At first I thought it must be an incorrect GPOS update but the alignment behavior is puzzling.

JimBobSquarePants avatar Jun 29 '22 08:06 JimBobSquarePants

Can you please try Fonts 1.0.0-beta17.13. I'm not seeing this.

all

Hold on.... I'm still seeing it in your demo app. It's not the Fonts library, it's the TextBuilder when a path is provided. I'll transfer the issue.

As a workaround you can set the TextOptions .Origin property to match your path origin.

output

JimBobSquarePants avatar Jun 29 '22 09:06 JimBobSquarePants

I've finally had the chance to have a look at this properly.

The issue is caused by our code which calculates the position of a point along the given path at a given distance. The code works well for closed paths since it reduces the distance to the remainder (distance %= path.Length) which makes a nice loop.

For open paths we don't want to do that, we want to create a new virtual point at the correct angle and distance calculated from the last two points in the path.

The below image demonstrates the product of two approaches drawing the text overlapped following my local updates.

  • Black = DrawText with origin set at the path origin.
  • Pink diagonal = TextBuilder + FillPath

As you can see the match is exact.

image

I'll open a PR once #261 is merged.

JimBobSquarePants avatar Mar 21 '23 01:03 JimBobSquarePants