Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update vhea and share logic with hhea #233

Merged
merged 7 commits into from
May 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions Lib/ufo2ft/fontInfoData.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,9 +325,12 @@ def postscriptBlueScaleFallback(info):
openTypeVheaVertTypoAscender=None,
openTypeVheaVertTypoDescender=None,
openTypeVheaVertTypoLineGap=None,
openTypeVheaCaretSlopeRise=None,
openTypeVheaCaretSlopeRun=None,
openTypeVheaCaretOffset=None,
# fallback to horizontal caret:
# a value of 0 for the rise
# and a value of 1 for the run.
openTypeVheaCaretSlopeRise=0,
openTypeVheaCaretSlopeRun=1,
openTypeVheaCaretOffset=0,

postscriptUniqueID=None,
postscriptWeightName=None,
Expand Down
199 changes: 110 additions & 89 deletions Lib/ufo2ft/outlineCompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ def _isNonBMP(s):
return False


def _getVerticalOrigin(glyph):
height = glyph.height
def _getVerticalOrigin(font, glyph):
font_ascender = font['hhea'].ascent
if (hasattr(glyph, "verticalOrigin") and
glyph.verticalOrigin is not None):
verticalOrigin = glyph.verticalOrigin
else:
verticalOrigin = height
verticalOrigin = font_ascender
return round(verticalOrigin)


Expand Down Expand Up @@ -79,12 +79,19 @@ def compile(self):
"""
self.otf = TTFont(sfntVersion=self.sfntVersion)

self.vertical = False
for glyph in self.allGlyphs.values():
if (hasattr(glyph, "verticalOrigin") and
glyph.verticalOrigin is not None):
self.vertical = True
break
# only compile vertical metrics tables if vhea metrics a defined
vertical_metrics = [
"openTypeVheaVertTypoAscender",
"openTypeVheaVertTypoDescender",
"openTypeVheaVertTypoLineGap",
"openTypeVheaCaretSlopeRise",
"openTypeVheaCaretSlopeRun",
"openTypeVheaCaretOffset",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@moyogo that all vhea metrics be defined in fontinfo.plist seems a bit too much. Maybe only check if VertTypo* are present and leave the VheaCaret* stuff as optional?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

]
self.vertical = all(
getAttrWithFallback(self.ufo.info, metric) is not None
for metric in vertical_metrics
)

# populate basic tables
self.setupTable_head()
Expand Down Expand Up @@ -589,55 +596,105 @@ def setupTable_hmtx(self):
left = bounds.xMin if bounds else 0
hmtx[glyphName] = (width, left)

def setupTable_hhea(self):
def _setupTable_hhea_or_vhea(self, tag):
"""
Make the hhea table. This assumes that the hmtx table was made first.

