pygraphite2
Cross-platform, fully typed Python binding for SIL Graphite2 text shaping.
pygraphite2 shapes complex scripts (Myanmar, Tai, many of the world's
orthographies that need smart-font rules) using the Graphite rendering
technology. It is a pure-Python package — it wraps SIL's official ctypes
binding and loads the native libgraphite2 at runtime, so there is no C/C++
compilation and no build toolchain required to install it.
import pygraphite2
font = open("Padauk-Regular.ttf", "rb").read()
glyphs = pygraphite2.shape(font, "မြန်မာ")
for g in glyphs:
print(f"gid={g.gid} cluster={g.cluster} advance={g.x_advance:.1f}")
Features
- Fully typed — ships a
py.typedmarker and complete inline annotations (mypy --strictclean on the package). - Cross-platform — Windows / macOS / Linux. The native library is discovered in a documented order (env var → system library → wheel-bundled → vendored), with Windows DLL-directory handling for MSYS2 runtime dependencies.
- No temp files — fonts are loaded fully in memory through a native table callback; nothing is ever written to disk.
- Rich, ergonomic API — glyph runs with advances/clusters, RTL support, script & language selection, feature overrides, and font metadata (units-per-em, glyph count, languages, feature enumeration).
- Graceful degradation — the pure-Python helpers
(
is_graphite_font,has_table,upem_from_ttf) work even without the native library; shaping calls raise a clear, actionablepygraphite2.LibraryNotFound.
Installation
pip install pygraphite2 # or: uv add pygraphite2
The wheel is a universal (py3-none-any) pure-Python wheel. It does not
bundle the native library by default; you must make libgraphite2 available
(see Native library). Platform-specific wheels that bundle
the library are planned for a future release (the loader already supports them
via pygraphite2/_lib/).
For development from source:
uv sync --extra dev # or: pip install -e ".[dev]"
pytest
ruff check .
mypy src
Native library
pygraphite2 locates libgraphite2 at runtime in this order (first hit wins):
| # | Source | How |
|---|---|---|
| 1 | pygraphite2.configure(path) |
explicit programmatic override |
| 2 | PYGRAPHITE2_LIBRARY_PATH env var |
path to a file or a directory |
| 3 | System library | ctypes.util.find_library("graphite2") |
| 4 | Wheel-bundled pygraphite2/_lib/ |
future platform wheels |
| 5 | Vendored checkout vendor/graphite2/ |
developer convenience |
Per-OS ways to obtain the library:
- Debian/Ubuntu:
sudo apt install libgraphite2-3 - conda-forge:
conda install graphite2 - macOS:
brew install graphite2(if a formula is available) or conda-forge - Windows: MSYS2 package
mingw-w64-x86_64-graphite2(dropgraphite2.dllplus its runtime DLLs intovendor/graphite2/), or conda-forge
Check what is loaded with pygraphite2.library_info():
>>> import pygraphite2
>>> pygraphite2.library_info()
'graphite2 v1.3.14 (D:\\...\\libgraphite2.dll)'
API
Shaping
pygraphite2.shape(font, text, *, direction="ltr", script=None, lang=None, features=None) -> list[Glyph]— one-shot shaping; returns the glyph run.pygraphite2.shape_segment(...) -> ShapedText— same, but returns the full run (glyphs+advance_x/advance_y+ metadata).pygraphite2.GraphiteFont(font, *, options=0)/from_bytes/from_path— a reusable font object; the recommended API when shaping many runs with the same font. Usable as a context manager.
font may be raw bytes or a path to a font file.
Glyph & ShapedText
Glyph is a NamedTuple:
| field | meaning |
|---|---|
gid |
glyph id in the font |
cluster |
source character index this glyph is associated with |
x_advance / y_advance |
advances in font units (from slot origins, matching gr2fonttest) |
x_offset / y_offset |
offsets in font units |
before / after |
source character range covered (inclusive/exclusive) |
slot_index |
slot index within the segment |
ShapedText adds advance_x, advance_y, text, direction, script.
Font inspection (pure Python — no native lib needed)
pygraphite2.is_graphite_font(font) -> bool— has aSilftable?pygraphite2.has_table(font, tag) -> boolpygraphite2.upem_from_ttf(font) -> intpygraphite2.read_font_bytes(font) -> bytes
Font metadata (needs the native lib)
On a GraphiteFont instance: upem, num_glyphs, languages,
feature_refs() (returns tuple[Feature, ...], each with tag and values).
Example with features and RTL
with pygraphite2.GraphiteFont.from_path("MyGraphite.ttf") as font:
shaped = font.shape(
"\u0645\u0627",
direction="rtl",
script="arab",
lang="urd",
features={"StylSet": 1},
)
print(shaped.advance_x)
Per-pass shaping trace (needs a tracing-enabled binary)
pygraphite2.shape_trace(font, text, ...) / GraphiteFont.shape_trace(...)
return a step-by-step shaping trace — one TraceStage per Graphite pass,
each a snapshot of the glyph run, bookended by "Start of shaping" (input glyphs)
and "End of shaping" (final glyphs). This is directly renderable by
Crowbar-style shaping debuggers (e.g. BabelMap's OpenType Test dialog):
import pygraphite2 as pg
trace = pg.shape_trace(open("Padauk-Regular.ttf", "rb").read(), "မြန်မာ", script="mymr")
for stage in trace.stages:
print(stage.m, [g.gid for g in stage.glyphs])
# Start of shaping [305, 392, 290, 383, 305, 354]
# Pass 1 ...
# ... the pass where reordering happens ...
# End of shaping [392, 305, 290, 383, 305, 354]
TraceStage.to_dict()/ShapedTrace.stages_to_dicts()serialize to the{m, glyphs, depth, effective}/{g, cl, dx, dy, ax, ay, flags}schema used by shaping-debug UIs.- Requires a graphite2 binary built with tracing support
(
GRAPHITE2_NTRACINGoff).GraphiteFont.tracing_supported()reports whether the loaded binary can trace; otherwiseshape_traceraisesTracingUnavailable.vendor/graphite2/ships tracing-enabled binaries for Windows and Linux, and.github/workflows/build-native.ymlbuilds + verifies tracing on macOS too — seevendor/graphite2/README.md. - Lower-level control:
GraphiteFont.start_logging(path)/stop_logging()wrap graphite2's Segment-JSON logging directly.
Error handling
All exceptions derive from pygraphite2.GraphiteError:
LibraryNotFound— no native library could be located/loaded.GraphiteFontError— invalid font data, missingSilftable, unknown feature.ShapingError— the native shaper failed.TracingUnavailable— a trace was requested but the binary has no tracing support.
Development
- Tests:
pytest— native tests auto-skip when the library is missing; golden tests auto-skip when the Padauk test font is missing (placePadauk-Regular.ttfinvendor/fonts/). - Lint:
ruff check .Format:ruff format . - Types:
mypy src(strict) - CI:
.github/workflows/ci.ymlruns the test matrix on Ubuntu/macOS/Windows and multiple Python versions;.github/workflows/build-native.ymlbuilds a tracing-enabled graphite2 from the pinned upstream source and verifiesshape_traceon all three OSes;.github/workflows/publish.ymlpublishes to PyPI via trusted publishing on version tags.
Licensing & attribution
- pygraphite2's own code is MIT-licensed.
- The vendored ctypes binding (
src/pygraphite2/_binding.py) is © 2013 SIL International and is distributed under its original multi-license (MIT OR MPL-2.0 OR GPL-2.0-or-later). SeeNOTICE.md.
The project's SPDX license expression is therefore
MIT OR MPL-2.0 OR GPL-2.0-or-later.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pygraphite2-0.3.0.tar.gz.
File metadata
- Download URL: pygraphite2-0.3.0.tar.gz
- Upload date:
- Size: 461.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2feacac8b14e0a4a5631761a4dda16a77c8ab9938eae4901661e5d9e2594a91a
|
|
| MD5 |
cc267b0679bccae7eababcacb098b622
|
|
| BLAKE2b-256 |
eb5d40d5b253e0c627847c9f504269e0d087667783feb95f22bf9f3c8dc83695
|
Provenance
The following attestation bundles were made for pygraphite2-0.3.0.tar.gz:
Publisher:
publish.yml on Kushim-Jiang/pygraphite2
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygraphite2-0.3.0.tar.gz -
Subject digest:
2feacac8b14e0a4a5631761a4dda16a77c8ab9938eae4901661e5d9e2594a91a - Sigstore transparency entry: 2659194893
- Sigstore integration time:
-
Permalink:
Kushim-Jiang/pygraphite2@ac9f32a05ce0c653f8896cab2fb0c0d1b5bf1d45 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Kushim-Jiang
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ac9f32a05ce0c653f8896cab2fb0c0d1b5bf1d45 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pygraphite2-0.3.0-py3-none-any.whl.
File metadata
- Download URL: pygraphite2-0.3.0-py3-none-any.whl
- Upload date:
- Size: 29.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2a66d6eced80dd89542596200b01e4c4687bba6c923b01ba52c4ddb44093f9e
|
|
| MD5 |
d963c01b5039a126dd7305bccf52f2e3
|
|
| BLAKE2b-256 |
e0c075f8003ebb7103b0476c4276936cd64d7b6df43018e407708025ed4349bc
|
Provenance
The following attestation bundles were made for pygraphite2-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on Kushim-Jiang/pygraphite2
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygraphite2-0.3.0-py3-none-any.whl -
Subject digest:
c2a66d6eced80dd89542596200b01e4c4687bba6c923b01ba52c4ddb44093f9e - Sigstore transparency entry: 2659195057
- Sigstore integration time:
-
Permalink:
Kushim-Jiang/pygraphite2@ac9f32a05ce0c653f8896cab2fb0c0d1b5bf1d45 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Kushim-Jiang
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ac9f32a05ce0c653f8896cab2fb0c0d1b5bf1d45 -
Trigger Event:
push
-
Statement type: