On May 15, 2013, at 4:48 PM,
rrob...@adobe.com wrote:
> The following is my first pass at supporting Type1 hints in a GLIF file. Let me know what you think.
I read over the Type 1 and Type 2 spec hinting sections so that I could better understand what you are proposing. I think I get it now, though I'm not 100% clear on what is needed to make a hint mask. (More on that below.)
Overall this seems good. The structure needs to be adjusted so that it can be stored in a property list. I sketched that out quickly (this includes some of the feedback from below):
com.adobe.type.autohint : {
formatVersion : 1,
fingerprint : "string",
stems : [
{
type : "hstem | vstem",
position : number,
width : number (-20 and -21 are reserved values as defined in the spec)
},
...
],
hintmasks : [
{
stems : [
{
stem : number,
point : number
}
]
}
]
}
> There may or may not be a <hintSetList> element. If this element is not present, then all the stem hints are applied over the entire glyph. If the glyph uses hint substitution, there must be a <hintSetList> element, containing at least one <hintset> elements A <hintset> element specifies the set of hints that is applied starting with the specified point index. There is no requirement that the <hintset> elements be in any particular order.
Is a hintset the same thing as a hintmask? If so, I'd recommend the name hintmask since that is the latest name in the spec. We try to stick to the spec terminology for clarity as much as we can.
> Note that the <hintset> element references stems definitions by child element index within the <stemList> element, and references points by point index within the list of points for the entire glyph. This means that changing point order or contour order will invalidate the hint data. The <hintset> point index is determined by building a list of all on-curve points in the order they are encountered when parsing the GLIF file, after expanding components to outlines.
Is the point index needed for the charstring? I'm trying to understand how this data can go to and come from the charstrings.
> The <stemhints> "id" property is a hash of the glyph point coordinates and types. It used to determine if a <stemhints> element is valid. A consumer of the <stemhints> element should build a hash of the glyph point coordinates and types; if this differs from the value of the "id" property, the <stemhints> element should be ignored. The hash is built by first expanding all components to contours, and then building a list of all the points in the glyph. A source string is built by concatenating the point x value, y value, and first letter of the type value to the string, without whitespace, for each point in turn. If the final string is less than 128 characters, that is used as the hash value. If the length is 128 characters or greater, then a hash value is calculated using a SHA512 algorithm, producing a 512 bit hash, written as a 128 character hexadecimal string.
I would call it "fingerprint" instead of "id". We use "identifier" elsewhere in the spec for a completely different thing.
Just to make sure I'm understanding this correctly, would a lineto at (100, 200) be expressed as "100,200l"? What should the precision be for floats?
I have been thinking about this and the fingerprint makes things fragile when changing left-margin values since changing that will move points. You could make the points relative to the minimum x and y values of all points (including off curves). You would need to do the same for the positions in the stem definitions as well. This wouldn't make the fingerprint robust against anything other than moving points, so it may not be all that useful.
> <glyph>
> ...
> <lib>
> <dict>
> <key><com.adobe.type.autohint><key>
If the data is specific to the autohinter, com.adobe.type.autohint is the best key. However, if hints defined in FontLab or elsewhere will be stored here, I'd suggest com.adobe.type.postscripthints or something more generic like that.
I would suggest adding a formatVersion so that if you change the structure of the data later you can quickly discern what you are dealing with when reading data.
I hav been thinking about how this could be stored officially in GLIF. My main concern is that changing the contour data in any way invalidates the hinting data. For example, if I change the left margin then the hinting data must be discarded. That's fine if the data was produced by an algorithm that can be rerun. If the data was produced manually, it's not so nice. We try to not have data that behaves that way. I did some sketching and came up with this:
<postscripthints>
<stems>
<hstem point1="point identifier" point2="point identifier" identifier="unique identifier (optional)" />
<vstem point1="point identifier" point2="point identifier" identifier="unique identifier (optional)" />
<hedge point="point identifier" direction="up | down" identifier="unique identifier (optional)" />
<vedge point="point identifier" direction="left | right" identifier="unique identifier (optional)" />
</stems>
<hintmasks>
<hintmask>
<stem>stem identifier</stem>
</hintmask>
</hintmasks>
</postscripthints>
Rather than using static coordinates in glyph units, the stems are linked to specific points that can in turn be used to calculate the values needed to create the charstring. The points are referenced by identifiers rather than index. Identifiers are sticky so they should survive moderate outline operations (i.e. metrics changes, contour reordering, direction changes) and may survive extensive outline operations (i.e. overlap removal, contour editing). I added new hedge and vedge values since there is no width value to indicate that a stem is an edge. The hint mask is based on my somewhat shaky understanding of the hintmask spec.
I translated the B from Source Code Pro-Regular into this syntax as a test case. The result is below.
There are some operators in the Type 2 spec that we haven't discussed. Specifically, cntrmask and flex. Should those be supported? Any thoughts from anyone on this would be appreciated.
Thanks,
Tal
<?xml version="1.0" encoding="UTF-8"?>
<glyph name="B" format="3">
<advance width="600"/>
<unicode hex="0042"/>
<outline>
<contour>
<point x="103" y="0" type="line" identifier="HbggNB8jlW"/>
<point x="298" y="0" type="line" smooth="yes"/>
<point x="444" y="0"/>
<point x="545" y="63"/>
<point x="545" y="192" type="curve" smooth="yes" identifier="8pFbrLXQhr"/>
<point x="545" y="282"/>
<point x="489" y="334"/>
<point x="393" y="349" type="curve"/>
<point x="393" y="353" type="line"/>
<point x="470" y="373"/>
<point x="508" y="431"/>
<point x="508" y="496" type="curve" smooth="yes" identifier="LRPWCKY4qj"/>
<point x="508" y="611"/>
<point x="417" y="656"/>
<point x="283" y="656" type="curve" smooth="yes"/>
<point x="103" y="656" type="line" identifier="cxnHEdxNQa"/>
</contour>
<contour>
<point x="187" y="376" type="line" identifier="Uq2xdW6RZc"/>
<point x="187" y="590" type="line" identifier="0jz1HOl0UG"/>
<point x="273" y="590" type="line" smooth="yes"/>
<point x="374" y="590"/>
<point x="426" y="560"/>
<point x="426" y="489" type="curve" smooth="yes" identifier="IagrAeCRhl"/>
<point x="426" y="416"/>
<point x="381" y="376"/>
<point x="269" y="376" type="curve" smooth="yes"/>
</contour>
<contour>
<point x="187" y="66" type="line" identifier="JEYqZEsWDV"/>
<point x="187" y="314" type="line" identifier="F4OYZGPovQ"/>
<point x="286" y="314" type="line" smooth="yes"/>
<point x="401" y="314"/>
<point x="463" y="277"/>
<point x="463" y="196" type="curve" smooth="yes" identifier="YfHt4H8LqQ"/>
<point x="463" y="107"/>
<point x="398" y="66"/>
<point x="286" y="66" type="curve" smooth="yes"/>
</contour>
</outline>
<postscripthints>
<stems>
<vstem point1="HbggNB8jlW" point2="JEYqZEsWDV" identifier="FNP9rQzr9D" />
<vstem point1="IagrAeCRhl" point2="LRPWCKY4qj" identifier="HVG8gkDTbx" />
<vstem point1="YfHt4H8LqQ" point2="8pFbrLXQhr" identifier="lj06rIo05z" />
<hstem point1="HbggNB8jlW" point2="JEYqZEsWDV" identifier="tR3lzQDuFw" />
<hstem point1="F4OYZGPovQ" point2="Uq2xdW6RZc" identifier="tCPsNPT5Il" />
<hstem point1="0jz1HOl0UG" point2="cxnHEdxNQa" identifier="97yeZPAcer" />
</stems>
<hintmasks>
<hintmask>
<stem>FNP9rQzr9D</stem>
<stem>lj06rIo05z</stem>
<stem>tR3lzQDuFw</stem>
<stem>tCPsNPT5Il</stem>
<stem>97yeZPAcer</stem>
</hintmask>
<hintmask>
<stem>FNP9rQzr9D</stem>
<stem>HVG8gkDTbx</stem>
<stem>tR3lzQDuFw</stem>
<stem>tCPsNPT5Il</stem>
<stem>97yeZPAcer</stem>
</hintmask>
</hintmasks>
</postscripthints>
</glyph>