Skip to content

Conversation

@oetiker
Copy link
Contributor

@oetiker oetiker commented Jan 10, 2026

Supersedes #922 (nicoburns' "Port resvg to harfrust and fontations")

This patch builds upon the excellent groundwork laid out in PR #922, which identified harfrust and skrifa as the path forward for long-term maintainability. As noted in that discussion, these actively-maintained libraries offer significant advantages: harfrust passes substantially more shaping tests than rustybuzz, and skrifa provides a modern, well-designed API for font parsing.

Core Migration (shared with #922)

  • Replace rustybuzz with harfrust for OpenType text shaping
  • Replace ttf-parser with skrifa for font parsing and metrics
  • Update font metrics extraction to use skrifa's MetadataProvider

Additional Changes

  • Vendor fontdb as a workspace crate, ported to use skrifa (eliminates ttf-parser entirely)

New Features

1. Font Hinting Support

  • Implement hinted outline extraction via skrifa's HintingInstance
  • Global hinting toggle via HintingOptions in parser options
  • CSS text-rendering property controls per-element hinting:
    • geometricPrecision: disables hinting (preserve exact outlines)
    • optimizeLegibility / optimizeSpeed: enables full hinting

Custom Hinting Properties (resvg extensions)

Fine-grained control via custom CSS properties or SVG attributes:

Property Values Default Description
-resvg-hinting-target smooth, mono smooth Rasterization target
-resvg-hinting-mode normal, light, lcd, vertical-lcd normal Hinting strength
-resvg-hinting-engine auto, native, auto-fallback auto-fallback Hinting engine
-resvg-hinting-symmetric true, false true Symmetric rendering
-resvg-hinting-preserve-linear-metrics true, false false Preserve glyph spacing

Usage as SVG attributes (with resvg: namespace) or CSS properties (with -resvg- prefix):

<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:resvg="https://github.com/linebender/resvg">
  <!-- As attributes -->
  <text resvg:hinting-target="mono">Crisp text</text>
  
  <!-- As CSS -->
  <text style="-resvg-hinting-target: mono;">Crisp text</text>
</svg>

2. Full Variable Font Support

  • Parse font-variation-settings CSS property
  • Parse font-optical-sizing CSS property (auto/none)
  • Auto-map standard properties to variation axes:
    • font-weightwght axis
    • font-stretchwdth axis
    • font-style: italicital axis
    • font-style: obliqueslnt axis
  • Auto-set opsz axis based on font size when font-optical-sizing: auto
  • Store per-glyph variation context in PositionedGlyph

3. Bitmap Font Mask Support

  • Add support for grayscale bitmap masks (1/2/4/8 bpp) in CBDT/CBLC tables
  • Proper mask-to-PNG conversion for monochrome bitmap glyphs

4. PNG Output with DPI Metadata

New public API for PNG encoding with resolution metadata:

  • resvg::encode_png_with_dpi() - Encode pixmap with DPI in pHYs chunk
  • resvg::save_png_with_dpi() - Save pixmap with DPI metadata

Useful for print output and e-ink displays where physical resolution matters.

5. Enhanced COLR Support

  • Implement skrifa's ColorPainter trait for SVG output
  • Support COLRv1 features: transforms, clips, composite modes
  • Handle linear, radial gradients with proper spread methods
  • New modular implementation in skrifa_colr.rs

Documentation

  • Updated module-level docs for usvg and resvg crates
  • Comprehensive documentation for all new types:
    • HintingSettings, HintingTarget, HintingMode, HintingEngine
    • FontVariation, FontOpticalSizing
    • HintingOptions
  • Usage examples for custom hinting properties
  • Example: generate_hinting_comparison.rs

Test Infrastructure

  • Add tests-hinted/ directory with hinted reference images
  • Add font-variation-settings test suite
  • Include RobotoFlex variable font for variation testing

Trade-offs

We appreciate the careful analysis in #922 regarding the ecosystem trade-offs, particularly around compile times and binary size. This implementation accepts those trade-offs in exchange for the additional capabilities (hinting, variable fonts) that skrifa enables.


🤖 Generated with Claude Code

@oetiker oetiker force-pushed the skrifa branch 8 times, most recently from 8ec6bd8 to 6662a49 Compare January 10, 2026 23:13
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy fontdb 0.23.0 source code into crates/fontdb/ as a first step toward
replacing ttf-parser with skrifa for font metadata parsing.

Source: https://github.com/RazrFalcon/fontdb (MIT licensed)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@oetiker oetiker force-pushed the skrifa branch 3 times, most recently from 8f31d4e to 6688502 Compare January 12, 2026 10:42
This eliminates the ttf-parser dependency by modifying the vendored
fontdb to use skrifa for font parsing. Key changes:

- Replace ttf_parser::fonts_in_collection with FileRef::new().len()
- Replace ttf_parser::RawFace with FontRef::from_index()
- Replace ttf_parser name table parsing with font.localized_strings()
- Replace OS/2 table parsing with font.attributes()
- Replace post table parsing with font.metrics() and font.post()
- Add local Language enum (simplified: EnglishUnitedStates + Unknown)
- Add local Stretch enum with to_number() for CSS matching
- Add stretch_from_skrifa() for skrifa::attribute::Stretch conversion
- Update usvg to use vendored fontdb via path dependency
- Update Rust 2024 edition patterns (remove redundant ref bindings)

The MAC_ROMAN encoding table is removed as skrifa handles all name
table encodings internally via LocalizedString::chars().

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix incorrect safety comment on mmap (fontdb)
- Replace deprecated core::u64::MAX with u64::MAX (fontdb)
- Use .first() instead of .get(0) (fontdb)
- Fix potential panics on empty slices in font fallback (usvg/text/mod.rs)
- Add empty path check in path_length() (usvg/parser/text.rs)
- Add ASCII validation for font variation tags (usvg/parser/text.rs)
- Use idiomatic .or() instead of .map_or() (usvg/parser/text.rs)
- Return &TextFlow instead of clone in text_flow() (usvg/tree/text.rs)
- Add logging for checked_sub failure (usvg/text/layout.rs)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move `variations`, `font_optical_sizing`, and `hinting` fields from
`PositionedGlyph` to `Span` struct since these values are uniform for
all glyphs within a span.

Benefits:
- Reduces memory by not duplicating Vec<FontVariation> per glyph
- Cleaner data model: span-level properties at span level
- Minor perf improvement: needs_variations check once per span

Based on PR linebender#997 review feedback.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
oetiker and others added 3 commits January 12, 2026 18:54
The empty --drop-tables= flag doesn't do anything. Remove it.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace the simple (font_id, glyph_id) HashMap cache with a comprehensive
LRU cache that captures all parameters affecting glyph outline shape:

- Font ID and glyph ID
- PPEM for hinting (stored as f32::to_bits() for exact matching)
- Hinting settings (target, mode, engine, symmetric, preserve_linear_metrics)
- Variation hash (computed from sorted variations + auto-opsz)

Key changes:
- Add `lru` crate dependency for bounded cache with LRU eviction
- Add OutlineCacheKey struct capturing all outline-affecting parameters
- Add CacheStats for tracking hits/misses/evictions
- Add compute_variation_hash() for consistent variation hashing
- Replace HashMap with LruCache (default capacity: 10,000 entries)
- Unify three code paths (simple/variable/hinted) into single cache lookup
- Add cache management methods (stats, clear, resize, len, capacity)

This ensures that hinted outlines and variable font outlines are now
cached, significantly improving performance for text-heavy documents
with repeated glyphs at the same settings.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Document all changes from the skrifa branch:
- Hinting support with CSS custom properties
- Variable font support with avar normalization
- LRU outline cache with full parameter support
- Port from ttf-parser/rustybuzz to skrifa/harfrust
- Vendored fontdb ported to skrifa
- Various bug fixes and improvements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@oetiker oetiker marked this pull request as draft January 14, 2026 19:26
@oetiker
Copy link
Contributor Author

oetiker commented Jan 14, 2026

I will be producing additional PRs based on this branch once the skrifa switch is merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant