define Glyph [require './support/glyph'].Glyph define Stroke [require './support/stroke'].Stroke define tp [require './support/transform'].transformPoint ### 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 [fallback] : for [local j 0] [j < arguments.length] [inc j] : if [arguments`j !== nothing] : return arguments`j define emptyFontStr : JSON.stringify [require './empty.json'] define [buildFont para recursive] : begin { define variantSelector para.variantSelector define font : JSON.parse emptyFontStr define glyphList font.glyf define glyphs (.'.notdef' glyphList.0) define unicodeGlyphs () define globalTransform ( .xx 1 .yx [Math.tan [para.italicangle / 180 * Math.PI]] .xy 0 .yy 1 .x 0 .y 0 ) define ITALICCOR : 1 / [Math.sqrt [1 - globalTransform.yx * globalTransform.yx]] define UPWARD (.x [-ITALICCOR] .y 0) define DOWNWARD (.x ITALICCOR .y 0) define RIGHTWARD (.x globalTransform.yx .y 1) define LEFTWARD (.x [- globalTransform.yx] .y [-1]) # metrics define DESCENDER para.descender define WIDTH para.width define CAP para.cap define XH para.xheight define O para.o define OXHOOK para.oxhook define SB para.sb define HOOK para.hook define AHOOK para.ahook define SHOOK para.shook define RHOOK para.rhook define SMOOTH para.smooth define SMALLSMOOTH para.smallsmooth define STROKE para.stroke define DOTSIZE para.dotsize define PERIODSIZE para.periodsize define BARPOS para.barpos define GBARPOS para.gbarpos define FIVEBARPOS para.fivebarpos define LONGJUT para.longjut define ACCENT para.accent define ACCENTX para.accentx # derived metrics define XO : XH - O define CAPO : CAP - O define HALFSTROKE : STROKE / 2 define RIGHTSB : WIDTH - SB define MIDDLE : WIDTH / 2 define CAPMIDDLE : CAP / 2 define CAP_SMOOTH : CAP - SMOOTH define DOTRADIUS : DOTSIZE / 2 define PERIODRADIUS : PERIODSIZE / 2 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 ITALICCORS : STROKE * globalTransform.yx # style parameters define EBARPOS : para.ebarpos || BARPOS define KAPPA para.kappa define COKAPPA : 1 - KAPPA define BKAPPA : para.bkappa || KAPPA + 0.1 define CKAPPA : para.ckappa || BKAPPA define COBKAPPA : 1 - BKAPPA define KAPPA_HOOK : para.kappa_hook || BKAPPA + 0.1 define KAPPA_AHOOK : para.kappa_ahook || KAPPA_HOOK define TAILADJX : WIDTH * 0.2 define TAILADJY : XH * 0.25 define TAILADJKAPPA 0.75 define TAILADJSX : WIDTH * 0.2 define TAILADJSY 0 define TAILADJSKAPPA 0.75 define ILBALANCE : LONGJUT * 0.04 define JBALANCE : para.jbalance || HALFSTROKE + ILBALANCE define TBALANCE : para.tbalance || JBALANCE define TBALANCE2 : para.tbalance2 || TBALANCE define RBALANCE : para.rbalance || JBALANCE * 0.3 # Blackness parameters # We will estimate blackness using lower-case 'e' define WHITENESS : [[XH - STROKE * 2.5] * [RIGHTSB - SB] * [1 / 3]] / [XH * [RIGHTSB - SB]] define [adviceBlackness crowdedness] : Math.min STROKE [[RIGHTSB - SB] * [1 - WHITENESS] / crowdedness] # Anchor parameters define BASE 'base' define MARK 'mark' define MARKBASE 'markbase' define AS_BASE 'AS-BASE' define [Upright angle] (.xx 1 .yx [-[Math.tan [[fallback angle para.italicangle] / 180 * Math.PI]]] .xy 0 .yy 1 .x 0 .y 0) define [Italify angle] (.xx 1 .yx [Math.tan [[fallback angle para.italicangle] / 180 * Math.PI]] .xy 0 .yy 1 .x 0 .y 0) 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 [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 markTrailingLower (.anchors (.trailing [tm (.x MIDDLE .y DESCENDER .type BASE)])) define markTrailingZero (.anchors (.trailing [tm (.x MIDDLE .y 0 .type BASE)])) define markLFLower (.anchors (.lf [tm (.x MIDDLE .y DESCENDER .type BASE)])) define markLFZero (.anchors (.lf [tm (.x MIDDLE .y 0 .type BASE)])) define capitalMarks (.anchors (.above markAboveCap.anchors.above .below markBelowZero.anchors.below .trailing markTrailingZero.anchors.trailing .lf markLFZero.anchors.lf)) define bMarks (.anchors (.above markAboveCap.anchors.above .below markBelowZero.anchors.below .trailing markTrailingZero.anchors.trailing .lf markLFZero.anchors.lf)) define eMarks (.anchors (.above markAboveLower.anchors.above .below markBelowZero.anchors.below .trailing markTrailingZero.anchors.trailing .lf markLFZero.anchors.lf)) define pMarks (.anchors (.above markAboveLower.anchors.above .below markBelowLower.anchors.below .trailing markTrailingLower.anchors.trailing .lf markLFLower.anchors.lf)) define ifMarks (.anchors (.above markAboveCap.anchors.above .below markBelowLower.anchors.below .trailing markTrailingLower.anchors.trailing .lf markLFLower.anchors.lf)) Stroke.bindParameters para ### 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 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.'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.head.macStyle : [if para.isBold 1 0] + [if para.isItalic 2 0] ### Metric metadata # Note: we use 1000upm in design, and 4096upm in production define upmscale : fallback para.upmscale 1 set font.head.unitsPerEm : upmscale * 1000 set font.hhea.ascent : upmscale * [CAP + ACCENT * 1.5] set font.'OS/2'.usWinAscent : upmscale * [CAP + ACCENT * 1.5] set font.'OS/2'.sTypoAscender : upmscale * [CAP + ACCENT * 1.5] set font.hhea.descent : upmscale * [DESCENDER - ACCENT * 0.5] set font.'OS/2'.usWinDescent : upmscale * [Math.abs [DESCENDER - ACCENT * 0.5]] set font.'OS/2'.sTypoDescender : upmscale * [DESCENDER - ACCENT * 0.5] set font.hhea.lineGap : upmscale * CAP * 0.2 set font.'OS/2'.sTypoLineGap : upmscale * CAP * 0.2 set font.'OS/2'.sxHeight : upmscale * XH set font.post.italicAnvle : 0 - para.italicangle ### Necessary macros define-macro glyph-construction : syntax-rules { @`[glyph-construction @::steps] ('.syntactic-closure' @`[lambda [] [begin { local currentGlyph this local set-width : this.set-width.bind this local assign-unicode : lambda [code] : begin { currentGlyph.assign-unicode code set unicodeGlyphs.(currentGlyph.unicode`[currentGlyph.unicode.length - 1]) currentGlyph } local start-from : this.start-from.bind this local line-to : this.line-to.bind this local curve-to : this.curve-to.bind this local cubic-to : this.cubic-to.bind this local arc-hv-to : this.arc-hv-to.bind this local arc-vh-to : this.arc-vh-to.bind this local put-shapes : this.put-shapes.bind this local reverse-last : this.reverse-last.bind this local include : this.include.bind this local create-stroke : this.create-stroke.bind this local set-anchor : this.set-anchor.bind this local apply-transform : this.apply-transform.bind this # Actually I can define them using macros, but it makes compilation slow. # define-macro set-width : syntax-rules { @`[set-width @::args] @`[currentGlyph.set-width @::args] } # define-macro start-from : syntax-rules { @`[start-from @::args] @`[currentGlyph.start-from @::args] } # define-macro line-to : syntax-rules { @`[line-to @::args] @`[currentGlyph.line-to @::args] } # define-macro curve-to : syntax-rules { @`[curve-to @::args] @`[currentGlyph.curve-to @::args] } # define-macro cubic-to : syntax-rules { @`[cubic-to @::args] @`[currentGlyph.cubic-to @::args] } # define-macro include : syntax-rules { @`[include @::args] @`[currentGlyph.include @::args] } # define-macro create-stroke : syntax-rules { @`[create-stroke @::args] @`[currentGlyph.create-stroke @::args] } # define-macro set-anchor : syntax-rules { @`[set-anchor @::args] @`[currentGlyph.set-anchor @::args] } # define-macro apply-transform : syntax-rules { @`[apply-transform @::args] @`[currentGlyph.apply-transform @::args] } # define-macro reverse-last : syntax-rules { @`[reverse-last @::args] @`[currentGlyph.reverse-last @::args] } local [dont-export] : set currentGlyph.dontExport true this.gizmo = globalTransform this.set-width WIDTH begin @::[steps.map formOf] return nothing }]] env) } 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 process.stderr.write : "Building /" + name + [if recursive " (recursive)" ""] + " for " + para.family + ' ' + para.style + "\n" 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] } return glyphObject } true : begin { local actions arguments.0 local glyphObject [new Glyph ['.temp-' + [set nTemp [inc nTemp]]]] 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] set glyphs`name chosenGlyph local allAliases : Object.keys glyphs :.filter [[k] -> [glyphs`k === glyphs.(chosenGlyph.name)]] set dependencyProfile`name : allAliases.concat dependencyProfile.(chosenGlyph.name) if unicode : begin { chosenGlyph.assign-unicode unicode set chosenGlyph.dontExport false set unicodeGlyphs.(chosenGlyph.unicode`[chosenGlyph.unicode.length - 1]) chosenGlyph } } 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 } ###### HERE WE GO! create-glyph 'space' : glyph-construction { set-width WIDTH assign-unicode ' ' include eMarks }