ActiveState Code

Recipe 68398: Convert (La)TeX math expressions to Tk Images on the fly


This code takes a TeX or LaTeX math expression and converts it into a Tk image suitable for placing in any suitable widget. This technique is ideal for complex documentation where you don't want to maintain a separate set of image files, but is not really fast enough for interactive use (unless your machine is much faster than mine.)

Tcl
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
namespace eval MathToImg {
   image create photo MathToImgHelperImage

   set tmpdir "/tmp"; # configure for platforms!

   # What LaTeX size should we format at?  This looks good on my workstation
   set latexSize huge; # No backslash!

   # Commands
   set latexCommand "latex '\\nonstopmode\\input{%s.tex}'"
   set dvipsCommand "dvips -E -q -o %s.eps %s.dvi"
   set gsCommand "gs -q -dNOPAUSE -dSAFER -sDEVICE=ppmraw           -sOutputFile=%s.ppm %s.eps"

   # Regular expression for finding bounding box
   set BBre {^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) *$}

   proc external cmd {
      exec sh -c $cmd 2>@stderr </dev/null
   }

   proc mathToImg {latex {packages {}}} {
      # Keep ourselves current-dir neutral
      set cwd [pwd]
      cd $::MathToImg::tmpdir

      # Put a wrapper round the supplied LaTeX source
      set latexSource "\\documentclass{article}\n"
      if {[llength $packages]} {
         # Add needed packages, converting Tcl list to comma-ed list
         append latexSource "\\usepackage{" [join $packages ,] "}"
      }
      append latexSource "\\begin{document} \\$::MathToImg::latexSize              \\pagestyle{empty} \\begin{displaymath}\n% USER CODE START\n"
      append latexSource $latex "\n% USER CODE END\n"
      append latexSource "\\end{displaymath} \\end{document}\n"

      set basename [file tail [file rootname $::argv0]]_mathToImg_[pid]

      set f [open $basename.tex w]
      puts -nonewline $f $latexSource
      close $f

      # LaTeX -> DVI
      external [format $::MathToImg::latexCommand $basename]
      # DVI -> EPS
      external [format $::MathToImg::dvipsCommand $basename $basename]

      # Grok out the size, which we need later...
      set f [open $basename.eps r]
      while {![eof $f]} {
         gets $f line
         if {[regexp $::MathToImg::BBre $line ? x1 y1 x2 y2]} {
            break
         }
      }
      close $f

      # EPS -> PPM
      external [format $::MathToImg::gsCommand $basename $basename]

      # Make the image - we can't go direct as we need the size of
      # the image to handle the cropping correctly.
      set img [image create photo               -height [expr {$y2-$y1}]               -width  [expr {$x2-$x1}]]
      # PS has (0,0) in bottom left, Tk in top left
      MathToImgHelperImage read $basename.ppm
      set h [image height MathToImgHelperImage]
      $img copy MathToImgHelperImage               -from $x1 [expr {$h-$y1}] $x2 [expr {$h-$y2}]
      MathToImgHelperImage blank; # Ditch the data

      # Try to clean up after ourselves!
      catch {eval file delete [glob $basename.*]}

      cd $cwd
      return $img
   }

   namespace export mathToImg
}
namespace import MathToImg::*

Discussion

Sample use: pack [label .l -image [mathToImg {\sum_{i=1}^9 x_i}]]

Requires: a UNIX with LaTeX, DVIPS and GhostScript installed. Can use rather a lot of intermediate memory, so solutions involving the PS command 'setpagedevice' would be welcome upgrades...

Sign in to comment