All formats

Matplotlib dviread

LanguagePython
LicenseMatplotlib license (PSF-based, BSD-compatible)
Repositorygithub.com/matplotlib/matplotlib (lib/matplotlib/dviread.py)
Documentationmatplotlib.dviread
Depends ona TeX installation (kpsewhich and TFM font metrics) to interpret pages
Read / writeyes / no

matplotlib.dviread is the DVI reader Matplotlib uses for text.usetex: Matplotlib runs LaTeX, reads the DVI with this module and draws each glyph itself. It’s a public module and works on any DVI file, including XeTeX and LuaTeX output with native fonts in recent versions.

Dvi(filename, dpi) iterates over pages. Each page is a Page named tuple with text (glyphs), boxes (rules), and width, height and descent of the area covered by ink (not the paper size). Coordinates are converted to units of dpi per inch; pass None to keep TeX’s internal units. Pages can only be read in order.

To resolve fonts it calls kpsewhich to find TFM files, so a TeX distribution (TeX Live or MiKTeX) must be installed. It doesn’t render glyphs by itself.

Installation#

Shell
pip install matplotlib

Install TeX Live or MiKTeX, and make sure kpsewhich is on PATH.

Basic usage: list pages, glyphs and rules#

Python
from matplotlib.dviread import Dvi

with Dvi("paper.dvi", 72) as dvi:          # coordinates in points (72 per inch)
    for n, page in enumerate(dvi, 1):
        print(f"page {n}: content {page.width:.1f} x {page.height:.1f} pt, "
              f"{len(page.text)} glyphs, {len(page.boxes)} rules")
        for t in page.text[:5]:
            print(f"  glyph {t.glyph} of {t.font.texname.decode()} "
                  f"at ({t.x:.1f}, {t.y:.1f}), {t.font.size:.1f} pt")
        for b in page.boxes[:3]:
            print(f"  rule at ({b.x:.1f}, {b.y:.1f}), {b.width:.1f} x {b.height:.1f}")

How to extract approximate text from a DVI file#

DVI stores font character codes, not text. For Computer Modern text fonts, codes 32-126 are mostly ASCII, so grouping glyphs by baseline gives readable text:

Python
from itertools import groupby
from matplotlib.dviread import Dvi

with Dvi("paper.dvi", 72) as dvi:
    for page in dvi:
        glyphs = sorted(page.text, key=lambda t: (-round(t.y), t.x))  # y goes up
        for _, line in groupby(glyphs, key=lambda t: -round(t.y)):
            out, last_end = "", None
            for t in line:
                if last_end is not None and t.x - last_end > 2:   # gap = space
                    out += " "
                out += chr(t.glyph) if 32 <= t.glyph < 127 else "?"
                last_end = t.x + t.width   # width is in the same units as x
            print(out)
        print("\f")

Ligatures (fi is code 12 in cmr10), accents and math symbols need a per-font encoding table for real text extraction.

How to list the fonts a DVI file uses#

Python
from matplotlib.dviread import Dvi

fonts = {}
with Dvi("paper.dvi", None) as dvi:
    for page in dvi:
        for t in page.text:
            fonts[t.font.texname] = t.font

for name, font in sorted(fonts.items()):
    print(name.decode(), f"{font.size:.2f} pt")

How to find the font file for a glyph#

Text.font_path resolves the font through the TeX font map (pdftex.map) to a Type 1 or OpenType file:

Python
with Dvi("paper.dvi", 72) as dvi:
    page = next(iter(dvi))
    t = page.text[0]
    print(t.font_path, t.glyph_name_or_index)

find_tex_file looks up any TeX file:

Python
from matplotlib.dviread import find_tex_file

print(find_tex_file("cmr10.tfm"))   # e.g. /usr/share/texlive/.../cmr10.tfm

Tips#