Skip to main content

LaTeX-like math renderer with PNG and SVG output

Project description

A simple mathematical LaTeX renderer for Python. Output as PNG (Pillow) or SVG (vector text with New Computer Modern).

Super- and subscript

Installation

pip install fondi

Requires Python 3.8+, Pillow, and NumPy.

Quick start

import fondi

mt = fondi.MathText(r"\frac{1}{2}", fontSize=50, color=(255, 255, 255, 255))

mt.to_pil().save("formula.png")   # raster
mt.save_svg("formula.svg")        # small SVG + external fonts (default)

MathText arguments

Argument Type Description
text str Fondi math string (LaTeX-like, not full LaTeX).
fontSize int Nominal size in pixels (same scale as the PNG renderer).
color (R, G, B, A) Each channel 0–255.

Output methods

Method Returns Use for
to_pil() PIL.Image Matplotlib, GUIs, notebooks, textures.
to_svg(...) str SVG XML; you control where files are written.
save_svg(path, ...) Path Write SVG to disk; optional font copy.
scene() Scene Inspect layout primitives (advanced).
image PIL.Image Same as to_pil() (backward compatible).

SVG export and fonts

Text in SVG uses New CM10 (NewCM10-Regular.otf / NewCM10-Italic.otf from the package). Integrals and braces are still embedded PNGs inside the SVG (v1).

You choose how those OTF files are shipped:

Mode SVG size Font files Best for
Embedded Large (~1 MB+) Inside the SVG Single file, email, simple hosting
External + copy Small (few KB) Copied next to the SVG First export in a project
External, no copy Small You provide them Many SVGs sharing one fonts/ folder

1. Embedded fonts (self-contained)

Every to_svg() call includes both full OTF files as base64 in <defs>. No separate font files; viewers need nothing installed.

svg = mt.to_svg()                                    # default
svg = mt.to_svg(embed_fonts=True)

with open("formula.svg", "w") as f:
    f.write(svg)

mt.save_svg("formula.svg", embed_fonts=True)

2. External fonts (small SVG)

The SVG references OTF files by URL, e.g. fonts/NewCM10-Regular.otf. You must keep those paths valid relative to where the SVG is opened (browser, Inkscape, etc.).

to_svg() — string only

svg = mt.to_svg(embed_fonts=False, font_url_prefix="fonts/")
# Writes no font files. You place the OTFs yourself.
Parameter Default Meaning
embed_fonts True False → external @font-face URLs.
font_url_prefix "" Directory prefix in the URL, e.g. "fonts/" or "" if OTFs sit beside the SVG.

Copy fonts once from the package:

from fondi.backends import copy_font_files

copy_font_files("shared/fonts/")
# Creates shared/fonts/NewCM10-Regular.otf and NewCM10-Italic.otf

Then export many formulas pointing at the same folder:

for name, tex in formulas.items():
    m = fondi.MathText(tex, 24, (0, 0, 0, 255))
    with open(f"out/{name}.svg", "w") as f:
        f.write(m.to_svg(embed_fonts=False, font_url_prefix="../shared/fonts/"))

save_svg() — write file (+ optional font copy)

mt.save_svg("out/formula.svg")

Defaults: embed_fonts=False, font_subdir="fonts", copy_fonts=True.

Parameter Default Meaning
embed_fonts False True → one large self-contained SVG.
font_subdir "fonts" Subfolder under the SVG’s parent for OTF files and URL prefix ("fonts/"). Use None for OTFs in the same directory as the SVG.
copy_fonts True Copy bundled OTFs into font_subdir. Falseonly write the SVG (fonts must already exist there).

Copy fonts once per output tree:

mt.save_svg("out/formula.svg", embed_fonts=False, font_subdir="fonts")
out/
  formula.svg
  fonts/
    NewCM10-Regular.otf
    NewCM10-Italic.otf

SVG only (fonts already present):

from fondi.backends import copy_font_files

copy_font_files("out/fonts/")   # once

for i, tex in enumerate(items):
    fondi.MathText(tex, 20, (0, 0, 0, 255)).save_svg(
        f"out/label_{i}.svg",
        embed_fonts=False,
        font_subdir="fonts",
        copy_fonts=False,
    )

