summaryrefslogtreecommitdiff
path: root/misc/tools/postprocess-designspace.py
blob: c69666b8ca0a853c6bd0b29ce81dd9e19ae5ad62 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import sys, os, os.path, re
import defcon
from multiprocessing import Pool
from fontTools.designspaceLib import DesignSpaceDocument
from datetime import datetime

sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), 'tools')))
from common import getGitHash, getVersion
from postprocess_instance_ufo import ufo_set_wws


def update_version(ufo):
  version = getVersion()
  buildtag, buildtagErrs = getGitHash()
  now = datetime.utcnow()
  if buildtag == "" or len(buildtagErrs) > 0:
    buildtag = "src"
    print("warning: getGitHash() failed: %r" % buildtagErrs, file=sys.stderr)
  versionMajor, versionMinor = [int(num) for num in version.split(".")]
  ufo.info.versionMajor = versionMajor
  ufo.info.versionMinor = versionMinor
  ufo.info.year = now.year
  ufo.info.openTypeNameVersion = "Version %d.%03d;git-%s" % (versionMajor, versionMinor, buildtag)
  psFamily = re.sub(r'\s', '', ufo.info.familyName)
  psStyle = re.sub(r'\s', '', ufo.info.styleName)
  ufo.info.openTypeNameUniqueID = "%s-%s:%d:%s" % (psFamily, psStyle, now.year, buildtag)
  ufo.info.openTypeHeadCreated = now.strftime("%Y/%m/%d %H:%M:%S")


def fix_opsz_range(designspace):
  # TODO: find extremes by looking at the source
  for a in designspace.axes:
    if a.tag == "opsz":
      a.minimum = 14
      a.maximum = 32
      break
  return designspace


def fix_wght_range(designspace):
  for a in designspace.axes:
    if a.tag == "wght":
      a.minimum = 100
      a.maximum = 900
      break
  return designspace


def should_decompose_glyph(g):
  if g.components and len(g.components) > 0:
    for c in g.components:
      # Does the component have non-trivial transformation? (i.e. scaled or skewed)
      # Example of no transformation: (identity matrix)
      #   (1, 0, 0, 1, 0, 0)    no scale or offset
      # Example of simple offset transformation matrix:
      #   (1, 0, 0, 1, 20, 30)  20 x offset, 30 y offset
      # Example of scaled transformation matrix:
      #   (-1.0, 0, 0.3311, 1, 1464.0, 0)  flipped x axis, sheered and offset
      # Matrix order:
      #   (x_scale, x_skew, y_skew, y_scale, x_pos, y_pos)

      # if g.name == 'dotmacron.lc':
      #   print(f"{g.name} cn {c.baseGlyph}", c.transformation)
      # Check if transformation is not identity (ignoring x & y offset)
      m = c.transformation
      if m[0] + m[1] + m[2] + m[3] != 2.0:
        return True
  return False


def find_glyphs_to_decompose(designspace_source):
  glyph_names = set()
  # print("find_glyphs_to_decompose inspecting %r" % designspace_source.name)
  ufo = defcon.Font(designspace_source.path)
  for g in ufo:
    if should_decompose_glyph(g):
      glyph_names.add(g.name)
  return list(glyph_names)


def set_ufo_filter(ufo, **filter_dict):
  filters = ufo.lib.setdefault("com.github.googlei18n.ufo2ft.filters", [])
  for i in range(len(filters)):
    if filters[i].get("name") == filter_dict["name"]:
      filters[i] = filter_dict
      return
  filters.append(filter_dict)


def update_source_ufo(ufo_file, glyphs_to_decompose):
  print(f"update {os.path.basename(ufo_file)}")
  ufo = defcon.Font(ufo_file)
  update_version(ufo)
  set_ufo_filter(ufo, name="decomposeComponents", include=glyphs_to_decompose)
  ufo_set_wws(ufo) # Fix missing WWS entries for Display fonts
  ufo.save(ufo_file)


def update_sources(designspace):
  with Pool() as p:
    sources = [source for source in designspace.sources]
    # sources = [s for s in sources if s.name == "Inter Thin"] # DEBUG
    glyphs_to_decompose = set()
    for glyph_names in p.map(find_glyphs_to_decompose, sources):
      glyphs_to_decompose.update(glyph_names)
    glyphs_to_decompose = list(glyphs_to_decompose)
    # print("glyphs marked to be decomposed: %s" % ', '.join(glyphs_to_decompose))
    source_files = list(set([s.path for s in sources]))
    p.starmap(update_source_ufo, [(path, glyphs_to_decompose) for path in source_files])
  return designspace


def main(argv):
  designspace_file = argv[1]
  designspace = DesignSpaceDocument.fromfile(designspace_file)
  designspace = fix_opsz_range(designspace)
  designspace = fix_wght_range(designspace)
  designspace = update_sources(designspace)
  designspace.write(designspace_file)


if __name__ == '__main__':
  main(sys.argv)