define Glyph [require './support/glyph'].Glyph define Stroke [require './support/stroke'].Stroke define tp [require './support/transform'].transformPoint define inverse [require './support/transform'].inverse ### 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-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 ast : parse [fs.readFileSync absolutePath 'utf-8'] (.included true) return ('.syntactic-closure' ast env) } otherwise @`nothing } 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 () # 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 HOOKX para.hookx 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 JUT para.jut define ACCENT para.accent define ACCENTX para.accentx # Common 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 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]) # 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 define OMIDCOR : globalTransform.yx * 1.2 define OMIDCOR_S : STROKE * OMIDCOR # 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 define serifed : not [not para.serif] # 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] define OPERATORSTROKE : adviceBlackness 3.25 # 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 capitalMarks (.anchors (.above markAboveCap.anchors.above .topright marktoprightCap.anchors.topright .below markBelowZero.anchors.below .bottomright markBottomrightZero.anchors.bottomright)) define bMarks (.anchors (.above markAboveCap.anchors.above .topright marktoprightCap.anchors.topright .below markBelowZero.anchors.below .bottomright markBottomrightZero.anchors.bottomright)) define eMarks (.anchors (.above markAboveLower.anchors.above .topright marktoprightLower.anchors.topright .below markBelowZero.anchors.below .bottomright markBottomrightZero.anchors.bottomright)) define pMarks (.anchors (.above markAboveLower.anchors.above .topright marktoprightLower.anchors.topright .below markBelowLower.anchors.below .bottomright markBottomrightLower.anchors.bottomright)) define ifMarks (.anchors (.above markAboveCap.anchors.above .topright marktoprightCap.anchors.topright .below markBelowLower.anchors.below .bottomright markBottomrightLower.anchors.bottomright)) 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 1000 set font.hhea.ascent [CAP + ACCENT * 1.5] set font.'OS/2'.usWinAscent [CAP + ACCENT * 1.5] set font.'OS/2'.sTypoAscender [CAP + ACCENT * 1.5] set font.hhea.descent [DESCENDER - ACCENT * 0.5] set font.'OS/2'.usWinDescent [Math.abs [DESCENDER - ACCENT * 0.5]] set font.'OS/2'.sTypoDescender [DESCENDER - ACCENT * 0.5] set font.hhea.lineGap [CAP * 0.2] set font.'OS/2'.sTypoLineGap [CAP * 0.2] set font.'OS/2'.sxHeight 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 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] 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 } ###### HERE WE GO! create-glyph 'space' : glyph-construction { set-width WIDTH assign-unicode ' ' include eMarks } $$include 'glyphs/common-shapes.patel' $$include 'glyphs/overmarks.patel' $$include 'glyphs/latin-basic-capital.patel' $$include 'glyphs/latin-basic-lower.patel' $$include 'glyphs/greek.patel' $$include 'glyphs/cyrillic-basic.patel' $$include 'glyphs/latin-extend.patel' $$include 'glyphs/cyrillic-extended.patel' $$include 'glyphs/numbers.patel' $$include 'glyphs/symbol-ascii.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' ### Zoom all glyphs by para.upmscale set font.head.unitsPerEm : upmscale * font.head.unitsPerEm set font.hhea.ascent : upmscale * font.hhea.ascent set font.'OS/2'.usWinAscent : upmscale * font.'OS/2'.usWinAscent set font.'OS/2'.sTypoAscender : upmscale * font.'OS/2'.sTypoAscender set font.hhea.descent : upmscale * font.hhea.descent set font.'OS/2'.usWinDescent : upmscale * font.'OS/2'.usWinDescent set font.'OS/2'.sTypoDescender : upmscale * font.'OS/2'.sTypoDescender set font.hhea.lineGap : upmscale * font.hhea.lineGap set font.'OS/2'.sTypoLineGap : upmscale * font.'OS/2'.sTypoLineGap set font.'OS/2'.sxHeight : upmscale * font.'OS/2'.sxHeight if [upmscale != 1] : foreach glyph [items-of glyphList] : begin { glyph.advanceWidth = glyph.advanceWidth * upmscale if glyph.contours: foreach contour [items-of glyph.contours] : foreach point [items-of contour] : begin { point.x = point.x * upmscale point.y = point.y * upmscale } } set font.glyfMap glyphs return font } exports.build = buildFont