**This should not be called externally.** Subclasses
may override or supplement this method to handle the
table creation in a different way if desired.
Make the hhea table or the vhea table. This assume the hmtx or
the vmtx were respectively made first.
"""
self.otf["hhea"] = hhea = newTable("hhea")
hmtx = self.otf["hmtx"]
if tag == "hhea":
isHhea = True
else:
isHhea = False
self.otf[tag] = table = newTable(tag)
mtxTable = self.otf[tag[0] + "mtx"]
font = self.ufo
hhea.tableVersion = 0x00010000
# vertical metrics
hhea.ascent = round(getAttrWithFallback(font.info, "openTypeHheaAscender"))
hhea.descent = round(getAttrWithFallback(font.info, "openTypeHheaDescender"))
hhea.lineGap = round(getAttrWithFallback(font.info, "openTypeHheaLineGap"))
# horizontal metrics
widths = []
lefts = []
rights = []
if isHhea:
table.tableVersion = 0x00010000
else:
table.tableVersion = 0x00011000
# Vertical metrics in hhea, horizontal metrics in vhea
# and caret info.
# The hhea metrics names are formed as:
# "openType" + tag.title() + "Ascender", etc.
# While vhea metrics names are formed as:
# "openType" + tag.title() + "VertTypo" + "Ascender", etc.
# Caret info names only differ by tag.title().
commonPrefix = "openType%s" % tag.title()
if isHhea:
metricsPrefix = commonPrefix
else:
metricsPrefix = "openType%sVertTypo" % tag.title()
metricsDict = {
"ascent": "%sAscender" % metricsPrefix,
"descent": "%sDescender" % metricsPrefix,
"lineGap": "%sLineGap" % metricsPrefix,
"caretSlopeRise": "%sCaretSlopeRise" % commonPrefix,
"caretSlopeRun": "%sCaretSlopeRun" % commonPrefix,
"caretOffset": "%sCaretOffset" % commonPrefix,
}
for otfName, ufoName in metricsDict.items():
setattr(table, otfName,
getAttrWithFallback(font.info, ufoName))
# Horizontal metrics in hhea, vertical metrics in vhea
advances = [] # width in hhea, height in vhea
firstSideBearings = [] # left in hhea, top in vhea
secondSideBearings = [] # right in hhea, bottom in vhea
extents = []
for glyphName in self.allGlyphs:
width, left = hmtx[glyphName]
widths.append(width)
advance, firstSideBearing = mtxTable[glyphName]
advances.append(advance)
bounds = self.glyphBoundingBoxes[glyphName]
if bounds is None:
continue
right = width - left - (bounds.xMax - bounds.xMin)
lefts.append(left)
rights.append(right)
# equation from the hhea spec for calculating xMaxExtent:
# Max(lsb + (xMax - xMin))
extent = left + (bounds.xMax - bounds.xMin)
if isHhea:
boundsAdvance = (bounds.xMax - bounds.xMin)
# equation from the hhea spec for calculating xMaxExtent:
# Max(lsb + (xMax - xMin))
extent = firstSideBearing + boundsAdvance
else:
boundsAdvance = (bounds.yMax - bounds.yMin)
# equation from the vhea spec for calculating yMaxExtent:
# Max(tsb + (yMax - yMin)).
extent = firstSideBearing + boundsAdvance
secondSideBearing = advance - firstSideBearing - boundsAdvance

firstSideBearings.append(firstSideBearing)
secondSideBearings.append(secondSideBearing)
extents.append(extent)
hhea.advanceWidthMax = max(widths)
hhea.minLeftSideBearing = min(lefts) if lefts else 0
hhea.minRightSideBearing = min(rights) if rights else 0
hhea.xMaxExtent = max(extents) if extents else 0
# misc
hhea.caretSlopeRise = getAttrWithFallback(font.info, "openTypeHheaCaretSlopeRise")
hhea.caretSlopeRun = getAttrWithFallback(font.info, "openTypeHheaCaretSlopeRun")
hhea.caretOffset = round(getAttrWithFallback(font.info, "openTypeHheaCaretOffset"))
hhea.reserved0 = 0
hhea.reserved1 = 0
hhea.reserved2 = 0
hhea.reserved3 = 0
hhea.metricDataFormat = 0
setattr(table,
"advance%sMax" % ("Width" if isHhea else "Height"),
max(advances) if advances else 0)
setattr(table,
"min%sSideBearing" % ("Left" if isHhea else "Top"),
min(firstSideBearings) if firstSideBearings else 0)
setattr(table,
"min%sSideBearing" % ("Right" if isHhea else "Bottom"),
min(secondSideBearings) if secondSideBearings else 0)
setattr(table,
"%sMaxExtent" % ("x" if isHhea else "y"),
max(extents) if extents else 0)
if isHhea:
reserved = range(4)
else:
# vhea.reserved0 is caretOffset for legacy reasons
reserved = range(1, 5)
for i in reserved:
setattr(table, "reserved%i" % i, 0)
table.metricDataFormat = 0
# glyph count
hhea.numberOfHMetrics = len(self.allGlyphs)
setattr(table,
"numberOf%sMetrics" % ("H" if isHhea else "V"),
len(self.allGlyphs))

def setupTable_hhea(self):
"""
Make the hhea table. This assumes that the hmtx table was made first.

**This should not be called externally.** Subclasses
may override or supplement this method to handle the
table creation in a different way if desired.
"""
self._setupTable_hhea_or_vhea("hhea")

def setupTable_vmtx(self):
"""
Expand All @@ -655,7 +712,7 @@ def setupTable_vmtx(self):
if height < 0:
raise ValueError(
"The height should not be negative: '%s'" % (glyphName))
verticalOrigin = _getVerticalOrigin(glyph)
verticalOrigin = _getVerticalOrigin(self.otf, glyph)
bounds = self.glyphBoundingBoxes[glyphName]
top = bounds.yMax if bounds else 0
vmtx[glyphName] = (height, verticalOrigin - top)
Expand All @@ -673,12 +730,13 @@ def setupTable_VORG(self):
vorg.minorVersion = 0
vorg.VOriginRecords = {}
# Find the most frequent verticalOrigin
vorg_count = Counter(_getVerticalOrigin(glyph)
vorg_count = Counter(_getVerticalOrigin(self.otf, glyph)
for glyph in self.allGlyphs.values())
vorg.defaultVertOriginY = vorg_count.most_common(1)[0][0]
if len(vorg_count) > 1:
for glyphName, glyph in self.allGlyphs.items():
vorg.VOriginRecords[glyphName] = _getVerticalOrigin(glyph)
vorg.VOriginRecords[glyphName] = _getVerticalOrigin(
self.otf, glyph)
vorg.numVertOriginYMetrics = len(vorg.VOriginRecords)

def setupTable_vhea(self):
Expand All @@ -690,44 +748,7 @@ def setupTable_vhea(self):
may override or supplement this method to handle the
table creation in a different way if desired.
"""
self.otf["vhea"] = vhea = newTable("vhea")
font = self.ufo
head = self.otf["head"]
vmtx = self.otf["vmtx"]
vhea.tableVersion = 0x00011000
# horizontal metrics
vhea.ascent = round(getAttrWithFallback(font.info, "openTypeVheaVertTypoAscender"))
vhea.descent = round(getAttrWithFallback(font.info, "openTypeVheaVertTypoDescender"))
vhea.lineGap = round(getAttrWithFallback(font.info, "openTypeVheaVertTypoLineGap"))
# vertical metrics
heights = []
tops = []
bottoms = []
for glyphName in self.allGlyphs:
height, top = vmtx[glyphName]
heights.append(height)
bounds = self.glyphBoundingBoxes[glyphName]
if bounds is None:
continue
bottom = height - top - (bounds.yMax - bounds.yMin)
tops.append(top)
bottoms.append(bottom)
vhea.advanceHeightMax = max(heights)
vhea.minTopSideBearing = max(tops)
vhea.minBottomSideBearing = max(bottoms)
vhea.yMaxExtent = vhea.minTopSideBearing - (head.yMax - head.yMin)
# misc
vhea.caretSlopeRise = getAttrWithFallback(font.info, "openTypeVheaCaretSlopeRise")
vhea.caretSlopeRun = getAttrWithFallback(font.info, "openTypeVheaCaretSlopeRun")
vhea.caretOffset = getAttrWithFallback(font.info, "openTypeVheaCaretOffset")
vhea.reserved0 = 0
vhea.reserved1 = 0
vhea.reserved2 = 0
vhea.reserved3 = 0
vhea.reserved4 = 0
vhea.metricDataFormat = 0
# glyph count
vhea.numberOfVMetrics = len(self.allGlyphs)
self._setupTable_hhea_or_vhea("vhea")

def setupTable_post(self):
"""
Expand Down
44 changes: 44 additions & 0 deletions tests/data/TestFont-CFF.ttx
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,13 @@
</LookupList>
</GPOS>

<VORG>
<majorVersion value="1"/>
<minorVersion value="0"/>
<defaultVertOriginY value="750"/>
<numVertOriginYMetrics value="0"/>
</VORG>

<hmtx>
<mtx name=".notdef" width="500" lsb="50"/>
<mtx name="uni0020" width="250" lsb="0"/>
Expand All @@ -459,4 +466,41 @@
<mtx name="uni006C" width="600" lsb="78"/>
</hmtx>

<vhea>
<tableVersion value="0x00011000"/>
<ascent value="750"/>
<descent value="-250"/>
<lineGap value="200"/>
<advanceHeightMax value="1000"/>
<minTopSideBearing value="0"/>
<minBottomSideBearing value="-80"/>
<yMaxExtent value="1030"/>
<caretSlopeRise value="0"/>
<caretSlopeRun value="1"/>
<caretOffset value="0"/>
<reserved1 value="0"/>
<reserved2 value="0"/>
<reserved3 value="0"/>
<reserved4 value="0"/>
<metricDataFormat value="0"/>
<numberOfVMetrics value="12"/>
</vhea>

<vmtx>
<mtx name=".notdef" height="1000" tsb="0"/>
<mtx name="uni0020" height="250" tsb="750"/>
<mtx name="uni0061" height="750" tsb="240"/>
<mtx name="uni0062" height="750" tsb="245"/>
<mtx name="uni0063" height="750" tsb="250"/>
<mtx name="uni0064" height="750" tsb="553"/>
<mtx name="uni0065" height="750" tsb="240"/>
<mtx name="uni0066" height="750" tsb="240"/>
<mtx name="uni0067" height="750" tsb="240"/>
<mtx name="uni0068" height="1000" tsb="93"/>
<mtx name="uni0069" height="750" tsb="240"/>
<mtx name="uni006A" height="1000" tsb="520"/>
<mtx name="uni006B" height="1000" tsb="240"/>
<mtx name="uni006C" height="1000" tsb="240"/>
</vmtx>

</ttFont>
44 changes: 44 additions & 0 deletions tests/data/TestFont-NoOverlaps-CFF.ttx
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,13 @@
</LookupList>
</GPOS>

<VORG>
<majorVersion value="1"/>
<minorVersion value="0"/>
<defaultVertOriginY value="750"/>
<numVertOriginYMetrics value="0"/>
</VORG>

<hmtx>
<mtx name=".notdef" width="500" lsb="50"/>
<mtx name="uni0020" width="250" lsb="0"/>
Expand All @@ -461,4 +468,41 @@
<mtx name="uni006C" width="600" lsb="78"/>
</hmtx>

<vhea>
<tableVersion value="0x00011000"/>
<ascent value="750"/>
<descent value="-250"/>
<lineGap value="200"/>
<advanceHeightMax value="1000"/>
<minTopSideBearing value="0"/>
<minBottomSideBearing value="-80"/>
<yMaxExtent value="1030"/>
<caretSlopeRise value="0"/>
<caretSlopeRun value="1"/>
<caretOffset value="0"/>
<reserved1 value="0"/>
<reserved2 value="0"/>
<reserved3 value="0"/>
<reserved4 value="0"/>
<metricDataFormat value="0"/>
<numberOfVMetrics value="12"/>
</vhea>

<vmtx>
<mtx name=".notdef" height="1000" tsb="0"/>
<mtx name="uni0020" height="250" tsb="750"/>
<mtx name="uni0061" height="750" tsb="240"/>
<mtx name="uni0062" height="750" tsb="245"/>
<mtx name="uni0063" height="750" tsb="250"/>
<mtx name="uni0064" height="750" tsb="553"/>
<mtx name="uni0065" height="750" tsb="240"/>
<mtx name="uni0066" height="750" tsb="240"/>
<mtx name="uni0067" height="750" tsb="240"/>
<mtx name="uni0068" height="1000" tsb="93"/>
<mtx name="uni0069" height="750" tsb="240"/>
<mtx name="uni006A" height="1000" tsb="520"/>
<mtx name="uni006B" height="1000" tsb="240"/>
<mtx name="uni006C" height="1000" tsb="240"/>
</vmtx>

</ttFont>
Loading