generate.py (8616B)
1 #!/usr/bin/python 2 # vim: set shiftwidth=4 tabstop=8 autoindent expandtab: 3 # This Source Code Form is subject to the terms of the Mozilla Public 4 # License, v. 2.0. If a copy of the MPL was not distributed with this 5 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 7 # For general fontforge documentation, see: 8 # http://fontforge.sourceforge.net/ 9 # For fontforge scripting documentation, see: 10 # http://fontforge.sourceforge.net/scripting-tutorial.html 11 # http://fontforge.sourceforge.net/scripting.html 12 # and most importantly: 13 # http://fontforge.sourceforge.net/python.html 14 15 # To install what you need, on Ubuntu, 16 # sudo apt-get install python-fontforge 17 18 import fontforge 19 20 em = 1000 21 22 23 def newMathFont(aName): 24 print("Generating %s.otf..." % aName, end="") 25 mathFont = fontforge.font() 26 mathFont.fontname = aName 27 mathFont.familyname = aName 28 mathFont.fullname = aName 29 mathFont.copyright = "Copyright (c) 2014 Mozilla Corporation" 30 mathFont.encoding = "UnicodeFull" 31 32 # Create a space character. Also force the creation of some MATH subtables 33 # so that OTS will not reject the MATH table. 34 g = mathFont.createChar(ord(" "), "space") 35 g.width = em 36 g.italicCorrection = 0 37 g.topaccent = 0 38 g.mathKern.bottomLeft = tuple([(0, 0)]) 39 g.mathKern.bottomRight = tuple([(0, 0)]) 40 g.mathKern.topLeft = tuple([(0, 0)]) 41 g.mathKern.topRight = tuple([(0, 0)]) 42 mathFont[ord(" ")].horizontalVariants = "space" 43 mathFont[ord(" ")].verticalVariants = "space" 44 return mathFont 45 46 47 def saveMathFont(aFont): 48 aFont.em = em 49 aFont.ascent = aFont.descent = em / 2 50 aFont.hhea_ascent = aFont.os2_typoascent = aFont.os2_winascent = em / 2 51 aFont.descent = aFont.hhea_descent = em / 2 52 aFont.os2_typodescent = aFont.os2_windescent = em / 2 53 aFont.hhea_ascent_add = aFont.hhea_descent_add = 0 54 aFont.os2_typoascent_add = aFont.os2_typodescent_add = 0 55 aFont.os2_winascent_add = aFont.os2_windescent_add = 0 56 aFont.os2_use_typo_metrics = True 57 aFont.generate(aFont.fontname + ".otf") 58 print(" done.") 59 60 61 def createSquareGlyph(aFont, aCodePoint): 62 g = aFont.createChar(aCodePoint) 63 p = g.glyphPen() 64 p.moveTo(0, 0) 65 p.lineTo(em, 0) 66 p.lineTo(em, em) 67 p.lineTo(0, em) 68 p.closePath() 69 70 71 def createLLTriangleGlyph(aFont, aCodePoint): 72 g = aFont.createChar(aCodePoint) 73 p = g.glyphPen() 74 p.moveTo(0, 0) 75 p.lineTo(em, 0) 76 p.lineTo(0, em) 77 p.closePath() 78 79 80 def createURTriangleGlyph(aFont, aCodePoint): 81 g = aFont.createChar(aCodePoint) 82 p = g.glyphPen() 83 p.moveTo(em, 0) 84 p.lineTo(em, em) 85 p.lineTo(0, em) 86 p.closePath() 87 88 89 def createDiamondGlyph(aFont, aCodePoint): 90 g = aFont.createChar(aCodePoint) 91 p = g.glyphPen() 92 p.moveTo(0, em / 2) 93 p.lineTo(em / 2, 0) 94 p.lineTo(em, em / 2) 95 p.lineTo(em / 2, em) 96 p.closePath() 97 98 99 ################################################################################ 100 # Glyph variants and constructions 101 f = newMathFont("stretchy") 102 nvariants = 3 103 104 # Draw boxes for the size variants and glues 105 for i in range(0, nvariants): 106 s = em * (i + 1) 107 108 g = f.createChar(-1, "h%d" % i) 109 p = g.glyphPen() 110 p.moveTo(0, -em) 111 p.lineTo(0, em) 112 p.lineTo(s, em) 113 p.lineTo(s, -em) 114 p.closePath() 115 g.width = s 116 117 g = f.createChar(-1, "v%d" % i) 118 p = g.glyphPen() 119 p.moveTo(0, 0) 120 p.lineTo(0, s) 121 p.lineTo(2 * em, s) 122 p.lineTo(2 * em, 0) 123 p.closePath() 124 g.width = 2 * em 125 126 # Draw some pieces for stretchy operators 127 s = em * nvariants 128 129 g = f.createChar(-1, "left") 130 p = g.glyphPen() 131 p.moveTo(0, -2 * em) 132 p.lineTo(0, 2 * em) 133 p.lineTo(s, em) 134 p.lineTo(s, -em) 135 p.closePath() 136 g.width = s 137 138 g = f.createChar(-1, "right") 139 p = g.glyphPen() 140 p.moveTo(0, -em) 141 p.lineTo(0, em) 142 p.lineTo(s, 2 * em) 143 p.lineTo(s, -2 * em) 144 p.closePath() 145 g.width = s 146 147 g = f.createChar(-1, "hmid") 148 p = g.glyphPen() 149 p.moveTo(0, -em) 150 p.lineTo(0, em) 151 p.lineTo(s, 2 * em) 152 p.lineTo(2 * s, em) 153 p.lineTo(2 * s, -em) 154 p.lineTo(s, -2 * em) 155 p.closePath() 156 g.width = 2 * s 157 158 g = f.createChar(-1, "bottom") 159 p = g.glyphPen() 160 p.moveTo(0, 0) 161 p.lineTo(0, s) 162 p.lineTo(2 * em, s) 163 p.lineTo(4 * em, 0) 164 p.closePath() 165 g.width = 4 * em 166 167 g = f.createChar(-1, "top") 168 p = g.glyphPen() 169 p.moveTo(0, 0) 170 p.lineTo(4 * em, 0) 171 p.lineTo(2 * em, -s) 172 p.lineTo(0, -s) 173 p.closePath() 174 g.width = 4 * em 175 176 g = f.createChar(-1, "vmid") 177 p = g.glyphPen() 178 p.moveTo(0, s) 179 p.lineTo(2 * em, s) 180 p.lineTo(4 * em, 0) 181 p.lineTo(2 * em, -s) 182 p.lineTo(0, -s) 183 p.closePath() 184 g.width = 3 * em 185 186 # Create small rectangle of various size for some exotic arrows that are 187 # unlikely to be stretchable with standard fonts. 188 hstretchy = [ 189 0x219C, # leftwards wave arrow 190 0x219D, # rightwards wave arrow 191 0x219E, # leftwards two headed arrow 192 0x21A0, # rightwards two headed arrow 193 0x21A2, # leftwards arrow with tail 194 ] 195 vstretchy = [ 196 0x219F, # upwards two headed arrow 197 0x21A1, # downwards two headed arrow 198 0x21A5, # upwards arrow from bar 199 0x21A7, # downwards arrow from bar 200 0x21A8, # up down arrow with base 201 ] 202 for i in range(0, 1 + nvariants + 1): 203 s = (i + 1) * em / 10 204 205 g = f.createChar(hstretchy[i]) 206 p = g.glyphPen() 207 p.moveTo(0, -em / 10) 208 p.lineTo(0, em / 10) 209 p.lineTo(s, em / 10) 210 p.lineTo(s, -em / 10) 211 p.closePath() 212 g.width = s 213 214 g = f.createChar(vstretchy[i]) 215 p = g.glyphPen() 216 p.moveTo(0, 0) 217 p.lineTo(0, s) 218 p.lineTo(2 * em / 10, s) 219 p.lineTo(2 * em / 10, 0) 220 p.closePath() 221 g.width = 2 * em / 10 222 223 # hstretchy[0] and vstretchy[0] have all the variants and the components. The others only have one of them. 224 s = em * nvariants 225 226 f[hstretchy[0]].horizontalVariants = "uni219C h0 h1 h2" 227 f[hstretchy[0]].horizontalComponents = ( 228 ("left", False, 0, 0, s), 229 ("h2", True, 0, 0, s), 230 ("hmid", False, 0, 0, 2 * s), 231 ("h2", True, 0, 0, s), 232 ("right", False, 0, 0, s), 233 ) 234 235 f[hstretchy[1]].horizontalVariants = "uni219D h0" 236 f[hstretchy[2]].horizontalVariants = "uni219E h1" 237 f[hstretchy[3]].horizontalVariants = "uni21A0 h2" 238 f[hstretchy[4]].horizontalVariants = "uni21A2 h2" 239 f[hstretchy[4]].horizontalComponents = f[hstretchy[0]].horizontalComponents 240 241 f[vstretchy[0]].verticalVariants = "uni219F v0 v1 v2" 242 f[vstretchy[0]].verticalComponents = ( 243 ("bottom", False, 0, 0, s), 244 ("v2", True, 0, 0, s), 245 ("vmid", False, 0, 0, 2 * s), 246 ("v2", True, 0, 0, s), 247 ("top", False, 0, 0, s), 248 ) 249 250 f[vstretchy[1]].verticalVariants = "uni21A1 v0" 251 f[vstretchy[2]].verticalVariants = "uni21A5 v1" 252 f[vstretchy[3]].verticalVariants = "uni21A7 v2" 253 f[vstretchy[4]].verticalVariants = "uni21A8" 254 f[vstretchy[4]].verticalComponents = f[vstretchy[0]].verticalComponents 255 256 ################################################################################ 257 # Testing DisplayOperatorMinHeight 258 f.math.DisplayOperatorMinHeight = 8 * em 259 largeop = [0x2A1B, 0x2A1C] # integral with overbar/underbar 260 261 # Draw boxes of size 1, 2, 7, 8, 9em. 262 for i in [1, 2, 7, 8, 9]: 263 s = em * i 264 if i in {1, 2}: 265 g = f.createChar(largeop[i - 1]) 266 else: 267 g = f.createChar(-1, "L%d" % i) 268 p = g.glyphPen() 269 p.moveTo(0, 0) 270 p.lineTo(0, s) 271 p.lineTo(s, s) 272 p.lineTo(s, 0) 273 p.closePath() 274 g.width = s 275 276 f[largeop[0]].verticalVariants = "uni2A1B L7 L8 L9" 277 f[largeop[1]].verticalVariants = "uni2A1C L8" 278 279 saveMathFont(f) 280 281 ################################################################################ 282 # Testing AxisHeight 283 f = newMathFont("axis-height-1") 284 f.math.AxisHeight = 0 285 createSquareGlyph(f, ord("+")) 286 saveMathFont(f) 287 288 f = newMathFont("axis-height-2") 289 f.math.AxisHeight = 20 * em 290 createSquareGlyph(f, ord("+")) 291 saveMathFont(f) 292 293 ################################################################################ 294 # Testing Limits Parameters 295 f = newMathFont("limits-5") 296 f.math.UpperLimitGapMin = 0 297 f.math.UpperLimitBaselineRiseMin = 0 298 f.math.LowerLimitGapMin = 0 299 f.math.LowerLimitBaselineDropMin = 0 300 f.math.AccentBaseHeight = 6 * em 301 f.math.FlattenedAccentBaseHeight = 3 * em 302 createSquareGlyph(f, ord("~")) 303 saveMathFont(f) 304 305 f = newMathFont("dtls-1") 306 createSquareGlyph(f, ord("a")) 307 createLLTriangleGlyph(f, ord("b")) 308 createSquareGlyph(f, ord("c")) 309 createDiamondGlyph(f, 0x1D51E) # mathvariant=fraktur a 310 createURTriangleGlyph(f, 0x1D51F) # mathvariant=fraktur b 311 createDiamondGlyph(f, 0x1D520) # mathvariant=fraktur c 312 f.addLookup("gsub", "gsub_single", (), (("dtls", (("latn", ("dflt")),)),)) 313 f.addLookupSubtable("gsub", "gsub_n") 314 glyph = f["a"] 315 glyph.addPosSub("gsub_n", "b") 316 glyph2 = f[0x1D51F] 317 glyph2.glyphname = "urtriangle" 318 glyph3 = f[0x1D51E] 319 glyph3.addPosSub("gsub_n", "urtriangle") 320 saveMathFont(f)