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 18from __future__ import print_function 19import fontforge 20 21em = 1000 22 23def 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 46def saveMathFont(aFont): 47 aFont.em = em 48 aFont.ascent = aFont.descent = em/2 49 aFont.hhea_ascent = aFont.os2_typoascent = aFont.os2_winascent = em/2 50 aFont.descent = aFont.hhea_descent = em/2 51 aFont.os2_typodescent = aFont.os2_windescent = em/2 52 aFont.hhea_ascent_add = aFont.hhea_descent_add = 0 53 aFont.os2_typoascent_add = aFont.os2_typodescent_add = 0 54 aFont.os2_winascent_add = aFont.os2_windescent_add = 0 55 aFont.os2_use_typo_metrics = True 56 aFont.generate(aFont.fontname + ".otf") 57 print(" done.") 58 59def createSquareGlyph(aFont, aCodePoint): 60 g = aFont.createChar(aCodePoint) 61 p = g.glyphPen() 62 p.moveTo(0, 0) 63 p.lineTo(em, 0) 64 p.lineTo(em, em) 65 p.lineTo(0, em) 66 p.closePath(); 67 68def createLLTriangleGlyph(aFont, aCodePoint): 69 g = aFont.createChar(aCodePoint) 70 p = g.glyphPen() 71 p.moveTo(0, 0) 72 p.lineTo(em, 0) 73 p.lineTo(0, em) 74 p.closePath(); 75 76def createURTriangleGlyph(aFont, aCodePoint): 77 g = aFont.createChar(aCodePoint) 78 p = g.glyphPen() 79 p.moveTo(em, 0) 80 p.lineTo(em, em) 81 p.lineTo(0, em) 82 p.closePath(); 83 84def createDiamondGlyph(aFont, aCodePoint): 85 g = aFont.createChar(aCodePoint) 86 p = g.glyphPen() 87 p.moveTo(0, em/2) 88 p.lineTo(em/2, 0) 89 p.lineTo(em, em/2) 90 p.lineTo(em/2, em) 91 p.closePath(); 92 93################################################################################ 94# Glyph variants and constructions 95f = newMathFont("stretchy") 96nvariants = 3 97 98# Draw boxes for the size variants and glues 99for i in range(0, nvariants): 100 s = em * (i + 1) 101 102 g = f.createChar(-1, "h%d" % i) 103 p = g.glyphPen() 104 p.moveTo(0, -em) 105 p.lineTo(0, em) 106 p.lineTo(s, em) 107 p.lineTo(s, -em) 108 p.closePath() 109 g.width = s 110 111 g = f.createChar(-1, "v%d" % i) 112 p = g.glyphPen() 113 p.moveTo(0, 0) 114 p.lineTo(0, s) 115 p.lineTo(2 * em, s) 116 p.lineTo(2 * em, 0) 117 p.closePath(); 118 g.width = 2 * em 119 120# Draw some pieces for stretchy operators 121s = em * nvariants 122 123g = f.createChar(-1, "left") 124p = g.glyphPen() 125p.moveTo(0, -2 * em) 126p.lineTo(0, 2 * em) 127p.lineTo(s, em) 128p.lineTo(s, -em) 129p.closePath(); 130g.width = s 131 132g = f.createChar(-1, "right") 133p = g.glyphPen() 134p.moveTo(0, -em) 135p.lineTo(0, em) 136p.lineTo(s, 2 * em) 137p.lineTo(s, -2 * em) 138p.closePath(); 139g.width = s 140 141g = f.createChar(-1, "hmid") 142p = g.glyphPen() 143p.moveTo(0, -em) 144p.lineTo(0, em) 145p.lineTo(s, 2 * em) 146p.lineTo(2 * s, em) 147p.lineTo(2 * s, -em) 148p.lineTo(s, -2 * em) 149p.closePath(); 150g.width = 2 * s 151 152g = f.createChar(-1, "bottom") 153p = g.glyphPen() 154p.moveTo(0, 0) 155p.lineTo(0, s) 156p.lineTo(2 * em, s) 157p.lineTo(4 * em, 0) 158p.closePath(); 159g.width = 4 * em 160 161g = f.createChar(-1, "top") 162p = g.glyphPen() 163p.moveTo(0, 0) 164p.lineTo(4 * em, 0) 165p.lineTo(2 * em, -s) 166p.lineTo(0, -s) 167p.closePath(); 168g.width = 4 * em 169 170g = f.createChar(-1, "vmid") 171p = g.glyphPen() 172p.moveTo(0, s) 173p.lineTo(2 * em, s) 174p.lineTo(4 * em, 0) 175p.lineTo(2 * em, -s) 176p.lineTo(0, -s) 177p.closePath(); 178g.width = 3 * em 179 180# Create small rectangle of various size for some exotic arrows that are 181# unlikely to be stretchable with standard fonts. 182hstretchy = [ 183 0x219C, # leftwards wave arrow 184 0x219D, # rightwards wave arrow 185 0x219E, # leftwards two headed arrow 186 0x21A0, # rightwards two headed arrow 187 0x21A2 # leftwards arrow with tail 188] 189vstretchy = [ 190 0x219F, # upwards two headed arrow 191 0x21A1, # downwards two headed arrow 192 0x21A5, # upwards arrow from bar 193 0x21A7, # downwards arrow from bar 194 0x21A8 # up down arrow with base 195] 196for i in range(0, 1 + nvariants + 1): 197 s = (i + 1) * em/10 198 199 g = f.createChar(hstretchy[i]) 200 p = g.glyphPen() 201 p.moveTo(0, -em/10) 202 p.lineTo(0, em/10) 203 p.lineTo(s, em/10) 204 p.lineTo(s, -em/10) 205 p.closePath() 206 g.width = s 207 208 g = f.createChar(vstretchy[i]) 209 p = g.glyphPen() 210 p.moveTo(0, 0) 211 p.lineTo(0, s) 212 p.lineTo(2 * em/10, s) 213 p.lineTo(2 * em/10, 0) 214 p.closePath(); 215 g.width = 2 * em/10 216 217# hstretchy[0] and vstretchy[0] have all the variants and the components. The others only have one of them. 218s = em * nvariants 219 220f[hstretchy[0]].horizontalVariants = "uni219C h0 h1 h2" 221f[hstretchy[0]].horizontalComponents = (("left", False, 0, 0, s), \ 222("h2", True, 0, 0, s), ("hmid", False, 0, 0, 2 * s), ("h2", True, 0, 0, s), \ 223("right", False, 0, 0, s)) 224 225f[hstretchy[1]].horizontalVariants = "uni219D h0" 226f[hstretchy[2]].horizontalVariants = "uni219E h1" 227f[hstretchy[3]].horizontalVariants = "uni21A0 h2" 228f[hstretchy[4]].horizontalVariants = "uni21A2 h2" 229f[hstretchy[4]].horizontalComponents = f[hstretchy[0]].horizontalComponents 230 231f[vstretchy[0]].verticalVariants = "uni219F v0 v1 v2" 232f[vstretchy[0]].verticalComponents = (("bottom", False, 0, 0, s), \ 233("v2", True, 0, 0, s), ("vmid", False, 0, 0, 2 * s), ("v2", True, 0, 0, s), \ 234("top", False, 0, 0, s)) 235 236f[vstretchy[1]].verticalVariants = "uni21A1 v0" 237f[vstretchy[2]].verticalVariants = "uni21A5 v1" 238f[vstretchy[3]].verticalVariants = "uni21A7 v2" 239f[vstretchy[4]].verticalVariants = "uni21A8" 240f[vstretchy[4]].verticalComponents = f[vstretchy[0]].verticalComponents 241 242################################################################################ 243# Testing DisplayOperatorMinHeight 244f.math.DisplayOperatorMinHeight = 8 * em 245largeop = [0x2A1B, 0x2A1C] # integral with overbar/underbar 246 247# Draw boxes of size 1, 2, 7, 8, 9em. 248for i in [1, 2, 7, 8, 9]: 249 s = em * i 250 if i == 1 or i == 2: 251 g = f.createChar(largeop[i-1]) 252 else: 253 g = f.createChar(-1, "L%d" % i) 254 p = g.glyphPen() 255 p.moveTo(0, 0) 256 p.lineTo(0, s) 257 p.lineTo(s, s) 258 p.lineTo(s, 0) 259 p.closePath(); 260 g.width = s 261 262f[largeop[0]].verticalVariants = "uni2A1B L7 L8 L9" 263f[largeop[1]].verticalVariants = "uni2A1C L8" 264 265saveMathFont(f) 266 267################################################################################ 268# Testing AxisHeight 269f = newMathFont("axis-height-1") 270f.math.AxisHeight = 0 271createSquareGlyph(f, ord("+")) 272saveMathFont(f) 273 274f = newMathFont("axis-height-2") 275f.math.AxisHeight = 20 * em 276createSquareGlyph(f, ord("+")) 277saveMathFont(f) 278 279################################################################################ 280# Testing Fraction Parameters 281f = newMathFont("fraction-1") 282f.math.FractionRuleThickness = 20 * em 283f.math.FractionNumeratorShiftUp = 0 284f.math.FractionNumeratorDisplayStyleShiftUp = 0 285f.math.FractionDenominatorShiftDown = 0 286f.math.FractionNumeratorGapMin = 0 287f.math.FractionDenominatorGapMin = 0 288f.math.FractionNumeratorDisplayStyleShiftUp = 0 289f.math.FractionDenominatorDisplayStyleShiftDown = 0 290f.math.FractionNumeratorDisplayStyleGapMin = 0 291f.math.FractionDenominatorDisplayStyleGapMin = 0 292saveMathFont(f) 293 294f = newMathFont("fraction-2") 295f.math.FractionRuleThickness = 1 * em 296f.math.FractionNumeratorShiftUp = 0 297f.math.FractionDenominatorShiftDown = 0 298f.math.FractionNumeratorGapMin = 9.5 * em 299f.math.FractionDenominatorGapMin = 0 300saveMathFont(f) 301 302f = newMathFont("fraction-3") 303f.math.FractionRuleThickness = 1 * em 304f.math.FractionNumeratorShiftUp = 0 305f.math.FractionDenominatorShiftDown = 0 306f.math.FractionNumeratorGapMin = 0 307f.math.FractionDenominatorGapMin = 9.5 * em 308saveMathFont(f) 309 310f = newMathFont("fraction-4") 311f.math.FractionRuleThickness = 1 * em 312f.math.FractionNumeratorShiftUp = 3 * em 313f.math.FractionDenominatorShiftDown = 0 314f.math.FractionNumeratorGapMin = 0 315f.math.FractionDenominatorGapMin = 0 316saveMathFont(f) 317 318f = newMathFont("fraction-5") 319f.math.FractionRuleThickness = 1 * em 320f.math.FractionNumeratorShiftUp = 0 321f.math.FractionDenominatorShiftDown = 3 * em 322f.math.FractionNumeratorGapMin = 0 323f.math.FractionDenominatorGapMin = 0 324saveMathFont(f) 325 326f = newMathFont("fraction-6") 327f.math.FractionRuleThickness = 1 * em 328f.math.FractionNumeratorDisplayStyleShiftUp = 0 329f.math.FractionDenominatorDisplayStyleShiftDown = 0 330f.math.FractionNumeratorDisplayStyleGapMin = 9.5 * em 331f.math.FractionDenominatorDisplayStyleGapMin = 0 332saveMathFont(f) 333 334f = newMathFont("fraction-7") 335f.math.FractionRuleThickness = 1 * em 336f.math.FractionNumeratorDisplayStyleShiftUp = 0 337f.math.FractionDenominatorDisplayStyleShiftDown = 0 338f.math.FractionNumeratorDisplayStyleGapMin = 0 339f.math.FractionDenominatorDisplayStyleGapMin = 9.5 * em 340saveMathFont(f) 341 342f = newMathFont("fraction-8") 343f.math.FractionRuleThickness = 1 * em 344f.math.FractionNumeratorDisplayStyleShiftUp = 3 * em 345f.math.FractionDenominatorDisplayStyleShiftDown = 0 346f.math.FractionNumeratorDisplayStyleGapMin = 0 347f.math.FractionDenominatorDisplayStyleGapMin = 0 348saveMathFont(f) 349 350f = newMathFont("fraction-9") 351f.math.FractionRuleThickness = 1 * em 352f.math.FractionNumeratorDisplayStyleShiftUp = 0 353f.math.FractionDenominatorDisplayStyleShiftDown = 3 * em 354f.math.FractionNumeratorDisplayStyleGapMin = 0 355f.math.FractionDenominatorDisplayStyleGapMin = 0 356saveMathFont(f) 357 358################################################################################ 359# Testing Stack Parameters 360f = newMathFont("stack-1") 361f.math.StackTopShiftUp = 0 362f.math.StackBottomShiftDown = 0 363f.math.StackGapMin = 20 * em 364saveMathFont(f) 365 366f = newMathFont("stack-2") 367f.math.StackTopShiftUp = 3 * em 368f.math.StackBottomShiftDown = 0 369f.math.StackGapMin = 0 370saveMathFont(f) 371 372f = newMathFont("stack-3") 373f.math.StackTopShiftUp = 0 374f.math.StackBottomShiftDown = 3 * em 375f.math.StackGapMin = 0 376saveMathFont(f) 377 378f = newMathFont("stack-4") 379f.math.StackTopDisplayStyleShiftUp = 0 380f.math.StackBottomDisplayStyleShiftDown = 0 381f.math.StackDisplayStyleGapMin = 20 * em 382saveMathFont(f) 383 384f = newMathFont("stack-5") 385f.math.StackTopDisplayStyleShiftUp = 3 * em 386f.math.StackBottomDisplayStyleShiftDown = 0 387f.math.StackDisplayStyleGapMin = 0 388saveMathFont(f) 389 390f = newMathFont("stack-6") 391f.math.StackTopDisplayStyleShiftUp = 0 392f.math.StackBottomDisplayStyleShiftDown = 3 * em 393f.math.StackDisplayStyleGapMin = 0 394saveMathFont(f) 395 396################################################################################ 397# Testing Radical Parameters 398f = newMathFont("radical-1") 399f.math.RadicalExtraAscender = 0 400f.math.RadicalRuleThickness = 5 * em 401f.math.RadicalVerticalGap = 0 402createSquareGlyph(f, 0x221a) 403saveMathFont(f) 404 405f = newMathFont("radical-2") 406f.math.RadicalExtraAscender = 7 * em 407f.math.RadicalRuleThickness = 1 * em 408f.math.RadicalVerticalGap = 0 409createSquareGlyph(f, 0x221a) 410saveMathFont(f) 411 412f = newMathFont("radical-3") 413f.math.RadicalExtraAscender = 0 414f.math.RadicalRuleThickness = 1 * em 415f.math.RadicalVerticalGap = 3 * em 416createSquareGlyph(f, 0x221a) 417saveMathFont(f) 418 419f = newMathFont("radical-4") 420f.math.RadicalExtraAscender = 0 421f.math.RadicalRuleThickness = 1 * em 422f.math.RadicalDisplayStyleVerticalGap = 9 * em 423createSquareGlyph(f, 0x221a) 424saveMathFont(f) 425 426f = newMathFont("radical-5") 427f.math.RadicalExtraAscender = 0 428f.math.RadicalRuleThickness = 1 * em 429f.math.RadicalVerticalGap = 0 430f.math.RadicalDegreeBottomRaisePercent = 25 431createSquareGlyph(f, 0x221a) 432saveMathFont(f) 433 434f = newMathFont("radical-6") 435f.math.RadicalKernBeforeDegree = 5 * em 436f.math.RadicalKernAfterDegree = 0 437createSquareGlyph(f, 0x221a) 438saveMathFont(f) 439 440f = newMathFont("radical-7") 441f.math.RadicalKernBeforeDegree = 0 442f.math.RadicalKernAfterDegree = 7 * em 443createSquareGlyph(f, 0x221a) 444saveMathFont(f) 445 446################################################################################ 447# Testing Scripts Parameters 448f = newMathFont("scripts-1") 449f.math.SpaceAfterScript = 3 * em 450saveMathFont(f) 451 452f = newMathFont("scripts-2") 453f.math.SuperscriptShiftUp = 7 * em 454saveMathFont(f) 455 456f = newMathFont("scripts-3") 457f.math.SuperscriptShiftUpCramped = 5 * em 458createSquareGlyph(f, 0x221a) 459saveMathFont(f) 460 461f = newMathFont("scripts-4") 462f.math.SubscriptShiftDown = 6 * em 463saveMathFont(f) 464 465f = newMathFont("scripts-5") 466f.math.SubSuperscriptGapMin = 11 * em 467saveMathFont(f) 468 469f = newMathFont("scripts-6") 470f.math.SubSuperscriptGapMin = 11 * em 471f.math.SuperscriptBottomMaxWithSubscript = 3 * em 472saveMathFont(f) 473 474f = newMathFont("scripts-7") 475f.math.SubscriptTopMax = 5 * em 476saveMathFont(f) 477 478f = newMathFont("scripts-8") 479f.math.SuperscriptBottomMin = 9 * em 480saveMathFont(f) 481 482f = newMathFont("scripts-9") 483f.math.SubscriptBaselineDropMin = 3 * em 484f.math.SuperscriptBaselineDropMax = 5 * em 485saveMathFont(f) 486 487################################################################################ 488# Testing Limits Parameters 489f = newMathFont("limits-1") 490f.math.UpperLimitGapMin = 7 * em 491f.math.UpperLimitBaselineRiseMin = 0 492f.math.LowerLimitGapMin = 0 493f.math.LowerLimitBaselineDropMin = 0 494createSquareGlyph(f, 0x2211) 495saveMathFont(f) 496 497f = newMathFont("limits-2") 498f.math.UpperLimitGapMin = 0 499f.math.UpperLimitBaselineRiseMin = 0 500f.math.LowerLimitGapMin = 5 * em 501f.math.LowerLimitBaselineDropMin = 0 502createSquareGlyph(f, 0x2211) 503saveMathFont(f) 504 505f = newMathFont("limits-3") 506f.math.UpperLimitGapMin = 0 507f.math.UpperLimitBaselineRiseMin = 9 * em 508f.math.LowerLimitGapMin = 0 509f.math.LowerLimitBaselineDropMin = 0 510createSquareGlyph(f, 0x2211) 511saveMathFont(f) 512 513f = newMathFont("limits-4") 514f.math.UpperLimitGapMin = 0 515f.math.UpperLimitBaselineRiseMin = 0 516f.math.LowerLimitGapMin = 0 517f.math.LowerLimitBaselineDropMin = 2 * em 518createSquareGlyph(f, 0x2211) 519saveMathFont(f) 520 521f = newMathFont("limits-5") 522f.math.UpperLimitGapMin = 0 523f.math.UpperLimitBaselineRiseMin = 0 524f.math.LowerLimitGapMin = 0 525f.math.LowerLimitBaselineDropMin = 0 526f.math.AccentBaseHeight = 6 * em 527f.math.FlattenedAccentBaseHeight = 3 * em 528createSquareGlyph(f, ord("~")) 529saveMathFont(f) 530 531f = newMathFont("dtls-1") 532createSquareGlyph(f, ord("a")) 533createLLTriangleGlyph(f, ord("b")) 534createSquareGlyph(f, ord("c")) 535createDiamondGlyph(f, 0x1D51E) #mathvariant=fraktur a 536createURTriangleGlyph(f, 0x1D51F) #mathvariant=fraktur b 537createDiamondGlyph(f, 0x1D520) #mathvariant=fraktur c 538f.addLookup("gsub", "gsub_single", (), (("dtls", (("latn", ("dflt")),)),)) 539f.addLookupSubtable("gsub", "gsub_n") 540glyph = f["a"] 541glyph.addPosSub("gsub_n", "b") 542glyph2 = f[0x1D51F] 543glyph2.glyphname="urtriangle" 544glyph3 = f[0x1D51E] 545glyph3.addPosSub("gsub_n", "urtriangle") 546saveMathFont(f) 547