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