Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
975d0cf
Replace hand-tuned kern pairs with HalfKern auto-kerning
jul-sh Mar 19, 2026
155f4ff
Update flake.lock (nixpkgs 2026-03-18, rust-overlay 2026-03-19)
jul-sh Mar 19, 2026
a6cb566
Enable HarfBuzz/raqm layout in specimen image scripts
jul-sh Mar 19, 2026
286438d
Fix CI: checkout halfkern submodule in test job
jul-sh Mar 19, 2026
72991c9
Fix kern feature not visible to shapers (missing ScriptList registrat…
jul-sh Mar 19, 2026
d6ab50e
Give HalfKern more freedom: positive kerns, lower threshold, wider clamp
jul-sh Mar 19, 2026
02b0818
Regenerate images with relaxed kern settings
jul-sh Mar 19, 2026
99f8db7
Disable kern halving — use full SDF-computed kern values
jul-sh Mar 19, 2026
3d1171f
Use reduce=max and negative-only kerns for tighter, optical kerning
jul-sh Mar 19, 2026
5c1c37d
Restore positive kerns alongside negative for full auto-kerning range
jul-sh Mar 19, 2026
977c207
Revert "Restore positive kerns alongside negative for full auto-kerni…
jul-sh Mar 19, 2026
bb206e0
Keep positive kerns but halve them for conservative loosening
jul-sh Mar 19, 2026
cea10ef
Reduce positive kerns to 30% of computed value
jul-sh Mar 19, 2026
f7569ae
init autokern properly
jul-sh Mar 19, 2026
edb4449
Fix cairoft bugs, expand kern ranges, add submodule check
jul-sh Mar 19, 2026
3558ba0
Point halfkern submodule at fork with cairoft fixes
jul-sh Mar 19, 2026
2fcbe4a
Suppress positive kerns for thin punctuation marks
jul-sh Mar 19, 2026
fb0461a
Add quotes to thin punctuation suppression list and regen images
jul-sh Mar 19, 2026
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
2 changes: 2 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ jobs:
needs: validate-environment
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v14
- name: Nix cache
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "scripts/halfkern"]
path = scripts/halfkern
url = https://github.com/jul-sh/halfkern.git
Binary file modified documentation/font-comparison.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions documentation/font-comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

def render_text_to_array(text, font_path, size):
"""Render text to a numpy array for pixel-level comparison"""
font = ImageFont.truetype(font_path, size)
font = ImageFont.truetype(font_path, size, layout_engine=ImageFont.Layout.RAQM)

# Create temporary image to measure text
temp_img = Image.new("L", (1, 1))
Expand Down Expand Up @@ -100,9 +100,9 @@ def calc_height():

# Title
try:
title_font = ImageFont.truetype(f"{FONT_DIR_1}/IosevkaCharon-Bold.ttf", TITLE_SIZE)
subtitle_font = ImageFont.truetype(f"{FONT_DIR_1}/IosevkaCharon-Regular.ttf", SUBTITLE_SIZE)
legend_font = ImageFont.truetype(f"{FONT_DIR_1}/IosevkaCharon-Regular.ttf", 18)
title_font = ImageFont.truetype(f"{FONT_DIR_1}/IosevkaCharon-Bold.ttf", TITLE_SIZE, layout_engine=ImageFont.Layout.RAQM)
subtitle_font = ImageFont.truetype(f"{FONT_DIR_1}/IosevkaCharon-Regular.ttf", SUBTITLE_SIZE, layout_engine=ImageFont.Layout.RAQM)
legend_font = ImageFont.truetype(f"{FONT_DIR_1}/IosevkaCharon-Regular.ttf", 18, layout_engine=ImageFont.Layout.RAQM)
except:
print("Warning: Could not load title fonts, using default")
title_font = ImageFont.load_default()
Expand Down
Binary file modified documentation/iosevka-charon-mono.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 4 additions & 3 deletions documentation/iosevka-charon-mono.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@
ORANGE = (206, 145, 80) # brighter orange
TEAL = (78, 201, 162) # brighter teal

