Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified xkcd-script/font/xkcd-script.otf
Binary file not shown.
2,539 changes: 1,295 additions & 1,244 deletions xkcd-script/font/xkcd-script.sfd

Large diffs are not rendered by default.

Binary file modified xkcd-script/font/xkcd-script.ttf
Binary file not shown.
Binary file modified xkcd-script/font/xkcd-script.woff
Binary file not shown.
19 changes: 14 additions & 5 deletions xkcd-script/generator/pt5_svg_to_font.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,11 +331,6 @@ def charname(char):
font.hhea_descent = -270; font.hhea_descent_add = False
font.hhea_linegap = 77

# Information to be conveyed to the next stage.
# I wanted to use font.persistent, but it causes an error. Instead, I use a dummy glyph.
font.createChar(-1, '_pad_space')
font['_pad_space'].width = SPACE + RSPACE

# Per-character size scaling applied after changeWeight, to fine-tune individual glyphs
# that end up slightly too large despite correct stroke weight.
_per_char_operation = {
Expand Down Expand Up @@ -443,6 +438,20 @@ def charname(char):
c = font.createChar(0x000D, 'nonmarkingreturn') # U+000D: carriage return, zero-width; required by OpenType
c.width = 256

# Information to be conveyed to the next stage.
font.createChar(-1, '_pad_space')
font['_pad_space'].width = SPACE + RSPACE
TYPICAL_BOUNDINGWIDTH = 200
font.createChar(-1, '_typical_bbox')
c = fontforge.contour()
l = SPACE // 2 + 20
r = SPACE // 2 + TYPICAL_BOUNDINGWIDTH - 20
_ascender_top = font['l'].boundingBox()[3]
_descender_bottom = font['p'].boundingBox()[1]
c.moveTo(l, _descender_bottom).lineTo(l, _ascender_top).lineTo(r, _ascender_top).lineTo(r, _descender_bottom).lineTo(l, _descender_bottom)
font['_typical_bbox'].foreground += c
font['_typical_bbox'].width = TYPICAL_BOUNDINGWIDTH + SPACE + RSPACE


# ---------------------------------------------------------------------------
# Glyphs imported from xkcd comic images
Expand Down
19 changes: 11 additions & 8 deletions xkcd-script/generator/pt6_derived_chars.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,6 @@ def _accented(cp, base_name, mark_name, gap=20, x_adj=0):
_bb = _breve_mark.boundingBox()
_breve_mark.transform(psMat.translate(-(_bb[0] + _bb[2]) / 2, 0))
_breve_mark.transform(psMat.scale(0.5, 1))
_breve_mark.transform(psMat.translate(-220, 0))

# --- Marks composed from existing mark glyphs ---

Expand Down Expand Up @@ -460,8 +459,11 @@ def _accented(cp, base_name, mark_name, gap=20, x_adj=0):
# Without GPOS anchors the renderer places marks at their native coordinates,
# so we translate here to ensure they appear above even tall letters like l/L/b/h.
# (Pre-composed glyphs are unaffected — _place_above computes its own dy.)
_ascender_top = font['l'].boundingBox()[3]
# Inter-stage contract: all above combining marks sit on top-center of _typical_bbox
_typical_bbox = font['_typical_bbox'].boundingBox()
_ascender_top = _typical_bbox[3]
_combining_gap = 20
_center_top = (_typical_bbox[0] + _typical_bbox[2]) / 2 - font['_typical_bbox'].width

for cp, mark in [
(0x0300, _grave_mark),
Expand All @@ -480,36 +482,37 @@ def _accented(cp, base_name, mark_name, gap=20, x_adj=0):
c.clear()
mark_bb = font[mark.glyphname].boundingBox()
dy = _ascender_top + _combining_gap - mark_bb[1]
c.addReference(mark.glyphname, psMat.translate(0, dy))
c.addReference(mark.glyphname, psMat.translate(_center_top, dy))
c.width = 0

# Below combining marks share the macron-below shape at different vertical positions.
_descender_bottom = font['p'].boundingBox()[1]
_descender_bottom = font['_typical_bbox'].boundingBox()[1]
_mb_bb = font['_macron_below_mark'].boundingBox()
_center_bottom = (_typical_bbox[0] + _typical_bbox[2]) / 2 - font['_typical_bbox'].width

# U+0331 ◌̱ COMBINING MACRON BELOW — below the descender.
_c0331 = font.createMappedChar(0x0331)
_c0331.clear()
_c0331.addReference('_macron_below_mark', psMat.translate(0, _descender_bottom - _combining_gap - _mb_bb[3]))
_c0331.addReference('_macron_below_mark', psMat.translate(_center_bottom, _descender_bottom - _combining_gap - _mb_bb[3]))
_c0331.width = 0

# U+0332 ◌̲ COMBINING LOW LINE — just below the baseline (underline position).
_c0332 = font.createMappedChar(0x0332)
_c0332.clear()
_c0332.addReference('_macron_below_mark', psMat.translate(0, -_combining_gap - _mb_bb[3]))
_c0332.addReference('_macron_below_mark', psMat.translate(_center_bottom, -_combining_gap - _mb_bb[3]))
_c0332.width = 0

# U+0320 ◌̠ COMBINING MINUS SIGN BELOW — halfway between baseline and descender.
_c0320 = font.createMappedChar(0x0320)
_c0320.clear()
_c0320.addReference('_macron_below_mark', psMat.translate(0, _descender_bottom // 2 - _combining_gap - _mb_bb[3]))
_c0320.addReference('_macron_below_mark', psMat.translate(_center_bottom, _descender_bottom // 2 - _combining_gap - _mb_bb[3]))
_c0320.width = 0

# U+0327 ◌̧ COMBINING CEDILLA — hook cedilla shape, positioned below descender.
_hc_bb = font['_hook_cedilla_mark'].boundingBox()
_c0327 = font.createMappedChar(0x0327)
_c0327.clear()
_c0327.addReference('_hook_cedilla_mark', psMat.translate(0, _descender_bottom - _combining_gap - _hc_bb[3]))
_c0327.addReference('_hook_cedilla_mark', psMat.translate(_center_bottom, _descender_bottom - _combining_gap - _hc_bb[3]))
_c0327.width = 0


Expand Down
58 changes: 48 additions & 10 deletions xkcd-script/generator/pt7_font_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,30 +173,39 @@ def getkern(left, right):
font.addLookup('above', 'gpos_mark2base', (), [['mark', [['latn', ['dflt']]]]])
font.addLookupSubtable('above', 'above_sub')
font.addAnchorClass('above_sub', 'above')
font.addLookup('mkmk', 'gpos_mark2mark', (), [['mkmk', [['latn', ['dflt']]]]], 'above')
font.addLookupSubtable('mkmk', 'abovechain_sub')
font.addAnchorClass('abovechain_sub', 'abovechain')

# Combining mark codepoints registered in pt6, paired with their private glyph names.
# The anchor sits at the bottom-centre of each combining mark glyph, adjusted by
# a per-mark y offset: positive = mark sits lower (less gap), negative = higher (more gap).
_COMBINING = [
(0x0300, '_grave_mark', 0),
(0x0301, '_acute_mark', 0),
(0x0302, '_circumflex_mark', 300), # was too high
(0x0303, '_tilde_mark', 270), # was too high
(0x0304, '_macron_mark', -90), # was too low
(0x0307, '_dot_above_mark', -30), # too low → raise
(0x0302, '_circumflex_mark', 0),
(0x0303, '_tilde_mark', 0),
(0x0304, '_macron_mark', 0),
(0x0307, '_dot_above_mark', 0),
(0x0308, '_diaeresis_mark', 0),
(0x030A, '_ring_above_mark', 45), # could be a little closer
(0x030A, '_ring_above_mark', 0),
(0x030B, '_double_acute_mark', 0),
(0x030C, '_caron_mark', 280), # was too high
(0x030C, '_caron_mark', 0),
(0x0306, '_breve_mark', 0),
]

# Inter-stage contract: all above combining marks sit on top-center of _typical_bbox.
# "Sit on" doesn't necessarily mean centering the bounding box. That design decision was
# made in Stage 6, and here we just make sure not to mess up the results in the x direction.
_typical_bbox = font['_typical_bbox'].boundingBox()
cx = (_typical_bbox[0] + _typical_bbox[2]) / 2 - font['_typical_bbox'].width
_MKMK_GAP = 20
for cp, private_name, y_offset in _COMBINING:
mark_glyph = font[cp]
# Use the private mark glyph's bbox (the encoded glyph is a composite whose
# bbox FontForge may not resolve; the private mark is a plain outline).
bb = font[private_name].boundingBox()
cx = (bb[0] + bb[2]) / 2
bb = font[cp].boundingBox()
mark_glyph.addAnchorPoint('above', 'mark', cx, bb[1] + y_offset)
mark_glyph.addAnchorPoint('abovechain', 'mark', cx, bb[1] + y_offset)
mark_glyph.addAnchorPoint('abovechain', 'basemark', cx, bb[3] + _MKMK_GAP)

# j's dot is to the right of the body centre; combining marks should sit above the dot.
_j_layer = font['j'].foreground
Expand All @@ -219,6 +228,35 @@ def getkern(left, right):
glyph.addAnchorPoint('above', 'base', cx, bb[3] + _BASE_GAP)


# ---------------------------------------------------------------------------
# ccmp GSUB: replacement rules that take priority over mark2base for dotlessi and dotleesj
# ---------------------------------------------------------------------------

font.addLookup('above_subst', 'gsub_ligature', (), [['ccmp', [['latn', ['dflt']]]]])
font.addLookupSubtable('above_subst', 'ccmp')

composition_tuple = tuple(['i', 'acutecomb'])
font[0x00ED].addPosSub('ccmp', composition_tuple) # í
composition_tuple = tuple(['i', 'uni030C'])
font[0x01D0].addPosSub('ccmp', composition_tuple) # ǐ
composition_tuple = tuple(['j', 'uni030C'])
font[0x01F0].addPosSub('ccmp', composition_tuple) # ǰ
composition_tuple = tuple(['i', 'uni0306'])
font[0x012D].addPosSub('ccmp', composition_tuple) # ĭ
composition_tuple = tuple(['i', 'uni0302'])
font[0x00EE].addPosSub('ccmp', composition_tuple) # î
composition_tuple = tuple(['j', 'uni0302'])
font[0x0135].addPosSub('ccmp', composition_tuple) # ĵ
composition_tuple = tuple(['i', 'gravecomb'])
font[0x00EC].addPosSub('ccmp', composition_tuple) # ì
composition_tuple = tuple(['i', 'tildecomb'])
font[0x0129].addPosSub('ccmp', composition_tuple) # ĩ
composition_tuple = tuple(['i', 'uni0308'])
font[0x00EF].addPosSub('ccmp', composition_tuple) # ï
composition_tuple = tuple(['i', 'uni0304'])
font[0x012B].addPosSub('ccmp', composition_tuple) # ī


# ---------------------------------------------------------------------------
# Mark-to-base GPOS: position combining cedilla below base glyphs
# ---------------------------------------------------------------------------
Expand Down
Binary file modified xkcd-script/samples/charmap_combining_diacritical_marks.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/charmap_latin_extended_a.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/handwriting.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/accents-zoo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/align-eqref.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/binomial-theorem.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/column-vector.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/deeply-nested-sqrt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/derivative-definition.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/display-euler-product.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/display-int-exp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/display-sum-closed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/eulers-identity.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/fourier-transform.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/gaussian-integral.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/greek-letters.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/inline-ops.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/math-symbols-zoo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/matrix-3x3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/maxwell-equations.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/maxwell-free-space.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/multiplication-table.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/piecewise-abs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/piecewise-big.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/piecewise-single.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified xkcd-script/samples/mathjax3/product-rule.png
Binary file modified xkcd-script/samples/mathjax3/pythagoras.png
Binary file modified xkcd-script/samples/mathjax3/quadratic-formula.png
Binary file modified xkcd-script/samples/mathjax3/ramanujan-identity.png
Binary file modified xkcd-script/samples/mathjax3/schrodinger.png
Binary file modified xkcd-script/samples/mathjax3/sqrt-digit-radicand.png
Binary file modified xkcd-script/samples/mathjax3/stretchy-arrows.png
Binary file modified xkcd-script/samples/mathjax3/tall-integral-frac.png
Binary file modified xkcd-script/samples/mathjax3/tall-integral-sum.png
Loading