Replies: 1 comment
-
I received a helpful answer on Stack Overflow after placing a bounty on my question. For reference purposes, I would like to share it here. public void Draw(string text)
{
var imageSize = 300;
var fontSize = 18;
var border = 10;
var color = Color.ParseHex("#808080");
FontFamily family = SystemFonts.Get("Arial");
Font font = new(family, fontSize);
var radius = imageSize / 2;
var center = new PointF(radius, radius);
var img = Image<Rgba32>.Load(@"C:\tmp\comet.png");
img.Mutate(o => o.Resize(new Size(imageSize, imageSize)));
var topSegment = new ArcLineSegment(center, new SizeF(radius - border, radius - border), 0, -220, 260);
PathBuilder pathBuilder = new PathBuilder();
pathBuilder.AddSegment(topSegment);
var bottomSegment = new ArcLineSegment(center, new SizeF(radius - fontSize / 2 - border, radius - fontSize / 2 - border), 0, -220, -100);
// Find the smallest curve encompassing the whole text
var textSweepAngle = FindLowestSweepAngle(text, font, new SizeF(radius - fontSize / 2 - border, radius - fontSize / 2 - border));
var fullSweepAngle = 100;
var gapSweepAngle = (fullSweepAngle - textSweepAngle) / 2;
var textStartAngle = -220 - gapSweepAngle;
var textSegment = new ArcLineSegment(center, new SizeF(radius - fontSize / 2 - border, radius - fontSize / 2 - border), 0, textStartAngle, -textSweepAngle);
IPath textShape = new Polygon(textSegment);
TextOptions textOptions = new(font)
{
WrappingLength = textShape.ComputeLength(),
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
TextAlignment = TextAlignment.Start,
TextDirection = TextDirection.LeftToRight
};
DrawingOptions options = new()
{
GraphicsOptions = new()
{
ColorBlendingMode = PixelColorBlendingMode.Multiply
}
};
IPen pen = Pens.Solid(color, 1);
img.Mutate(x => x.Draw(color, 2, pathBuilder.Build()));
img.Mutate(x => x.Draw(color, 2, new PathBuilder().AddSegment(bottomSegment).Build()));
IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, textShape, textOptions);
img.Mutate(i => i.Fill(color, glyphs));
string fullPath = IOPath.GetFullPath(IOPath.Combine("Output", IOPath.Combine("test.png")));
IODirectory.CreateDirectory(IOPath.GetDirectoryName(fullPath));
img.Save(fullPath);
Console.WriteLine($"Saved to {fullPath}");
}
private float FindLowestSweepAngle(string text, Font font, SizeF radius)
{
var low = 0.1f;
var high = 359.9f;
var step = 0.1f;
while (low < high)
{
var mid = (low + high) / 2;
var arcLineSegment = new ArcLineSegment(PointF.Empty, radius, 0, 0, mid);
var polygon = new Polygon(arcLineSegment);
TextOptions textOptions = new(font)
{
WrappingLength = polygon.ComputeLength(),
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
TextAlignment = TextAlignment.Start,
TextDirection = TextDirection.LeftToRight
};
try
{
TextBuilder.GenerateGlyphs(text, polygon, textOptions);
high = mid - step; // Curve is too big. Keep finding a smaller one
}
catch // InvalidOperationException: Should always reach a point along the path
{
low = mid + step; // Curve is too small to hold the whole text
}
}
return low;
} |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
I'm trying to replicate this: (drawing circle and text in the lower arc)
With this code:
I get to this result:
My questions are:
GenerateGlyphs
will fail with'Should always reach a point along the path.'
. Is there a way to scale the text size to fit the arc automatically if the text is too long?EDIT: I experimented with different segments, I have the same issue with a simpler line segment:
Similarly
TextAligment
has no effect and havingHorizontalAligment.Center
will cause the text to be aligned in the middle of the segment start point:What is also strange is that the
textShape.ComputeLength()
returns 400 in the above example, whereas the segment is only 200 long.Thanks a lot
Beta Was this translation helpful? Give feedback.
All reactions