Iosevka/buildglyphs.patel
2015-11-18 00:21:48 +08:00

575 lines
No EOL
25 KiB
Text

define Glyph [require './support/glyph'].Glyph
define Stroke [require './support/stroke'].Stroke
define tp [require './support/transform'].transformPoint
define utp [require './support/transform'].untransform
define inverse [require './support/transform'].inverse
define libspiro : require 'libspiro-js'
define SpiroExpansionContext [require './support/spiroexpand'].SpiroExpansionContext
### COMMON FUNCTIONS
define [mix a b p] : a + (b - a) * p
define [linreg x0 y0 x1 y1 x] : y0 + (x - x0) * (y1 - y0) / (x1 - x0)
define [bilinear x0 x1 y0 y1 z00 z01 z10 z11 x y] : linreg
* y0
linreg x0 z00 x1 z10 x
* y1
linreg x0 z01 x1 z11 x
* y
define [clamp l h x] : if (x < l) l : if (x > h) h x
define [fallback] : for [local j 0] (j < arguments.length) [inc j] : if (arguments.(j) !== nothing) : return arguments.(j)
define [TempFont] {.glyf {} .head {.} .hhea {.} ."OS/2" {.} .name {.} .post {.}}
# Empty font base file
define-macro $$include : syntax-rules
{'$$include' {'.quote' file}} : begin
local path : require 'path'
local fs : require 'fs'
local f0 [[env.macros.get 'input-path']].1
local parse [require './syntax.js'].parse
local absolutePath : path.resolve [path.dirname f0] [formOf file]
local input : fs.readFileSync absolutePath 'utf-8'
local ast : parse input {.within {.file absolutePath .input input}}
return {'.syntactic-closure' ast env}
otherwise `nothing
define [buildFont para recursive] : begin
define variantSelector para.variantSelector
define font this
define glyphList font.glyf
define glyphs {.}
define unicodeGlyphs {}
define UPM 1000
define serifed : not [not para.serif]
# Key metrics
define WIDTH para.width
define SB para.sb
define CAP para.cap
define XH para.xheight
define DESCENDER para.descender
# Key metrics for symbols
local parenTop ((XH * 0.625) + (CAP - XH) * 2.5)
local parenBot ((XH * 0.625) - (CAP - XH) * 2.5)
local parenMid [mix parenTop parenBot 0.5]
# Transform constructors
define [Italify angle shift] : begin
local slope [Math.tan ([fallback angle para.italicangle] / 180 * Math.PI)]
return {.xx 1 .yx slope .xy 0 .yy 1 .x [fallback shift : -slope * parenMid] .y 0}
define [Upright angle shift] [inverse : Italify angle shift]
define [Scale sx sy] {.xx sx .yx 0 .xy 0 .yy [fallback sy sx] .x 0 .y 0}
define [Translate x y] {.xx 1 .yx 0 .xy 0 .yy 1 .x x .y y}
define [Rotate angle] {.xx [Math.cos angle] .yx (-[Math.sin angle]) .xy [Math.sin angle] .yy [Math.cos angle] .x 0 .y 0}
define globalTransform : Italify para.italicAngle
define deGlobalTransform : inverse globalTransform
define CORRECTION_HX : [fallback para.contrast 1] / [Math.sqrt (1 - globalTransform.yx * globalTransform.yx)]
# Orient parameters
define UPWARD {.x (-CORRECTION_HX) .y 0}
define DOWNWARD {.x CORRECTION_HX .y 0}
define RIGHTWARD {.x globalTransform.yx .y 1}
define LEFTWARD {.x (- globalTransform.yx) .y (-1)}
define [normalize a] : begin
local m : Math.hypot a.x a.y
return {.x (a.x / m) .y (a.y / m)}
# Style parameters
define O para.overshoot
define OXHOOK para.oxhook
define HOOK para.hook
define AHOOK para.ahook
define SHOOK para.shook
define RHOOK para.rhook
define JHOOK para.jhook
define FHOOK para.fhook
define HOOKX para.hookx
define SMOOTH para.smooth
define SMALLSMOOTH para.smallsmooth
define STROKE para.stroke
define CONTRAST para.contrast
define DOTSIZE : fallback para.dotsize STROKE
define PERIODSIZE : fallback para.periodsize DOTSIZE
define BARPOS : fallback para.barpos 0.5
define GBARPOS : fallback para.gbarpos 0.5
define PBARPOS : fallback para.pbarpos 0.5
define EBARPOS : fallback para.ebarpos BARPOS
define OVERLAYPOS para.overlaypos
define FIVEBARPOS para.fivebarpos
define LONGJUT para.longjut
define JUT para.jut
define VJUT para.vjut
define ACCENT para.accent
define ACCENTX para.accentx
define SLAB para.slab
define KAPPA para.kappa
define BKAPPA : para.bkappa || KAPPA + 0.1
define KAPPA_SPIRO_ARC : KAPPA + 0.1
define CKAPPA : para.ckappa || BKAPPA
define KAPPA_HOOK : fallback para.kappa_hook (BKAPPA + 0.1)
define KAPPA_AHOOK : fallback para.kappa_ahook KAPPA_HOOK
define KAPPA_RHOOK : fallback para.kappa_rhook KAPPA_HOOK
define TAILADJX : WIDTH * 0.2
define TAILADJY : XH * 0.25
define LBALANCE : LONGJUT * 0.04
define IBALANCE : fallback para.ibalance (LONGJUT * 0.04)
define JBALANCE : fallback para.jbalance 0
define JBALANCE2 : fallback para.jbalance2 (STROKE / 2 + LBALANCE)
define TBALANCE : fallback para.tbalance JBALANCE
define TBALANCE2 : fallback para.tbalance2 TBALANCE
define RBALANCE : fallback para.rbalance (JBALANCE * 0.3)
define RBALANCE2 : fallback para.rbalance2 0
define SBALANCE : fallback para.sbalance 0.52
define FBALANCE : fallback para.fbalance 0
define ONEBALANCE : fallback para.onebalance 0
# derived metrics
define FULLWIDTH : if para.cjkSpacing (WIDTH * 2) WIDTH
define VSTROKE : STROKE * CONTRAST
define ESS : STROKE * [fallback para.essx CONTRAST]
define XO : XH - O
define CAPO : CAP - O
define HALFSTROKE : STROKE / 2
define RIGHTSB : WIDTH - SB
define FWRSB : FULLWIDTH - SB
define MIDDLE : WIDTH / 2
define FWMIDDLE : FULLWIDTH / 2
define CAPMIDDLE : CAP / 2
define CAP_SMOOTH : CAP - SMOOTH
define DOTRADIUS : DOTSIZE / 2
define PERIODRADIUS : PERIODSIZE / 2
define SIDEJUT : JUT - HALFSTROKE * CORRECTION_HX
define SMOOTHA : SMOOTH - globalTransform.yx * para.smoothadjust
define SMOOTHB : SMOOTH + globalTransform.yx * para.smoothadjust
define SMALLSMOOTHA : SMALLSMOOTH - globalTransform.yx * para.smoothadjust
define SMALLSMOOTHB : SMALLSMOOTH + globalTransform.yx * para.smoothadjust
define CORRECTION_VX globalTransform.yx
define CORRECTION_VS : STROKE * globalTransform.yx
define CORRECTION_OMIDX : globalTransform.yx * 1.2
define CORRECTION_OMIDS : STROKE * CORRECTION_OMIDX
# Blackness parameters
# We will estimate blackness using lower-case 'e'
define WHITENESS : ((XH - STROKE * 3) * (RIGHTSB - SB) * (1 / 3)) / (XH * (RIGHTSB - SB))
define [adviceBlackness crowdedness] : Math.min STROKE ((RIGHTSB - SB) * (1 - WHITENESS) / (crowdedness * CORRECTION_HX))
define MVERTSTROKE : adviceBlackness : fallback para.lllcrowdedness (3 + 1 / 3)
define OVERLAYSTROKE : adviceBlackness 4
define OPERATORSTROKE : adviceBlackness 3.2
define SHOULDERFINE : [adviceBlackness 4] / 2
define SUPERNESS : fallback para.superness 2
define [superxy x] : Math.pow (1 - [Math.pow x SUPERNESS]) (1 / SUPERNESS)
define [adviceSSmooth y sign] : begin
local ss : y * 0.22 + STROKE * 0.22 + 0.02 * (RIGHTSB - SB)
return : ss + sign * globalTransform.yx * para.smoothadjust * (ss / SMALLSMOOTH)
define [adviceGlottalStopSmooth y sign] : ((y - STROKE) * 0.25 + STROKE / 2) + sign * globalTransform.yx * para.smoothadjust
define [shoulderMidSlope _fine _stroke _dir] : 0.5 * CORRECTION_HX * ([fallback _stroke STROKE] - [fallback _fine SHOULDERFINE]) / [fallback _stroke STROKE] + [fallback _dir 1] * globalTransform.yx
# Anchor parameters
define BASE 'base'
define MARK 'mark'
define MARKBASE 'markbase'
define AS_BASE 'AS-BASE'
define [tm anchor] : return {
.x (anchor.x * globalTransform.xx + anchor.y * globalTransform.yx + globalTransform.x)
.y (anchor.x * globalTransform.xy + anchor.y * globalTransform.yy + globalTransform.y)
.type anchor.type
}
define markAboveLower {.anchors {.above [tm {.x MIDDLE .y XH .type BASE}]}}
define markAboveCap {.anchors {.above [tm {.x MIDDLE .y CAP .type BASE}]}}
define markBelowLower {.anchors {.below [tm {.x MIDDLE .y DESCENDER .type BASE}]}}
define markBelowZero {.anchors {.below [tm {.x MIDDLE .y 0 .type BASE}]}}
define markToprightLower {.anchors {.topright [tm {.x RIGHTSB .y XH .type BASE}]}}
define markToprightCap {.anchors {.topright [tm {.x RIGHTSB .y CAP .type BASE}]}}
define markBottomrightLower {.anchors {.bottomright [tm {.x RIGHTSB .y DESCENDER .type BASE}]}}
define markBottomrightZero {.anchors {.bottomright [tm {.x RIGHTSB .y 0 .type BASE}]}}
define [anchorDeriv] : begin
local h {.}
foreach a [items-of arguments] : foreach k [items-of [Object.keys a.anchors]] : begin
set h.(k) {.}
set {.x h.(k).x .y h.(k).y .type h.(k).type .mbx h.(k).mbx .mby h.(k).mby} a.anchors.(k)
return {.anchors h}
define [StdAnchorGroup] : begin
local a : anchorDeriv.apply null arguments
set a.anchors.overlay {.type BASE
.x [mix a.anchors.below.x a.anchors.above.x OVERLAYPOS]
.y [mix a.anchors.below.y a.anchors.above.y OVERLAYPOS]
}
set a.anchors.slash {.type BASE
.x [mix a.anchors.below.x a.anchors.above.x 0.5]
.y [mix a.anchors.below.y a.anchors.above.y 0.5]
}
return a
define capitalMarks : StdAnchorGroup markAboveCap markBelowZero markToprightCap markBottomrightZero
define bMarks : StdAnchorGroup markAboveCap markBelowZero markToprightCap markBottomrightZero
define eMarks : StdAnchorGroup markAboveLower markBelowZero markToprightLower markBottomrightZero
define pMarks : StdAnchorGroup markAboveLower markBelowLower markToprightLower markBottomrightLower
define ifMarks : StdAnchorGroup markAboveCap markBelowLower markToprightCap markBottomrightLower
### Necessary macros
# Remap Glyph's methods to macros in order to simplify writing
define-macro set-width : syntax-rules
`[set-width @::args] {'.syntactic-closure' `[currentGlyph.set-width @::args] env}
define-macro start-from : syntax-rules
`[start-from @::args] {'.syntactic-closure' `[currentGlyph.start-from @::args] env}
define-macro line-to : syntax-rules
`[line-to @::args] {'.syntactic-closure' `[currentGlyph.line-to @::args] env}
define-macro curve-to : syntax-rules
`[curve-to @::args] {'.syntactic-closure' `[currentGlyph.curve-to @::args] env}
define-macro cubic-to : syntax-rules
`[cubic-to @::args] {'.syntactic-closure' `[currentGlyph.cubic-to @::args] env}
define-macro arc-hv-to : syntax-rules
`[arc-hv-to @::args] {'.syntactic-closure' `[currentGlyph.arc-hv-to @::args] env}
define-macro arc-vh-to : syntax-rules
`[arc-vh-to @::args] {'.syntactic-closure' `[currentGlyph.arc-vh-to @::args] env}
define-macro include : syntax-rules
`[include @::args] {'.syntactic-closure' `[currentGlyph.include @::args] env}
define-macro create-stroke : syntax-rules
`[create-stroke @::args] {'.syntactic-closure' `[currentGlyph.create-stroke @::args] env}
define-macro set-anchor : syntax-rules
`[set-anchor @::args] {'.syntactic-closure' `[currentGlyph.set-anchor @::args] env}
define-macro apply-transform : syntax-rules
`[apply-transform @::args] {'.syntactic-closure' `[currentGlyph.apply-transform @::args] env}
define-macro reverse-last : syntax-rules
`[reverse-last @::args] {'.syntactic-closure' `[currentGlyph.reverse-last @::args] env}
define-macro eject-contour : syntax-rules
`[eject-contour @::args] {'.syntactic-closure' `[currentGlyph.eject-contour @::args] env}
define-macro tag-contour : syntax-rules
`[tag-contour @name] {'.syntactic-closure' `(currentGlyph.contours.(currentGlyph.contours.length - 1).tag = @name) env}
define-macro dont-export : syntax-rules
`[dont-export] {".syntactic-closure" `[set currentGlyph.dontExport true] env}
define-macro assign-unicode : syntax-rules
`[assign-unicode @code] {".syntactic-closure" `[begin \\
currentGlyph.assign-unicode @code
set unicodeGlyphs.(currentGlyph.unicode.((currentGlyph.unicode.length - 1))) currentGlyph
] env}
# The macro [glyph-construction] creates a function which builds a glyph.
define-macro glyph-construction : syntax-rules
`[glyph-construction @::steps] {'.syntactic-closure' `[lambda [_args] [begin \\
local glyphArguments : fallback _args {.}
local currentGlyph this
currentGlyph.gizmo = globalTransform
local _tag currentGlyph.defaultTag
if glyphArguments.tag : set currentGlyph.defaultTag glyphArguments.tag
begin @::[steps.map formOf]
set currentGlyph.defaultTag _tag
return nothing
]] env}
### Glyph slots and dependency profile generation (used for recursive subfonts)
local dependencyProfile {.}
local nTemp 0
local pickHash : if recursive
then : let [h {.}] : begin
foreach j [items-of recursive] : set h.(j) j
* h
else nothing
define [create-glyph name actions] : piecewise
(name && actions) : begin
if (pickHash && [not pickHash.(name)]) : return nothing
set dependencyProfile.(name) {}
define glyphObject [new Glyph name]
glyphObject.set-width WIDTH
glyphList.push glyphObject
glyphs.(name) = glyphObject
actions.call glyphObject
foreach d [items-of glyphObject.dependencies] : begin
local allAliases : Object.keys glyphs :.filter ([k] -> (glyphs.(k) === glyphs.(d)))
dependencyProfile.(name) = [dependencyProfile.(name).concat dependencyProfile.(d) allAliases]
# process.stderr.write "\[if recursive 'Recursive ' '']Built Glyph /\(name).\n"
return glyphObject
true : begin
local actions arguments.0
local glyphObject [new Glyph ('.temp-' + [set nTemp (nTemp + 1)])]
glyphObject.set-width WIDTH
actions.call glyphObject
return glyphObject
define [select-variant name unicode default] : begin
if (pickHash && [not pickHash.(name)]) : return nothing
local variant : variantSelector.(name) || default
local chosenGlyph glyphs.((name + '.' + variant))
create-glyph name : glyph-construction
include chosenGlyph AS_BASE
if unicode : assign-unicode unicode
local allAliases : Object.keys glyphs :.filter ([k] -> (glyphs.(k) === glyphs.(chosenGlyph.name)))
set dependencyProfile.(name) : allAliases.concat dependencyProfile.(chosenGlyph.name)
define [italic-variant name unicode] : create-glyph name : glyph-construction
if (para.italicangle > 0)
then : include glyphs.(name + '.italic') AS_BASE
else : include glyphs.(name + '.upright') AS_BASE
if unicode : assign-unicode unicode
define [alias newid unicode oldid] : create-glyph newid : glyph-construction
if unicode : assign-unicode unicode
include glyphs.(oldid) AS_BASE
set-width glyphs.(oldid).advanceWidth
### Spiro constructions
# Basic knots
define [g4 x y f] {.x x .y y .type 'g4' .af f}
define [g2 x y f] {.x x .y y .type 'g2' .af f}
define [corner x y f] {.x x .y y .type 'corner' .af f}
define [flat x y f] {.x x .y y .type 'left' .af f}
define [curl x y f] {.x x .y y .type 'right' .af f}
define [close f] {.type 'close' .af f}
define [end f] {.type 'end' .af f}
define straight {.l flat .r curl}
#derived knots
#"ai" knots, used for left and right edges of letters `o`, and similar letters
define flat.ai : if para.isItalic g4 flat
define curl.ai : if para.isItalic g4 curl
#directional bi-knots
let
directions {{.name 'up' .x 0 .y 1}, {.name 'down' .x 0 .y (-1)}, {.name 'left' .x (-1) .y 0}, {.name 'right' .x 1 .y 0}}
adhensions {{.name 'start' .l 0 .r 0.01}, {.name 'mid', .l (-0.005) .r 0.005}, {.name 'end', .l (-0.01) .r 0}}
knottypes {g4, g2, corner, straight}
foreach [direction : items-of directions] : let [d direction] : begin
foreach [knottype : items-of knottypes] : let [kt knottype] : begin
set kt.(d.name) {.}
foreach [adh : items-of adhensions] : let [a adh] : begin
set kt.(d.name).(a.name) : lambda [x y f] : list
[fallback kt.l kt] (x + d.x * a.l) (y + d.y * a.l) f
[fallback kt.r kt] (x + d.x * a.r) (y + d.y * a.r) f
# Aux functions
define [widths l r] : lambda [] : this.set-width l r
define [widths.lhs w] : widths [fallback w STROKE] 0
define [widths.rhs w] : widths 0 [fallback w STROKE]
define [widths.center w] : widths ([fallback w STROKE] / 2) ([fallback w STROKE] / 2)
define [heading d] : lambda [] : this.heads-to d
define [widths.heading l r d] : lambda [] : begin
this.set-width l r; this.heads-to d
define [unimportant] : begin
if (this.points && this.points.length && this.points.(this.points.length - 1)) : this.points.(this.points.length - 1).subdivided = true
if (this.controlKnots && this.controlKnots.length && this.controlKnots.(this.controlKnots.length - 1)) : this.controlKnots.(this.controlKnots.length - 1).unimportant = true
define [important] nothing
# Interpolation pesudoknots
define [afInterpolate before after args] : g4
mix before.x after.x args.rx
mix before.y after.y args.ry
fallback args.raf unimportant
define [afInterpolateThem before after args] : begin
local knots {}
foreach {rx ry preserve} [items-of args.rs] : knots.push : [fallback args.ty g2] [mix before.x after.x rx] [mix before.y after.y ry] : fallback args.raf : match preserve
1 before.af
2 after.af
otherwise unimportant
return knots
define [alsothru rx ry raf] {.type 'interpolate' .rx rx .ry ry .raf raf .af afInterpolate}
define [alsothruthem rs raf ty] {.type 'interpolate' .rs rs .raf raf .ty ty .af afInterpolateThem}
define [bez3 a b c d t] : (1 - t) * (1 - t) * (1 - t) * a + 3 * (1 - t) * (1 - t) * t * b +3 * t * t * (1 - t) * c + t * t * t * d
define [bezcontrols x1 y1 x2 y2 _samples raf ty] : begin
local samples : fallback _samples 3
local tiny 0.005
local rs {}
foreach j [range 1 samples] : rs.push : list
bez3 0 x1 x2 1 [mix tiny (1 - tiny) (j / samples)]
bez3 0 y1 y2 1 [mix tiny (1 - tiny) (j / samples)]
alsothruthem rs raf ty
define [bezcontrols.absolute x1 y1 x2 y2 _samples raf ty] : object [type 'interpolate'] : af : lambda [before after] : begin
local samples : fallback _samples 3
local tiny 0.005
local rs {}
foreach j [range 1 samples] : rs.push : g4
bez3 before.x x1 x2 after.x [mix tiny (1 - tiny) (j / samples)]
bez3 before.y y1 y2 after.y [mix tiny (1 - tiny) (j / samples)]
fallback raf unimportant
return rs
define [quadcontrols x1 y1 samples raf ty] : bezcontrols (x1 * 2 / 3) (y1 * 2 / 3) [mix 1 x1 (2 / 3)] [mix 1 y1 (2 / 3)] samples raf ty
define {jhv, jvh} : let [cache {}] : begin
local [build samples] : begin
local hv {}
local vh {}
foreach [j : range 1 samples] : begin
local theta : j / samples * Math.PI / 2
local c : Math.pow [Math.cos theta] (2 / SUPERNESS)
local s : Math.pow [Math.sin theta] (2 / SUPERNESS)
hv.push {s (1 - c)}
vh.push {(1 - c) s}
cache.(samples) = {.hv hv .vh vh}
local [hv samples] : begin
if [not cache.(samples)] : build samples
return cache.(samples).hv
local [vh samples] : begin
if [not cache.(samples)] : build samples
return cache.(samples).vh
list hv vh
define [archv samples notiny k raf] : alsothruthem [jhv [fallback samples 4]] raf
define [arcvh samples notiny k raf] : alsothruthem [jvh [fallback samples 4]] raf
define [complexThru] : begin
local a : {}.slice.call arguments
return {.type 'interpolate' .af [lambda [before after args] : begin \\
local ks {}
foreach knot [items-of a] : ks.push [knot.af.call this before after knot]
return ks
]}
# Spiro construction
define [flatten knots] : begin
local a {}
foreach p [items-of knots] : piecewise
(p <@ Array) : set a : a.concat [flatten p]
true : a.push p
return a
define [prepareSpiroKnots knots s] : begin
local closed false
local lastafs {}
while (knots.0 && knots.0 <@ Function) : begin
knots.0.call s
set knots : knots.slice 1
while (knots.(knots.length - 1) && (knots.(knots.length - 1).type === 'close' || knots.(knots.length - 1).type === 'end')) : begin
set closed : knots.(knots.length - 1).type === 'close'
lastafs.push knots.(knots.length - 1).af
set knots : knots.slice 0 (-1)
set knots : flatten knots
if closed : knots.push knots.0
foreach j [range 0 knots.length] : if (knots.(j) && knots.(j).type === 'interpolate') : begin
set knots.(j) : knots.(j).af.call s knots.(j - 1) knots.(j + 1) knots.(j)
if closed : knots.pop
return {.knots [flatten knots] .closed closed .lastafs lastafs}
define [dispiro] : begin
local s : new SpiroExpansionContext
set s.gizmo globalTransform
local {.knots knots .closed closed .lastafs lastafs} : prepareSpiroKnots [{}.slice.call arguments 0] s
foreach knot [items-of knots] : let [ty knot.type] [af knot.af] : begin
set knot.af : lambda [] : begin
this.set-type ty
if af : af.apply this arguments
libspiro.spiroToBezierOnContext knots closed s
foreach af [items-of lastafs] : if af : af.call s
local {.lhs lhs .rhs rhs} : s.expand CONTRAST
if closed
then
local g : new Glyph
libspiro.spiroToBezierOnContext [lhs.slice 0 (-1)] true g true
local lhsContour g.contours.0
set g.contours {}
libspiro.spiroToBezierOnContext [rhs.reverse :.slice 0 (-1)] true g true
local rhsContour g.contours.0
set g.contours {[lhsContour.concat rhsContour]}
else
local g : new Glyph
lhs.0.type = rhs.0.type = lhs.(lhs.length - 1).type = rhs.(rhs.length - 1).type = 'corner'
libspiro.spiroToBezierOnContext [lhs.concat : rhs.reverse] true g true
local r g.contours
r.knots = knots
return r
define [spiro-outline] : let [k : {}.slice.call arguments 0] : glyph-construction
local {.knots knots .closed closed .lastafs lastafs} : prepareSpiroKnots k this
libspiro.spiroToBezierOnContext knots closed this
foreach af [items-of lastafs] : if af : af.call this
# contour tagging
define [tagged tag fn] : lambda [_args] : begin
local args : if _args [begin [local a : Object.create _args] [set a.tag tag] a] {.tag tag}
return : fn.call this args
###### HERE WE GO!
### Metadata
# Font names
set font.name.fontFamily para.family
set font.name.fontSubFamily para.style
set font.name.preferredFamily para.family
set font.name.preferredSubFamily para.style
set font.name.uniqueSubFamily "\(para.family) \(para.style) \(para.version) (\(para.codename))"
set font.name.version para.version
set font.name.fullName : if (para.style != 'Regular') (para.family + ' ' + para.style) para.family
set font.name.postScriptName : font.name.fullName.replace [regex ' ' 'g'] '-'
set font.name.copyright para.copyright
set font.name.licence para.licence
# Weight, width and slantness
set font.'OS/2'.usWeightClass para.weight
set font.'OS/2'.bProportion 9 # Monospaced
set font.'OS/2'.bWeight : 1 + para.weight / 100
set font.'OS/2'.fsSelection : [if para.isBold 32 0] + [if para.isItalic 1 0] + [if ([not para.isBold] && [not para.isItalic]) 64 0] + 128
set font.'OS/2'.sFamilyClass : 8 * 0x100 + 9
set font.post.isFixedPitch 1
set font.head.macStyle : [if para.isBold 1 0] + [if para.isItalic 2 0]
# Metric metadata
# Note: we use 1000upm in design, and (1000 * upmsacle)upm in production, to avoid rounding error.
let [asc : 1250 * CAP / (CAP - DESCENDER)] [desc : 1250 * DESCENDER / (CAP - DESCENDER)] : begin
set font.head.unitsPerEm 1000
set font.hhea.ascent asc
set font.'OS/2'.usWinAscent asc
set font.'OS/2'.sTypoAscender asc
set font.hhea.descent desc
set font.'OS/2'.usWinDescent [Math.abs desc]
set font.'OS/2'.sTypoDescender desc
set font.hhea.lineGap (CAP * 0.2)
set font.'OS/2'.sTypoLineGap (CAP * 0.2)
set font.'OS/2'.sxHeight XH
set font.'OS/2'.sCapHeight CAP
set font.post.italicAngle (0 - para.italicangle)
# Necessary notdef glyph
create-glyph '.notdef' : glyph-construction
start-from SB 0
line-to SB CAP
line-to RIGHTSB CAP
line-to RIGHTSB 0
start-from (SB + STROKE) STROKE
line-to (RIGHTSB - STROKE) STROKE
line-to (RIGHTSB - STROKE) (CAP - STROKE)
line-to (SB + STROKE) (CAP - STROKE)
create-glyph 'space' : glyph-construction
set-width WIDTH
assign-unicode ' '
include eMarks
# ASCII
$$include 'glyphs/common-shapes.patel'
$$include 'glyphs/overmarks.patel'
$$include 'glyphs/latin-basic-capital.patel'
$$include 'glyphs/latin-basic-lower.patel'
$$include 'glyphs/numbers.patel'
$$include 'glyphs/symbol-ascii.patel'
# Extended
$$include 'glyphs/greek.patel'
$$include 'glyphs/cyrillic-basic.patel'
$$include 'glyphs/latin-extend-basis.patel'
$$include 'glyphs/latin-extend-decorated.patel'
$$include 'glyphs/cyrillic-extended.patel'
$$include 'glyphs/symbol-punctuation.patel'
$$include 'glyphs/symbol-math.patel'
$$include 'glyphs/symbol-letter.patel'
$$include 'glyphs/symbol-geometric.patel'
$$include 'glyphs/symbol-other.patel'
$$include 'glyphs/autobuilds.patel'
set font.glyfMap glyphs
return font
exports.build = buildFont