Skip to content

Instantly share code, notes, and snippets.

@jinwei233
Created November 15, 2017 07:40
Show Gist options
  • Save jinwei233/3898f30cb532ac055609d49c1efb69f9 to your computer and use it in GitHub Desktop.
Save jinwei233/3898f30cb532ac055609d49c1efb69f9 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
'''
NAME
latex2png - Converts LaTeX source to PNG file
SYNOPSIS
latex2png [options] INFILE
DESCRIPTION
This filter reads LaTeX source text from the input file
INFILE (or stdin if INFILE is -) and renders it to PNG image file.
Typically used to render math equations.
Requires latex(1), dvipng(1) commands and LaTeX math packages.
OPTIONS
-D DPI
Set the output resolution to DPI dots per inch. Use this option to
scale the output image size.
-o OUTFILE
The file name of the output file. If not specified the output file is
named like INFILE but with a .png file name extension.
-m
Skip if the PNG output file is newer that than the INFILE.
Compares timestamps on INFILE and OUTFILE. If
INFILE is - (stdin) then compares MD5 checksum stored in file
named like OUTFILE but with a .md5 file name extension.
The .md5 file is created if the -m option is used and the
INFILE is - (stdin).
-v
Verbosely print processing information to stderr.
--help, -h
Print this documentation.
--version
Print program version number.
SEE ALSO
latex(1), dvipng(1)
AUTHOR
Written by Stuart Rackham, <srackham@gmail.com>
The code was inspired by Kjell Magne Fauske's code:
http://fauskes.net/nb/htmleqII/
See also:
http://www.amk.ca/python/code/mt-math
http://code.google.com/p/latexmath2png/
COPYING
Copyright (C) 2010 Stuart Rackham. Free use of this software is
granted under the terms of the MIT License.
'''
# Suppress warning: "the md5 module is deprecated; use hashlib instead"
import warnings
warnings.simplefilter('ignore',DeprecationWarning)
import os, sys, tempfile, md5
VERSION = '0.1.0'
# Include LaTeX packages and commands here.
TEX_HEADER = r'''\documentclass{article}
\usepackage{amsmath}
\usepackage{amsthm}
\usepackage{amssymb}
\usepackage{bm}
\newcommand{\mx}[1]{\mathbf{\bm{#1}}} % Matrix command
\newcommand{\vc}[1]{\mathbf{\bm{#1}}} % Vector command
\newcommand{\T}{\text{T}} % Transpose
\pagestyle{empty}
\begin{document}'''
TEX_FOOTER = r'''\end{document}'''
# Globals.
verbose = False
class EApp(Exception): pass # Application specific exception.
def print_stderr(line):
sys.stderr.write(line + os.linesep)
def print_verbose(line):
if verbose:
print_stderr(line)
def write_file(filename, data, mode='w'):
f = open(filename, mode)
try:
f.write(data)
finally:
f.close()
def read_file(filename, mode='r'):
f = open(filename, mode)
try:
return f.read()
finally:
f.close()
def run(cmd):
global verbose
if verbose:
cmd += ' 1>&2'
else:
cmd += ' 2>%s 1>&2' % os.devnull
print_verbose('executing: %s' % cmd)
if os.system(cmd):
raise EApp, 'failed command: %s' % cmd
def latex2png(infile, outfile, dpi, modified):
'''Convert LaTeX input file infile to PNG file named outfile.'''
outfile = os.path.abspath(outfile)
outdir = os.path.dirname(outfile)
if not os.path.isdir(outdir):
raise EApp, 'directory does not exist: %s' % outdir
texfile = tempfile.mktemp(suffix='.tex', dir=os.path.dirname(outfile))
basefile = os.path.splitext(texfile)[0]
dvifile = basefile + '.dvi'
temps = [basefile + ext for ext in ('.tex','.dvi', '.aux', '.log')]
skip = False
if infile == '-':
tex = sys.stdin.read()
if modified:
checksum = md5.new(tex).digest()
md5_file = os.path.splitext(outfile)[0] + '.md5'
if os.path.isfile(md5_file) and os.path.isfile(outfile) and \
checksum == read_file(md5_file,'rb'):
skip = True
else:
if not os.path.isfile(infile):
raise EApp, 'input file does not exist: %s' % infile
tex = read_file(infile)
if modified and os.path.isfile(outfile) and \
os.path.getmtime(infile) <= os.path.getmtime(outfile):
skip = True
if skip:
print_verbose('skipped: no change: %s' % outfile)
return
tex = '%s\n%s\n%s\n' % (TEX_HEADER, tex.strip(), TEX_FOOTER)
print_verbose('tex:\n%s' % tex)
write_file(texfile, tex)
saved_pwd = os.getcwd()
os.chdir(outdir)
try:
# Compile LaTeX document to DVI file.
run('latex %s' % texfile)
# Convert DVI file to PNG.
cmd = 'dvipng'
if dpi:
cmd += ' -D %s' % dpi
cmd += ' -T tight -x 1000 -z 9 -bg Transparent --truecolor -o "%s" "%s" ' \
% (outfile,dvifile)
run(cmd)
finally:
os.chdir(saved_pwd)
for f in temps:
if os.path.isfile(f):
print_verbose('deleting: %s' % f)
os.remove(f)
if 'md5_file' in locals():
print_verbose('writing: %s' % md5_file)
write_file(md5_file, checksum, 'wb')
def usage(msg=''):
if msg:
print_stderr(msg)
print_stderr('\n'
'usage:\n'
' latex2png [options] INFILE\n'
'\n'
'options:\n'
' -D DPI\n'
' -o OUTFILE\n'
' -m\n'
' -v\n'
' --help\n'
' --version')
def main():
# Process command line options.
global verbose
dpi = None
outfile = None
modified = False
import getopt
opts,args = getopt.getopt(sys.argv[1:], 'D:o:mhv', ['help','version'])
for o,v in opts:
if o in ('--help','-h'):
print __doc__
sys.exit(0)
if o =='--version':
print('latex2png version %s' % (VERSION,))
sys.exit(0)
if o == '-D': dpi = v
if o == '-o': outfile = v
if o == '-m': modified = True
if o == '-v': verbose = True
if len(args) != 1:
usage()
sys.exit(1)
infile = args[0]
if dpi and not dpi.isdigit():
usage('invalid DPI')
sys.exit(1)
if outfile is None:
if infile == '-':
usage('OUTFILE must be specified')
sys.exit(1)
outfile = os.path.splitext(infile)[0] + '.png'
# Do the work.
latex2png(infile, outfile, dpi, modified)
# Print something to suppress asciidoc 'no output from filter' warnings.
if infile == '-':
sys.stdout.write(' ')
if __name__ == "__main__":
try:
main()
except SystemExit:
raise
except KeyboardInterrupt:
sys.exit(1)
except Exception, e:
print_stderr("%s: %s" % (os.path.basename(sys.argv[0]), str(e)))
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment