|
5 | 5 |
|
6 | 6 | Reads the base SFD produced by pt4_svg_to_font.py, adds derived glyphs, saves. |
7 | 7 | """ |
| 8 | +import math |
8 | 9 | import os |
9 | 10 | import fontforge |
10 | 11 | import psMat |
@@ -367,6 +368,111 @@ def _accented(cp, base_name, mark_name, gap=20, x_adj=0): |
367 | 368 | _make_dstroke(font, 0x013E, 'l', gap=-50, width_extra=80) |
368 | 369 | _make_dstroke(font, 0x013D, 'L', gap=-50, dy_offset=100, width_extra=80) |
369 | 370 |
|
| 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 | + |
370 | 476 | # Circumflex: Â Ê Î Ô Û / â ê î ô û + Esperanto Ĉ Ĝ Ĥ Ĵ Ŝ + Welsh Ŵ Ŷ |
371 | 477 | for cp, base in [ |
372 | 478 | (0x00C2, 'A'), (0x00CA, 'E'), (0x00CE, 'I'), (0x00D4, 'O'), (0x00DB, 'U'), |
|
0 commit comments