Incorrect interpretation of USE_MY_METRICS
Closed this issue · 5 comments
Lines 1885 to 1890 in 4e2ba14
It is my understanding that the USE_MY_METRICS
flag has nothing to do with the path transformation. As I read it, if a child glyph has the USE_MY_METRICS
flag set it means that the parent glyph should use the metrics of the flagged child glyph. The metrics being the left side-bearing, right side-bearing, and advance width.
The Apple docs aren't very clear on this. The Microsoft docs are a bit better - but not much.
Hopefully fixed now.
Sorry to keep going about this but I can't see that you're doing anything at all with the hmtx struct.
Its journey starts in GetGlyphMetricsInternal
where you get the metrics for the "root" glyph...
Lines 2001 to 2005 in 20a2ec6
...and pass it on to GetGlyphPaths
:
Line 2009 in 20a2ec6
In GetGlyphPaths
you pass it on to GetCompositeGlyph
:
Line 1592 in 20a2ec6
And GetCompositeGlyph
copies it to a local temporary and passes that recursively on to GetGlyphPaths
, etc., etc.
Lines 1880 to 1881 in 20a2ec6
In GetCompositeGlyph
, once GetGlyphPaths
returns, you examine the composite flag and if USE_MY_METRICS
is set you copy it back from the local:
Lines 1887 to 1888 in 20a2ec6
Now, in none of the above is the metrics actually updated. The structure remains unmodified from start to end.
The problem is that you're only getting the metrics of the root GlyphID. What you need to do is get the metrics of each component (potentially) all the way down the tree.
For example, move the call to GetGlyphHorzMetrics
from GetGlyphMetricsInternal
into GetGlyphPaths
so you're getting the metrics for the glyph component and have GetCompositeGlyph
update the caller's metrics if the USE_MY_METRICS
flag is set. You're almost doing it and it's as if this was your meaning all along.
function TFontReader.GetCompositeGlyph(var tbl_glyf: TFontTable_Glyf;
var tbl_hmtx: TFontTable_Hmtx): TPathsEx;
...
component_tbl_hmtx := Default(TFontTable_Hmtx);
componentPaths := GetGlyphPaths(glyphIndex, component_tbl_hmtx, component_tbl_glyf);
...
if (flag and USE_MY_METRICS <> 0) then
tbl_hmtx := component_tbl_hmtx; //(#24)
Btw, I believe the vmtx values should be copied as well. Sorry :-)
If you're planning on implementing point-to-point positioning (ARGS_ARE_XY_VALUES
flag not set) at some time it might be easier to just pass the hmtx and vmtx values as "phantom-points" since you will need those for that anyway.
Thanks again Anders for your valuable feedback.
It's been quite a while since I've closely looked at this stuff so I'm rather rusty about this now. I might look at this more closely soonish, but at the moment I'm occupied with other things, including improving the vectorization and color quantization code here.
Regarding implementing point-to-point positioning and hmtx and vmtx: if you can provide code that implements these, or links to where these are implemented elsewhere, I would of course fix this more quickly 😁🤞.
The problem is that you're only getting the metrics of the root GlyphID. What you need to do is get the metrics of each component (potentially) all the way down the tree.
I ended up taking a different approach which I like much better: Instead of passing the metric structure around, I simply implemented a GetGlyphMetric method that internally traverses the glyph tree to find the GlyphID which will supply the metric. Since it doesn't need to traverse the whole tree it's actually pretty efficient. It goes something like this:
function TRedacted.GetGlyphMetric(GlyphIndex: Word): TTrueTypeGlyphMetric;
var
GlyphDataTable: TTrueTypeFontGlyphDataTable;
procedure DoGetGlyphMetric(GlyphIndex: Word; var MetricIndex: Word);
var
Glyph: TCustomTrueTypeFontGlyphData;
i: integer;
CompositeGlyphData: TTrueTypeFontCompositeGlyphData;
ComponentGlyphMetric: TTrueTypeGlyphMetric;
begin
Glyph := GlyphDataTable.GlyphData[GlyphIndex];
// If glyph is a simple glyph then we will just use its index as the metric index.
if (Glyph is TTrueTypeFontSimpleGlyphData) then
// The default MetricIndex value has already been set by the caller.
exit;
// If glyph is a composite glyph, then we will either use its index or one of
// its components index as the metric index.
if Glyph is TTrueTypeFontCompositeGlyphData then
begin
// The default MetricIndex value has already been set by the caller.
CompositeGlyphData := TTrueTypeFontCompositeGlyphData(Glyph);
// Recursively process composite glyph components
for i := 0 to CompositeGlyphData.GlyphCount-1 do
if (CompositeGlyphData.Glyph[i].Flags and TRedactedCompositeGlyph.GLYF_USE_MY_METRICS <> 0) then
begin
// We will use the index of the component. Set MetricIndex and recurse.
MetricIndex := CompositeGlyphData.Glyph[i].GlyphIndex;
DoGetGlyphMetric(CompositeGlyphData.Glyph[i].GlyphIndex, MetricIndex);
// In theory multiple components could set USE_MY_METRICS but we
// ignore that as it doesn't make sense.
exit;
end;
end;
end;
var
MetricIndex: Word;
VerticalMetricsTable: TRedactedVerticalMetricsTable;
begin
Result := Default(TTrueTypeGlyphMetric);
GlyphDataTable := TTrueTypeFontGlyphDataTable(GetTableByTableName('glyf'));
if (GlyphDataTable = nil) then
exit;
MetricIndex := GlyphIndex;
DoGetGlyphMetric(GlyphIndex, MetricIndex);
Result.HorizontalMetric := FHorizontalMetrics.HorizontalMetric[MetricIndex];
VerticalMetricsTable := TRedactedVerticalMetricsTable(GetTableByTableType(TRedactedVerticalMetricsTable.GetTableType));
if (VerticalMetricsTable <> nil) then
Result.VerticalMetric := VerticalMetricsTable.VerticalMetric[MetricIndex];
end;
Regarding implementing point-to-point positioning and hmtx and vmtx
Point-to-point position is pretty easy and the documentation is clear on how it's done. Unfortunately, a key part of that feature is "phantom points" and the details of those seem to be completely undocumented. I know what they represent (the four "corners" of the glyph), but apart from that it's pretty much guesswork.
I have also been unable to find any glyphs that actually use this feature so I'm concentrating on more important stuff for now.