Decompose outline fails for postscript font after reversal
Hi, I'm running into an issue where FT_Outline_Decompose fails after FT_Outline_Reverse is run first. SF Mono is an example font where I can reproduce this issue for some glyphs.
For context, the reason I would like to call FT_Outline_Reverse on postscript glyph outlines to try to guarantee that all glyph paths I'm loading have a uniform orientation. This allows for additional paths to be appended to these outlines (for example, a rectangle path for a strikethrough) without running into winding number bugs based on the glyph orientation. I can give more context on the motivation here if helpful.
If I call FT_Outline_Reverse and then FT_Outline_Decompose on SF Mono, I hit this condition https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/src/base/ftoutln.c#L106 and the decompose fails because the path starts with a cubic control point (since the original path ended with a cubic control point, and then I reversed it)
I can fix this issue locally by handling the case where the first point is cubic control but the last point is on the curve, similar to what is done for conic control. For example add the following logic to FT_Outline_Decompose
@@ -102,8 +102,16 @@
tag = FT_CURVE_TAG( tags[0] );
/* A contour cannot start with a cubic control point! */
- if ( tag == FT_CURVE_TAG_CUBIC )
- goto Invalid_Outline;
+ if ( tag == FT_CURVE_TAG_CUBIC ) {
+ if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
+ {
+ /* start at last point if it is on the curve */
+ v_start = v_last;
+ limit--;
+ point--;
+ tags--;
+ } else {
+ goto Invalid_Outline;
+ }
+ }
/* check first point to determine origin */
if ( tag == FT_CURVE_TAG_CONIC )
I also noticed that in the case that a contour ends in a cubic control point with no destination point on the contour, then FT_Outline_Decompose assumes that the destination is the starting point of the contour. So alternatively in FT_Outline_Reverse, don't swap the first point if it would lead to this issue, since we assume that the final point is the same as the starting point anyway:
@@ -544,6 +544,12 @@
{
last = outline->contours[n];
+ if (FT_CURVE_TAG(outline->tags[last]) == FT_CURVE_TAG_CUBIC) {
+ // Don't swap the first point, since it's getting swapped with the imaginary duplicate last
+ // point
+ first++;
+ }
+
/* reverse point table */
{
FT_Vector* p = outline->points + first;
I'm looking for any guidance on whether either of these are safe for the specific case where I call FT_Outline_Decompose immediately after FT_Outline_Reverse, and also whether this is a bug worth fixing more generally or if I'm making any incorrect assumptions. Thanks for your help!