mathfont.py (7619B)
1 import fontforge 2 3 PUA_startCodePoint = 0xE000 4 em = 1000 5 6 7 def create(aName, aCopyRight): 8 print("Generating %s.woff..." % aName, end="") 9 mathFont = fontforge.font() 10 mathFont.fontname = aName 11 mathFont.familyname = aName 12 mathFont.fullname = aName 13 mathFont.copyright = aCopyRight 14 mathFont.encoding = "UnicodeFull" 15 16 # Create a space character. Also force the creation of some MATH subtables 17 # so that OTS will not reject the MATH table. 18 g = mathFont.createChar(ord(" "), "space") 19 g.width = em 20 g.italicCorrection = 0 21 g.topaccent = 0 22 g.mathKern.bottomLeft = tuple([(0, 0)]) 23 g.mathKern.bottomRight = tuple([(0, 0)]) 24 g.mathKern.topLeft = tuple([(0, 0)]) 25 g.mathKern.topRight = tuple([(0, 0)]) 26 mathFont[ord(" ")].horizontalVariants = "space" 27 mathFont[ord(" ")].verticalVariants = "space" 28 return mathFont 29 30 31 def drawRectangleGlyph(glyph, width, ascent, descent=0, padding_left=0): 32 p = glyph.glyphPen() 33 p.moveTo(padding_left, -descent) 34 p.lineTo(padding_left, ascent) 35 p.lineTo(padding_left + width, ascent) 36 p.lineTo(padding_left + width, -descent) 37 p.closePath() 38 glyph.width = padding_left + width 39 40 41 def createSquareGlyph(aFont, aCodePoint): 42 g = aFont.createChar(aCodePoint) 43 drawRectangleGlyph(g, em, em, 0) 44 45 46 def drawHexaDigit(aGlyph, aX, aValue): 47 t = em / 10 48 p = aGlyph.glyphPen(replace=False) 49 if aValue == 0: 50 p.moveTo(aX + t, t) 51 p.lineTo(aX + t, em - t) 52 p.lineTo(aX + em / 2 - t, em - t) 53 p.lineTo(aX + em / 2 - t, t) 54 p.closePath() 55 elif aValue == 1: 56 p.moveTo(aX + em / 2 - t, em - t) 57 p.lineTo(aX + em / 2 - t, t) 58 p.endPath() 59 elif aValue == 2: 60 p.moveTo(aX + t, em - t) 61 p.lineTo(aX + em / 2 - t, em - t) 62 p.lineTo(aX + em / 2 - t, em / 2) 63 p.lineTo(aX + t, em / 2) 64 p.lineTo(aX + t, t) 65 p.lineTo(aX + em / 2 - t, t) 66 p.endPath() 67 elif aValue == 3: 68 p.moveTo(aX + t, em - t) 69 p.lineTo(aX + em / 2 - t, em - t) 70 p.lineTo(aX + em / 2 - t, t) 71 p.lineTo(aX + t, t) 72 p.endPath() 73 p.moveTo(aX + t, em / 2) 74 p.lineTo(aX + em / 2 - 2.5 * t, em / 2) 75 p.endPath() 76 elif aValue == 4: 77 p.moveTo(aX + em / 2 - t, em - t) 78 p.lineTo(aX + em / 2 - t, t) 79 p.endPath() 80 p.moveTo(aX + t, em - t) 81 p.lineTo(aX + t, em / 2) 82 p.lineTo(aX + em / 2 - 2.5 * t, em / 2) 83 p.endPath() 84 elif aValue == 5: 85 p.moveTo(aX + em / 2 - t, em - t) 86 p.lineTo(aX + t, em - t) 87 p.lineTo(aX + t, em / 2) 88 p.lineTo(aX + em / 2 - t, em / 2) 89 p.lineTo(aX + em / 2 - t, t) 90 p.lineTo(aX + t, t) 91 p.endPath() 92 elif aValue == 6: 93 p.moveTo(aX + em / 2 - t, em - t) 94 p.lineTo(aX + t, em - t) 95 p.lineTo(aX + t, t) 96 p.lineTo(aX + em / 2 - t, t) 97 p.lineTo(aX + em / 2 - t, em / 2) 98 p.lineTo(aX + 2.5 * t, em / 2) 99 p.endPath() 100 elif aValue == 7: 101 p.moveTo(aX + t, em - t) 102 p.lineTo(aX + em / 2 - t, em - t) 103 p.lineTo(aX + em / 2 - t, t) 104 p.endPath() 105 elif aValue == 8: 106 p.moveTo(aX + t, t) 107 p.lineTo(aX + t, em - t) 108 p.lineTo(aX + em / 2 - t, em - t) 109 p.lineTo(aX + em / 2 - t, t) 110 p.closePath() 111 p.moveTo(aX + 2.5 * t, em / 2) 112 p.lineTo(aX + em / 2 - 2.5 * t, em / 2) 113 p.endPath() 114 elif aValue == 9: 115 p.moveTo(aX + t, t) 116 p.lineTo(aX + em / 2 - t, t) 117 p.lineTo(aX + em / 2 - t, em - t) 118 p.lineTo(aX + t, em - t) 119 p.lineTo(aX + t, em / 2) 120 p.lineTo(aX + em / 2 - 2.5 * t, em / 2) 121 p.endPath() 122 elif aValue == 10: # A 123 p.moveTo(aX + t, t) 124 p.lineTo(aX + t, em - t) 125 p.lineTo(aX + em / 2 - t, em - t) 126 p.lineTo(aX + em / 2 - t, t) 127 p.endPath() 128 p.moveTo(aX + 2.5 * t, em / 2) 129 p.lineTo(aX + em / 2 - 2.5 * t, em / 2) 130 p.endPath() 131 elif aValue == 11: # b 132 p.moveTo(aX + t, em - t) 133 p.lineTo(aX + t, t) 134 p.lineTo(aX + em / 2 - t, t) 135 p.lineTo(aX + em / 2 - t, em / 2) 136 p.lineTo(aX + 2.5 * t, em / 2) 137 p.endPath() 138 elif aValue == 12: # C 139 p.moveTo(aX + em / 2 - t, em - t) 140 p.lineTo(aX + t, em - t) 141 p.lineTo(aX + t, t) 142 p.lineTo(aX + em / 2 - t, t) 143 p.endPath() 144 elif aValue == 13: # d 145 p.moveTo(aX + em / 2 - t, em - t) 146 p.lineTo(aX + em / 2 - t, t) 147 p.lineTo(aX + t, t) 148 p.lineTo(aX + t, em / 2) 149 p.lineTo(aX + em / 2 - 2.5 * t, em / 2) 150 p.endPath() 151 elif aValue == 14: # E 152 p.moveTo(aX + em / 2 - t, em - t) 153 p.lineTo(aX + t, em - t) 154 p.lineTo(aX + t, t) 155 p.lineTo(aX + em / 2 - t, t) 156 p.endPath() 157 p.moveTo(aX + em / 2 - t, em / 2) 158 p.lineTo(aX + 2.5 * t, em / 2) 159 p.endPath() 160 elif aValue == 15: # F 161 p.moveTo(aX + em / 2 - t, em - t) 162 p.lineTo(aX + t, em - t) 163 p.lineTo(aX + t, t) 164 p.endPath() 165 p.moveTo(aX + em / 2 - t, em / 2) 166 p.lineTo(aX + 2.5 * t, em / 2) 167 p.endPath() 168 169 170 def createGlyphFromValue(aFont, aCodePoint): 171 g = aFont.createChar(aCodePoint) 172 value = aCodePoint 173 for i in range(0, 5): 174 drawHexaDigit(g, (5 - (i + 1)) * em / 2, value % 16) 175 value /= 16 176 g.width = 5 * em // 2 177 g.stroke("circular", em / 10, "square", "miter", "cleanup") 178 179 180 def createSizeVariants(aFont, aUsePUA=False, aCenterOnBaseline=False): 181 if aUsePUA: 182 codePoint = PUA_startCodePoint 183 else: 184 codePoint = -1 185 for size in (0, 1, 2, 3): 186 g = aFont.createChar(codePoint, "v%d" % size) 187 if aCenterOnBaseline: 188 drawRectangleGlyph(g, em, (size + 1) * em / 2, (size + 1) * em / 2) 189 else: 190 drawRectangleGlyph(g, em, (size + 1) * em, 0) 191 if aUsePUA: 192 codePoint += 1 193 g = aFont.createChar(codePoint, "h%d" % size) 194 if aCenterOnBaseline: 195 drawRectangleGlyph(g, (size + 1) * em, em / 2, em / 2) 196 else: 197 drawRectangleGlyph(g, (size + 1) * em, em, 0) 198 if aUsePUA: 199 codePoint += 1 200 201 202 def createStretchy(aFont, codePoint, isHorizontal): 203 if isHorizontal: 204 aFont[codePoint].horizontalVariants = "h0 h1 h2 h3" 205 # Part: (glyphName, isExtender, startConnector, endConnector, fullAdvance) 206 aFont[codePoint].horizontalComponents = \ 207 (("h2", False, 0, em, 3 * em), 208 ("h1", True, em, em, 2 * em)) 209 else: 210 aFont[codePoint].verticalVariants = "v0 v1 v2 v3" 211 # Part: (glyphName, isExtender, startConnector, endConnector, fullAdvance) 212 aFont[codePoint].verticalComponents = \ 213 (("v2", False, 0, em, 3 * em), 214 ("v1", True, em, em, 2 * em)) 215 216 217 def save(aFont): 218 aFont.em = em 219 aFont.ascent = aFont.hhea_ascent = aFont.os2_typoascent = em 220 aFont.descent = aFont.hhea_descent = aFont.os2_typodescent = 0 221 # aFont.os2_winascent, aFont.os2_windescent should be the maximum of 222 # ascent/descent for all glyphs. Does fontforge compute them automatically? 223 aFont.hhea_ascent_add = aFont.hhea_descent_add = 0 224 aFont.os2_typoascent_add = aFont.os2_typodescent_add = 0 225 aFont.os2_winascent_add = aFont.os2_windescent_add = 0 226 aFont.os2_use_typo_metrics = True 227 aFont.generate("../../fonts/math/%s.woff" % aFont.fontname) 228 if aFont.validate() == 0: 229 print(" done.") 230 else: 231 print(" validation error!") 232 exit(1)