Skip to content

Commit 138aa67

Browse files
committed
Add Ł, ł, Ø, ø, Đ đ, Ħ, ħ, Ŧ, ŧ and Ð
1 parent f537506 commit 138aa67

8 files changed

Lines changed: 725 additions & 363 deletions

File tree

xkcd-script/font/xkcd-script.otf

3.41 KB
Binary file not shown.

xkcd-script/font/xkcd-script.sfd

Lines changed: 608 additions & 355 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

xkcd-script/font/xkcd-script.ttf

2.22 KB
Binary file not shown.

xkcd-script/font/xkcd-script.woff

2.15 KB
Binary file not shown.

xkcd-script/generator/pt6_derived_chars.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
Reads the base SFD produced by pt4_svg_to_font.py, adds derived glyphs, saves.
77
"""
8+
import math
89
import os
910
import fontforge
1011
import psMat
@@ -367,6 +368,111 @@ def _accented(cp, base_name, mark_name, gap=20, x_adj=0):
367368
_make_dstroke(font, 0x013E, 'l', gap=-50, width_extra=80)
368369
_make_dstroke(font, 0x013D, 'L', gap=-50, dy_offset=100, width_extra=80)
369370

371+
# ---------------------------------------------------------------------------
372+
# L with stroke: Ł U+0141 / ł U+0142
373+
# ---------------------------------------------------------------------------
374+
375+
def _make_l_crossbar_mark(font, name, bar_width, rotation=0):
376+
"""Scaled hyphen stroke centered at origin (x=0, y=0), zero-width mark.
377+
378+
rotation: counter-clockwise angle in degrees.
379+
"""
380+
hyp_bb = font['hyphen'].boundingBox()
381+
hyp_cx = (hyp_bb[0] + hyp_bb[2]) / 2
382+
hyp_cy = (hyp_bb[1] + hyp_bb[3]) / 2
383+
sx = bar_width / (hyp_bb[2] - hyp_bb[0])
384+
mark = font.createChar(-1, name)
385+
mark.clear()
386+
layer = fontforge.layer()
387+
for c in font['hyphen'].foreground:
388+
layer += c
389+
mark.foreground = layer
390+
mark.transform(psMat.compose(
391+
psMat.scale(sx, 1.0),
392+
psMat.translate(-hyp_cx * sx, -hyp_cy),
393+
))
394+
if rotation:
395+
mark.transform(psMat.rotate(math.radians(rotation)))
396+
# Re-centre after rotation: the bounding box shifts if the source shape is asymmetric.
397+
bb = mark.boundingBox()
398+
mark.transform(psMat.translate(-((bb[0] + bb[2]) / 2), -((bb[1] + bb[3]) / 2)))
399+
mark.width = 0
400+
return mark
401+
402+
403+
def _make_lslash(font, cp, base_name, crossbar_name, y_frac, x_center):
404+
"""L-with-stroke: base glyph + crossbar mark at (x_center, y_frac * height)."""
405+
c = font.createMappedChar(cp)
406+
c.clear()
407+
c.addReference(base_name)
408+
c.width = font[base_name].width
409+
base_bb = font[base_name].boundingBox()
410+
target_y = base_bb[1] + (base_bb[3] - base_bb[1]) * y_frac
411+
c.addReference(crossbar_name, psMat.translate(x_center, target_y))
412+
return c
413+
414+
415+
_l_crossbar = _make_l_crossbar_mark(font, '_l_crossbar', bar_width=300, rotation=40)
416+
417+
# Ł U+0141 — crossbar at 42% of cap height, centered on the vertical stroke (x≈75)
418+
_make_lslash(font, 0x0141, 'L', '_l_crossbar', y_frac=0.42, x_center=75)
419+
# ł U+0142 — crossbar at 60% of ascender height, l's stroke is at x≈75
420+
_make_lslash(font, 0x0142, 'l', '_l_crossbar', y_frac=0.60, x_center=75)
421+
422+
423+
# ---------------------------------------------------------------------------
424+
# O with stroke: Ø U+00D8 / ø U+00F8
425+
# ---------------------------------------------------------------------------
426+
427+
def _make_oslash(font, cp, base_name, crossbar_name, y_offset=0):
428+
"""O-with-stroke: base glyph + crossbar centered on its bounding box.
429+
430+
y_offset: shift the crossbar up (positive) or down (negative) from centre.
431+
"""
432+
c = font.createMappedChar(cp)
433+
c.clear()
434+
c.addReference(base_name)
435+
c.width = font[base_name].width
436+
base_bb = font[base_name].boundingBox()
437+
cx = (base_bb[0] + base_bb[2]) / 2
438+
cy = (base_bb[1] + base_bb[3]) / 2
439+
c.addReference(crossbar_name, psMat.translate(cx, cy + y_offset))
440+
return c
441+
442+
443+
# bar_width sized to span the oval diagonal plus a small overhang on each side:
444+
# O: bbox ≈450×580 → diagonal ≈735; o: bbox ≈365×420 → diagonal ≈557
445+
_cap_o_crossbar = _make_l_crossbar_mark(font, '_cap_o_crossbar', bar_width=720, rotation=55)
446+
_lc_o_crossbar = _make_l_crossbar_mark(font, '_lc_o_crossbar', bar_width=558, rotation=55)
447+
448+
_make_oslash(font, 0x00D8, 'O', '_cap_o_crossbar', y_offset=30) # Ø
449+
_make_oslash(font, 0x00F8, 'o', '_lc_o_crossbar', y_offset=15) # ø
450+
451+
452+
# ---------------------------------------------------------------------------
453+
# D/d/H/h/T/t with stroke (all horizontal bars, rotation=0)
454+
# ---------------------------------------------------------------------------
455+
456+
_bar_300 = _make_l_crossbar_mark(font, '_bar_300', bar_width=300) # D/d/Ð
457+
_bar_220 = _make_l_crossbar_mark(font, '_bar_220', bar_width=220) # T/t/h
458+
_bar_480 = _make_l_crossbar_mark(font, '_bar_480', bar_width=480) # H (spans past both uprights)
459+
460+
# Đ U+0110 — bar through D's left vertical stem (like Ł for L)
461+
_make_lslash(font, 0x0110, 'D', '_bar_300', y_frac=0.50, x_center=145)
462+
# đ U+0111 — bar through d's right ascending stem
463+
_make_lslash(font, 0x0111, 'd', '_bar_300', y_frac=0.78, x_center=360)
464+
# Ħ U+0126 — bar spanning past both H uprights, centred on full glyph width
465+
_make_lslash(font, 0x0126, 'H', '_bar_480', y_frac=0.85, x_center=239)
466+
# ħ U+0127 — bar through h's left ascending stem
467+
_make_lslash(font, 0x0127, 'h', '_bar_220', y_frac=0.85, x_center=70)
468+
# Ŧ U+0166 — bar through T's vertical stem
469+
_make_lslash(font, 0x0166, 'T', '_bar_220', y_frac=0.45, x_center=194)
470+
# ŧ U+0167 — bar through t's stem, below t's existing crossbar
471+
_make_lslash(font, 0x0167, 't', '_bar_220', y_frac=0.35, x_center=148)
472+
# Ð U+00D0 — Eth: like Đ but slightly higher (upper curve of D)
473+
_make_lslash(font, 0x00D0, 'D', '_bar_300', y_frac=0.58, x_center=145)
474+
475+
370476
# Circumflex: Â Ê Î Ô Û / â ê î ô û + Esperanto Ĉ Ĝ Ĥ Ĵ Ŝ + Welsh Ŵ Ŷ
371477
for cp, base in [
372478
(0x00C2, 'A'), (0x00CA, 'E'), (0x00CE, 'I'), (0x00D4, 'O'), (0x00DB, 'U'),

xkcd-script/generator/run.sh

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
55
cd ${DIR}
66
RUN_CTXT="docker run --rm -u $(id -u) -v $(pwd)/../:$(pwd)/../ -w $(pwd) -e LC_ALL=en_US.UTF-8 ${IMAGE}"
77

8+
FROM=${1:-1}
9+
810
set -ex
911

1012
# Following @pelson's field notes at https://pelson.github.io/2017/xkcd_font/
11-
$RUN_CTXT python3 pt1_character_extraction.py
12-
$RUN_CTXT python3 pt2_character_classification.py
13-
$RUN_CTXT python3 pt3_ppm_to_svg.py
14-
$RUN_CTXT python3 pt4_svg_to_font.py
15-
$RUN_CTXT python3 pt5_additional_sources.py
16-
$RUN_CTXT python3 pt6_derived_chars.py
17-
$RUN_CTXT python3 pt7_font_properties.py
18-
$RUN_CTXT python3 pt8_gen_reprod_font.py
13+
# Pass a part number as the first argument to start from that step, e.g. ./run.sh 6
14+
[ "$FROM" -le 1 ] && $RUN_CTXT python3 pt1_character_extraction.py
15+
[ "$FROM" -le 2 ] && $RUN_CTXT python3 pt2_character_classification.py
16+
[ "$FROM" -le 3 ] && $RUN_CTXT python3 pt3_ppm_to_svg.py
17+
[ "$FROM" -le 4 ] && $RUN_CTXT python3 pt4_svg_to_font.py
18+
[ "$FROM" -le 5 ] && $RUN_CTXT python3 pt5_additional_sources.py
19+
[ "$FROM" -le 6 ] && $RUN_CTXT python3 pt6_derived_chars.py
20+
[ "$FROM" -le 7 ] && $RUN_CTXT python3 pt7_font_properties.py
21+
[ "$FROM" -le 8 ] && $RUN_CTXT python3 pt8_gen_reprod_font.py
851 Bytes
Loading
1.86 KB
Loading

0 commit comments

Comments
 (0)