split_slnt_vf.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. """
  2. ...
  3. """
  4. import argparse
  5. import sys
  6. import os
  7. from fontTools.ttLib import TTFont
  8. from fontTools.ttLib.tables import otTables as ot
  9. from fontTools.otlLib.builder import buildStatTable, _addName
  10. from fontTools.varLib.instancer import (
  11. instantiateVariableFont,
  12. sanityCheckVariableTables
  13. )
  14. def split_slnt(ttfont, out_dir):
  15. """Use varlib instance to split a variable font if it contains a
  16. slnt or ital axis."""
  17. sanityCheckVariableTables(ttfont)
  18. axes = {a.axisTag: a for a in ttfont['fvar'].axes}
  19. ital_angle = axes['ital'].maxValue
  20. roman = instantiateVariableFont(ttfont, {"ital": 0})
  21. italic = instantiateVariableFont(ttfont, {"ital": ital_angle})
  22. _update_bits(italic)
  23. _update_nametable(italic)
  24. _update_fvar(roman)
  25. _update_fvar(italic)
  26. _update_roman_stat(roman)
  27. _update_italic_stat(italic)
  28. roman_filename = os.path.join(
  29. out_dir,
  30. vf_filename(roman)
  31. )
  32. roman.save(roman_filename)
  33. italic_filename = os.path.join(
  34. out_dir,
  35. vf_filename(italic)
  36. )
  37. italic.save(italic_filename)
  38. def _update_fvar(ttfont):
  39. fvar = ttfont['fvar']
  40. nametable = ttfont['name']
  41. family_name = nametable.getName(16, 3, 1, 1033) or nametable.getName(1, 3, 1, 1033)
  42. family_name = family_name.toUnicode()
  43. font_style = "Italic" if "Italic" in nametable.getName(2, 3, 1, 1033).toUnicode() else "Roman"
  44. ps_family_name = f"{family_name.replace(' ', '')}{font_style}"
  45. nametable.setName(ps_family_name, 25, 3, 1, 1033)
  46. for instance in fvar.instances:
  47. instance_style = nametable.getName(instance.subfamilyNameID, 3, 1, 1033).toUnicode()
  48. instance_style = instance_style.replace("Italic", "").strip().replace(" ", "")
  49. if instance_style == "":
  50. instance_style = "Regular"
  51. ps_name = f"{ps_family_name}-{instance_style}"
  52. instance.postscriptNameID = _addName(nametable, ps_name, 256)
  53. def _update_roman_stat(ttfont):
  54. stat = ttfont['STAT'].table
  55. record = ot.AxisValue()
  56. record.AxisIndex = 2
  57. record.Flags = 2
  58. record.ValueNameID = 296 # Roman
  59. record.LinkedValue = 1
  60. record.Value = 0
  61. record.Format = 3
  62. stat.AxisValueArray.AxisValue[-1] = record
  63. def _update_italic_stat(ttfont):
  64. stat = ttfont['STAT'].table
  65. record = ot.AxisValue()
  66. record.AxisIndex = 2
  67. record.Flags = 0
  68. record.ValueNameID = 258 # Italic
  69. record.Value = 1.0
  70. record.Format = 1
  71. stat.AxisValueArray.AxisValue[-1] = record
  72. def vf_filename(ttfont):
  73. axes = sorted([a.axisTag for a in ttfont['fvar'].axes])
  74. axes = ",".join(axes)
  75. family_name = ttfont['name'].getName(1, 3, 1, 1033)
  76. name = family_name.toUnicode()
  77. if "Italic" in ttfont['name'].getName(2, 3, 1, 1033).toUnicode():
  78. return f"{name}-Italic[{axes}].ttf"
  79. return f"{name}[{axes}].ttf"
  80. def _update_bits(ttfont):
  81. """Update bits for instantiated italic font"""
  82. # OS/2: disable Regular bit and enable italic bit
  83. ttfont['OS/2'].fsSelection ^= (1 << 6) | 1
  84. # head: enable italic bit
  85. ttfont["head"].macStyle |= (1 << 1)
  86. ttfont["post"].italicAngle = -12
  87. ttfont["hhea"].caretSlopeRun = 435
  88. ttfont["hhea"].caretSlopeRise = 2048
  89. def _update_nametable(ttfont):
  90. nametable = ttfont['name']
  91. dflt_axes_loc = {a.axisTag: a.defaultValue for a in ttfont['fvar'].axes}
  92. dflt_nameid = None
  93. for instance in ttfont['fvar'].instances:
  94. if instance.coordinates == dflt_axes_loc:
  95. dflt_nameid = instance.subfamilyNameID
  96. if not dflt_nameid:
  97. raise ValueError("Cannot name font. Default axis locations are not represented by an instance.")
  98. dflt_name = nametable.getName(dflt_nameid, 3, 1, 1033).toUnicode()
  99. familyname = nametable.getName(1, 3, 1, 1033).toUnicode()
  100. # Update subfamily name
  101. nametable.setName(dflt_name, 2, 3, 1, 1033)
  102. # Update full font name and uniqueID
  103. full_font_name = f"{familyname} {dflt_name}"
  104. nametable.setName(full_font_name, 3, 3, 1, 1033)
  105. nametable.setName(full_font_name, 4, 3, 1, 1033)
  106. nametable.setName(full_font_name, 4, 1, 0, 0)
  107. # Postscript name
  108. postscript_name = f"{familyname}-{dflt_name}"
  109. nametable.setName(postscript_name, 6, 3, 1, 1033)
  110. nametable.setName(postscript_name, 6, 1, 0, 0)
  111. def main():
  112. ttfont = TTFont(sys.argv[1])
  113. split_slnt(ttfont, sys.argv[2])
  114. if __name__ == "__main__":
  115. main()