All formats

star-tex dvi

LanguageGo
LicenseBSD-3-Clause
Repositorygit.sr.ht/~sbinet/star-tex
Documentationpkg.go.dev/star-tex.org/x/tex/dvi
Depends onpure Go; TeX font metrics (TFM) for interpreting pages, found through star-tex.org/x/tex/kpath
Read / writeyes / yes (command level)

star-tex is a port of TeX to Go, and its dvi package handles TeX’s output format. It works at two levels:

The commands also have a write side used by star-tex itself, so DVI can be produced. It’s a young project (v0.7.x); the API may change between minor versions.

Installation#

Shell
go get star-tex.org/x/tex@latest

go run star-tex.org/x/tex/cmd/dvi-dump@latest file.dvi prints a DVI file in readable form.

Basic usage: count pages and list fonts#

Go
package main

import (
	"fmt"
	"log"
	"os"

	"star-tex.org/x/tex/dvi"
)

func main() {
	f, err := os.Open("paper.dvi")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	pages := 0
	err = dvi.Dump(f, func(cmd dvi.Cmd) error {
		switch c := cmd.(type) {
		case *dvi.CmdPre:
			fmt.Printf("DVI version %d, comment %q\n", c.Version, c.Msg)
		case *dvi.CmdBOP:
			pages++
		case *dvi.CmdFntDef1:
			fmt.Printf("font %d: %s\n", c.ID, c.Font)
		case *dvi.CmdPost:
			fmt.Printf("postamble: %d pages, max size %d x %d sp\n", c.Pages, c.Width, c.Height)
		}
		return nil
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("pages:", pages)
}

Font definitions appear twice: before first use and again in the postamble.

How to list the \special commands in a DVI file#

Specials carry colors, image references and hyperlinks:

Go
err = dvi.Dump(f, func(cmd dvi.Cmd) error {
	if c, ok := cmd.(*dvi.CmdXXX1); ok {
		fmt.Printf("special: %s\n", c.Value)
	}
	return nil
})

Long specials use *dvi.CmdXXX4.

How to get glyph and rule positions with a renderer#

Implement dvi.Renderer and run the file on a machine. Positions are in DVI units (scaled points: 65536 per TeX point), with y going down from the top of the page:

Go
package main

import (
	"fmt"
	"image/color"
	"log"
	"os"

	"star-tex.org/x/tex/dvi"
	"star-tex.org/x/tex/kpath"
)

type printer struct{ page int }

func (p *printer) Init(pre *dvi.CmdPre, post *dvi.CmdPost) {}
func (p *printer) BOP(bop *dvi.CmdBOP)                     { p.page++; fmt.Println("page", bop.C0) }
func (p *printer) EOP()                                    {}
func (p *printer) DrawGlyph(x, y int32, font dvi.Font, glyph rune, c color.Color) {
	fmt.Printf("  %s char %d at (%.1f, %.1f) pt\n", font.Name(), glyph,
		float64(x)/65536, float64(y)/65536)
}
func (p *printer) DrawRule(x, y, w, h int32, c color.Color) {
	fmt.Printf("  rule %.1f x %.1f pt\n", float64(w)/65536, float64(h)/65536)
}

func main() {
	raw, err := os.ReadFile("paper.dvi")
	if err != nil {
		log.Fatal(err)
	}
	prog, err := dvi.Compile(raw)
	if err != nil {
		log.Fatal(err)
	}
	// TeX tree with the TFM files; kpath.New() uses a minimal built-in tree
	ktx, err := kpath.NewFromFS(os.DirFS("/usr/share/texlive/texmf-dist"))
	if err != nil {
		log.Fatal(err)
	}
	vm := dvi.NewMachine(dvi.WithContext(ktx), dvi.WithRenderer(&printer{}))
	if err := vm.Run(prog); err != nil {
		log.Fatal(err)
	}
}

dvi.WithHandlers(dvi.NewColorHandler(ktx)) makes the machine follow color push/color pop specials, and the color is passed to DrawGlyph.

Tips#