OTFs beside the SVG (no subfolder):

mt.save_svg("out/formula.svg", embed_fonts=False, font_subdir=None)
# Expects out/NewCM10-Regular.otf next to out/formula.svg
# URLs in SVG: NewCM10-Regular.otf (no prefix)

Choosing a mode (cheat sheet)

# One file, simplest sharing
mt.save_svg("x.svg", embed_fonts=True)

# Project with assets/fonts/ reused everywhere
copy_font_files("assets/fonts/")
mt.save_svg("plots/a.svg", embed_fonts=False, font_subdir="fonts", copy_fonts=False)
# Ensure font_subdir resolves from plots/a.svg → often use font_url_prefix like "../assets/fonts/"
# via to_svg(), or place fonts relative to each SVG accordingly

For nested paths, prefer to_svg(embed_fonts=False, font_url_prefix="...") with a prefix that matches your real directory layout.

Low-level API

from fondi.backends import render_svg, save_svg_bundle, copy_font_files

scene = mt.scene()
svg = render_svg(scene, embed_fonts=False, font_url_prefix="fonts/")
save_svg_bundle(scene, "out.svg", embed_fonts=False, font_subdir="fonts", copy_fonts=False)

PNG export

Raster output bakes the font into pixels. No font files are needed at view time.

img = mt.to_pil()
img.save("formula.png")
mt.image.save("formula.png")   # equivalent

Architecture

Layouts position math and emit a backend-neutral scene graph (TextRun, Line, RasterSymbol, …). Renderers turn the same scene into PNG or SVG — new macros are implemented once.

Layer Role
layout/*, plain/plain.py Geometry + scene primitives only
metrics.py Font measurement (single source)
scene.py Data types
backends/pillow.py, backends/svg.py PNG / SVG output

Adding a macro (write once)

  1. Add FooLayout(Layout) under src/fondi/layout/.
  2. Register MACROS["\\foo"] = FooLayout.
  3. Position children with boundingBox, setCenter, etc.
  4. Implement collect_scene(offset) returning TextRun, Line, RasterSymbol, … — no Pillow or SVG code in the layout.
  5. Run tests/test.py (PNG) and tests/test_svg.py (SVG smoke).

Features

  • Parentheses
  • Subscript / superscript
  • Delimiters ((), [], cases {) as vector paths; integrals still PNG (v1)
  • Fractions
  • Cases
  • Greek letters
  • Square root
  • Integrals

Tests

cd tests && python test.py      # regenerate PNG golden images
cd tests && python test_svg.py  # SVG smoke (embedded + external fonts)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fondi-0.2.1.dev22.tar.gz (10.5 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fondi-0.2.1.dev22-py3-none-any.whl (10.5 MB view details)

Uploaded Python 3

File details

Details for the file fondi-0.2.1.dev22.tar.gz.

File metadata

  • Download URL: fondi-0.2.1.dev22.tar.gz
  • Upload date:
  • Size: 10.5 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fondi-0.2.1.dev22.tar.gz
Algorithm Hash digest
SHA256 99f8f934e70b8d990ac1645741a52eb7d9f233ce344fc203d7b176d1c4764945
MD5 388b95cd8d34f85db33c99fbe7ce80cc
BLAKE2b-256 b0644db683dfb860ee3209bad874dbc48417a7c55416ec718c7d7aa34702a378

See more details on using hashes here.

File details

Details for the file fondi-0.2.1.dev22-py3-none-any.whl.

File metadata

  • Download URL: fondi-0.2.1.dev22-py3-none-any.whl
  • Upload date:
  • Size: 10.5 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fondi-0.2.1.dev22-py3-none-any.whl
Algorithm Hash digest
SHA256 562b1ab20e5e065da1bd9457b4e40387a612fb45663cab79bfd46e8fa4dd277c
MD5 8316e96231446cf2bffe4c190fc58fd6
BLAKE2b-256 9cd5f62b0407bc9992ccfcc8c539061edfabf45a0b4d22e354ee7acc4f28a43b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page