Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

img2bez

CI Apache 2.0 or MIT license. Linebender Zulip chat.

img2bez traces raster glyph images into bézier outlines and writes them directly into UFO font sources.

Most autotracers reproduce a glyph's silhouette and leave the outline structure to a human. img2bez aims for the structure a font source needs: points at extrema with horizontal or vertical handles, straight segments as lines, points at inflections, smooth joins harmonized toward G2, and the minimum number of points that render the shape. Traces run in milliseconds on a CPU. Built on kurbo and norad from the Linebender ecosystem.

Read the blog post for the design rationale and an interactive in-browser demo.

img2bez: a raster glyph image being traced into a bézier outline with smooth points, corner points, and handles.

Install

Not yet published to crates.io. Install from GitHub for now:

cargo install --git https://github.com/eliheuer/img2bez            # the CLI
cargo add --git https://github.com/eliheuer/img2bez img2bez        # as a library

Quick start

# Trace a glyph image into a UFO source (created if it doesn't exist)
img2bez --input glyph.png --output MyFont.ufo --name A --unicode 0041

# With font metrics and grid snapping
img2bez --input glyph.png --output MyFont.ufo --name A --unicode 0041 \
  --target-height 1024 --y-offset -256 --grid 2

img2bez --help is the authoritative flag list, including the masters (interpolation-compatible variable-font masters) and new-font subcommands. Key flags: --profile wild|clean|photo (source-class preset), --accuracy (fit tolerance, font units), --mode smooth|line (shape constraint), --format ufo|glif|json|svg.

Library usage

use std::path::Path;

use img2bez::{trace_glyph_path, FontMetrics, Profile, TraceOptions};

let opts = TraceOptions::for_profile(Profile::Clean)
    .with_em_height(1088.0)
    .with_grid(2);
let metrics = FontMetrics::from_target_height(1088.0, -256.0)
    .with_advance(600.0);

let glyph = trace_glyph_path(
    Path::new("glyph.png"), "A", &['A'], &opts, &metrics,
)?;

// The same Glyph model serializes to GLIF, JSON, or SVG.
std::fs::write("A.glif", glyph.to_glif())?;

# Ok::<(), Box<dyn std::error::Error>>(())

Designed to embed: trace_glyph takes image bytes and returns a Glyph with no filesystem access; output is deterministic (cache by input hash); options are per call; a lean build (--no-default-features --features ufo) drops the CLI and renderer dependencies. WASM bindings run the identical pipeline in the browser; mcp/ exposes it to AI agents as an MCP server.

How it works

 Input image (grayscale)
      |
 1. Sub-pixel boundary extraction — marching squares at the
    threshold iso-level; the anti-aliasing carries ~0.1px accuracy
 2. Structure detection — corners, straights, tangent points,
    extrema, inflections from the curvature signal
 3. Constrained cubic fitting — tangent directions fixed (exactly
    H/V at extrema), handle lengths solved, candidate structures
    scored against the source pixels
 4. Font cleanup — contour direction, grid snapping, H/V handle
    correction, G2 harmonization, corner reconstruction
      |
 UFO / GLIF / JSON / SVG

Optional learned models (tiny decision heads, a few KB each, plain Rust inference) can replace individual structural judgment calls. They only choose between structures the procedural pipeline proposes — all drawing stays procedural — and they are off by default (IMG2BEZ_SITE_HEAD=1, or siteHead in the wasm config). The demo has a live rules-vs-learned toggle.

Tracing quality

The eval harness traces clean renders of a reference font and scores the result against the original UFO outlines (point count and placement, line/curve structure, H/V handles). Across basic Latin (a–z, A–Z, 0–9): mean structural score 0.964, 12 glyphs exact, 62/62 pass. Try your own images in the demo.

A structure-compare sheet: the source font's outline structure (left) next to img2bez's trace of a rendering of it (right) — identical point counts, smooth points, corners, and handles.

A structure-compare sheet: the source font's outlines (left), the trace of a 1024px rendering of them (right) — same 27 points, same structure.

Per-glyph scores
GlyphScoreGlyphScoreGlyphScoreGlyphScore
i1.000j0.98760.967D0.946
o1.000q0.98790.967S0.943
v1.000z0.986B0.965b0.939
E1.000P0.986g0.964p0.938
F1.000w0.982U0.964l0.934
H1.000J0.982n0.96020.934
I1.000W0.982h0.958Y0.931
L1.000x0.981u0.958s0.927
O1.000R0.981X0.958m0.922
T1.000C0.97850.958y0.918
V1.000a0.972d0.95700.915
11.00040.971Q0.957e0.909
80.993A0.969c0.954r0.863
f0.99130.968N0.95070.779
t0.991k0.967Z0.950
M0.990K0.967G0.948

Cargo features

FeatureDefaultDescription
ufoyesUFO output via norad
cliyesThe img2bez binary (implies ufo and render)
parallelyesPer-contour parallelism via rayon
renderyesPNG rendering and raster IoU via tiny-skia
Debug environment variables

Prefix a command with VAR=1 for a single run, e.g. IMG2BEZ_DEBUG_FIT=1 img2bez ...:

VariableEffect
IMG2BEZ_DEBUG_FITPipeline splits, fit errors, refine decisions
IMG2BEZ_DEBUG_CORNERSCorner decisions; site-head zone scores
IMG2BEZ_DEBUG_FLATSJunction-flat decisions
IMG2BEZ_DEBUG_SIMPLIFYPoint-merge accept/reject reasons
IMG2BEZ_DEBUG_TANGENT / _WELD / _ARCCHAINTangent, weld, arc-chain decisions
IMG2BEZ_DEBUG_NO_CLEANUPSkip all post-processing
IMG2BEZ_DEBUG_PIXELDIFFSave a 1:1 pixel diff image
IMG2BEZ_DEBUG_JOINT / _JOINT_EXTREMAJoint masters decisions
IMG2BEZ_PHOTO_FAIR_DEVPhoto-profile fairing threshold override
IMG2BEZ_LOG / IMG2BEZ_LOG_DECISIONSAppend JSONL trace/decision logs to a path (causes file writes)

IMG2BEZ_CORNER_HEAD=1 / IMG2BEZ_SITE_HEAD=1 are opt-ins, not debug switches: they enable the learned heads (the default pipeline is fully procedural).

Development

The tracer is developed against a measurable loop: render a reference font, trace it back, score the structural match. The harness and the structure-compare QA tool (side-by-side sheets like the one above, for any UFO) live in eval-harness/ (repository only, not in the published crate). Known gaps: docs/known-problems.md.

Minimum supported Rust version (MSRV)

img2bez has been verified to compile with Rust 1.88 and later.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.

关于 About

Trace raster images to font-ready bezier outlines — Rust library and CLI tool.
fontstype-designufo

语言 Languages

Rust88.3%
Python9.7%
Shell2.0%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
5
Total Commits
峰值: 4次/周
Less
More

核心贡献者 Contributors