regular = ImageFont.truetype(FONTS["regular"], FONT_SIZE)
bold = ImageFont.truetype(FONTS["bold"], FONT_SIZE)
small = ImageFont.truetype(FONTS["regular"], SMALL_SIZE)
RAQM = ImageFont.Layout.RAQM
regular = ImageFont.truetype(FONTS["regular"], FONT_SIZE, layout_engine=RAQM)
bold = ImageFont.truetype(FONTS["bold"], FONT_SIZE, layout_engine=RAQM)
small = ImageFont.truetype(FONTS["regular"], SMALL_SIZE, layout_engine=RAQM)


# Calculate height
Expand Down
Binary file modified documentation/iosevka-charon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 6 additions & 5 deletions documentation/iosevka-charon.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@
WHITE = (255, 255, 255)
GRAY = (140, 140, 140)

regular = ImageFont.truetype(FONTS["regular"], FONT_SIZE)
italic = ImageFont.truetype(FONTS["italic"], FONT_SIZE)
bold = ImageFont.truetype(FONTS["bold"], FONT_SIZE)
title = ImageFont.truetype(FONTS["regular"], TITLE_SIZE)
small = ImageFont.truetype(FONTS["regular"], SMALL_SIZE)
RAQM = ImageFont.Layout.RAQM
regular = ImageFont.truetype(FONTS["regular"], FONT_SIZE, layout_engine=RAQM)
italic = ImageFont.truetype(FONTS["italic"], FONT_SIZE, layout_engine=RAQM)
bold = ImageFont.truetype(FONTS["bold"], FONT_SIZE, layout_engine=RAQM)
title = ImageFont.truetype(FONTS["regular"], TITLE_SIZE, layout_engine=RAQM)
small = ImageFont.truetype(FONTS["regular"], SMALL_SIZE, layout_engine=RAQM)


# Calculate height
Expand Down
Binary file modified documentation/multilingual-test.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 4 additions & 3 deletions documentation/multilingual-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@
WHITE = (255, 255, 255)
GRAY = (140, 140, 140)

regular = ImageFont.truetype(FONTS["regular"], FONT_SIZE)
bold = ImageFont.truetype(FONTS["bold"], FONT_SIZE)
title = ImageFont.truetype(FONTS["bold"], TITLE_SIZE)
RAQM = ImageFont.Layout.RAQM
regular = ImageFont.truetype(FONTS["regular"], FONT_SIZE, layout_engine=RAQM)
bold = ImageFont.truetype(FONTS["bold"], FONT_SIZE, layout_engine=RAQM)
title = ImageFont.truetype(FONTS["bold"], TITLE_SIZE, layout_engine=RAQM)

# Multilingual test text
multilingual_text = [
Expand Down
12 changes: 6 additions & 6 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@
unicodedata2
uharfbuzz

# Auto-kerning (HalfKern) dependencies
pycairo
numpy
scipy
scikit-fmm

# Async concurrency
trio
outcome
Expand Down Expand Up @@ -153,6 +159,10 @@
# Python environment with all font tools
pythonEnv

# System libraries needed by HalfKern (cairoft.py dlopen)
cairo
freetype

# Other useful tools
git
which
Expand All @@ -161,6 +171,9 @@

shellHook = ''
export PYTHONPATH="${pythonEnv}/${pythonEnv.sitePackages}"
# Make native libs findable by HalfKern's cairoft.py (ctypes.CDLL)
export DYLD_FALLBACK_LIBRARY_PATH="${pkgs.cairo}/lib:${pkgs.freetype}/lib:''${DYLD_FALLBACK_LIBRARY_PATH:-}"
export LD_LIBRARY_PATH="${pkgs.cairo}/lib:${pkgs.freetype}/lib:''${LD_LIBRARY_PATH:-}"
# Workaround for protobuf compatibility with gflanguages
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
# Install trio-parallel (not available in nixpkgs)
Expand Down
Loading
Loading