# fontcore for Rust

fontcore is a Rust library for loading fonts, selecting a face, shaping text, and exporting SVG.

Japanese: [README.ja.md](README.ja.md)

## What To Use First

Most users only need these three types.

- `FontFile`
  - Opens a font file, TTC, or in-memory buffer
  - Lets you choose a face
- `FontFace`
  - Represents one face
  - Exposes metadata such as `family()`, `full_name()`, `weight()`, `is_italic()`
- `FontEngine`
  - Shapes text, measures text, and renders SVG
  - Main entry point for direction, locale, variant, and variable-font axes

The old low-level parser surface still exists behind `features = ["raw"]`.

## Supported Formats

- TrueType
- OpenType / CFF
- TTC
- WOFF
- WOFF2

Default features include `layout` and `cff`.

## Features

- `layout`
  - GSUB / GPOS shaping
  - Vertical flow, RTL, locale, and variant selection
- `cff`
  - OpenType / CFF outlines
- `raw`
  - Legacy low-level parser API
- `svg-fonts`
  - OpenType-SVG fonts are limited implementation
  - Provisional support for converting OpenType `SVG ` glyphs into glyph layers
  - Currently regression-tested mainly against `EmojiOneColor.otf` and `NotoColorEmoji-Regular.ttf`
  - Simple shapes are pathified, while payloads that cannot be pathified are kept as `GlyphLayer::Svg`
  - Full path conversion and CSS / text interpretation are not implemented yet

## Install

```toml
[dependencies]
fontcore = "0.0.12"
```

If you need the low-level parser API:

```toml
[dependencies]
fontcore = { version = "0.0.12", features = ["raw"] }
```

If you also want provisional SVG emoji font support:

```toml
[dependencies]
fontcore = { version = "0.0.12", features = ["svg-fonts"] }
```

## Quick Start

```rust
use fontcore::{FontFile, ShapingPolicy};

let face = FontFile::from_file("fonts/YourFont.ttf")?.current_face()?;
let engine = face
    .engine()
    .with_shaping_policy(ShapingPolicy::LeftToRight)
    .with_font_size(32.0)
    .with_svg_unit("px");

println!("{}", face.family());
println!("{}", engine.measure("Hello")?);
println!("{}", engine.render_svg("Hello")?);
println!("{}", engine.shape("Hello")?.glyphs.len());
# Ok::<(), Box<dyn std::error::Error>>(())
```

## Common Tasks

- Show metadata
  - `face.family()`
  - `face.full_name()`
  - `face.weight()`
  - `face.is_italic()`
- Shape text
  - `engine.shape(text)`
- Measure text
  - `engine.measure(text)`
- Render SVG
  - `engine.render_svg(text)`
- Vertical flow
  - `engine.with_vertical_flow()`
- RTL shaping
  - `engine.with_right_to_left()`
- GSUB variant selection
  - `engine.with_font_variant(...)`
- Variable-font axes
  - `face.variation_axes()`
  - `engine.with_variation("wght", 700.0)`

More runnable examples live in [doc/api-recipes.md](doc/api-recipes.md).

## About SVG Color Fonts

`sbix` is exposed as raster layers, `COLR/CPAL` as path layers, and the OpenType `SVG ` table is pathified first under `svg-fonts`, falling back to `Svg` layers only when needed.

The current `svg-fonts` implementation converts simple `path`, `rect`, `circle`, `ellipse`, `line`, `polyline`, and `polygon` elements into `PathGlyphLayer` values. It includes minimal `defs` / `use`, `fill` / `fill-rule` / `stroke` / `stroke-width`, `clipPath` / `clip-path`, simple `mask`, `translate` / `scale` / `rotate` / `skewX` / `skewY` / `matrix`, `linearGradient` / `radialGradient` / `stop`, and preserved `gradientUnits` / `gradientTransform` support, and keeps a `GlyphLayer::Svg` fallback not only for payloads that cannot be pathified, but also for payloads that still contain unsupported constructs such as `pattern`, complex `mask`, or `filter`.

On the 0.0.12 line, the `paintcore` renderer tracks the public `fontcore` layer model closely enough to preserve `clip_commands` and gradient paint values across the `fontcore -> paintcore` conversion.

See [doc/SVFONTSPEC.md](doc/SVFONTSPEC.md) for the exact current scope, limitations, and the `paintcore` handoff contract.

## Examples

High-level examples that work without `raw`:

- `api_overview`
- `fontmetadata`
- `fontcore`

Low-level inspection examples that require `--features raw`:

- `fontcmaps`
- `fontcolor`
- `fontgkana`
- `fontgsub`
- `fontheader`
- `fontload`
- `fontname`
- `fontsbix`
- `fontsvg`
- `fonttest`
- `fonttype`
- `tategaki`

Shared CLI flags:

- `-f`, `--font`: font path
- `-d`, `--dir`: font directory
- `-i`, `--index`: face index inside a collection
- `-o`, `--output`: output file path
- `-s`, `--string`: inline text
- `-t`, `--text-file`: text file path
- `--vertical`: top-to-bottom flow
- `--variant`: variant shortcut such as `jp78`, `jp90`, `trad`, `nlck`

Run a high-level example:

```bash
cargo run --example api_overview -- -f path/to/font.ttf -s "Hello"
```

Run a raw example:

```bash
cargo run --example fontheader --features raw -- -f path/to/font.otf
```

## `cargo doc`

The crate has rustdoc on the public API surface.

```bash
cargo doc --no-deps
```

If you want to open it immediately:

```bash
cargo doc --no-deps --open
```

The crate-level docs and type docs are the best entry point for:

- `FontFile`
- `FontFace`
- `FontEngine`
- `FontFamily`
- `FontVariant`
- `ShapingPolicy`

## WebAssembly

The crate supports `wasm32-unknown-unknown`.

- Prefer `load_font_from_buffer()` or `load_font(FontSource::Buffer(...))`
- `load_font_from_file()` and `load_font_from_net()` return `ErrorKind::Unsupported` on `wasm32`

## Documentation Map

- Overview of the extra docs: [doc/README.md](doc/README.md)
- Public API recipes: [doc/api-recipes.md](doc/api-recipes.md)
- Current implementation status and limitations: [doc/feature-status.md](doc/feature-status.md)
- CFF2 investigation notes: [doc/cff2-investigation.md](doc/cff2-investigation.md)
