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.2.dev24.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.2.dev24-py3-none-any.whl (10.5 MB view details)

Uploaded Python 3

File details

Details for the file fondi-0.2.2.dev24.tar.gz.

File metadata

  • Download URL: fondi-0.2.2.dev24.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.2.dev24.tar.gz
Algorithm Hash digest
SHA256 4953a63d83b666a6663b6151078f4aa15b82ca793ef6096bb7e56d98783f850d
MD5 75fda607335bcb5b261d4cc4e3e11d4e
BLAKE2b-256 d05f7bf05626f16c09854a2a21f5c3f2e3e86f45089f5764e7b9b4356efd1e96

See more details on using hashes here.

File details

Details for the file fondi-0.2.2.dev24-py3-none-any.whl.

File metadata

  • Download URL: fondi-0.2.2.dev24-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.2.dev24-py3-none-any.whl
Algorithm Hash digest
SHA256 4af7fbb11ae70f349ece8a6aa6d231eeae6cb615fab33b085baa28e084216738
MD5 4acfc3081c3028b5944781849bede765
BLAKE2b-256 0fa893c4f3049111bbe1bbafc72b1505c6f5ee47eaa7e0f2bd1461e3fde20e70

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