#!/bin/bash # This file is part of pnglatex . # Copyright Massimo Neri and all the contributors. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . BACKGROUND= BORDER= DPI= ENVIRONMENT= FOREGROUND= FORMULA= HEADER= HELP=0 LOGFILE= MARGIN= OPTIMIZE=0 PACKAGES= PADDING= PNGFILE= SHOWVERSION=0 SILENT=0 SIZE= TEXFILE= declare -r VERSION=0.13 # Add margin, border and padding to the image. function box { if [ "$PADDING" ]; then convert $PNGFILE -bordercolor $BACKGROUND -border $(scale $PADDING) $PNGFILE fi if [ "$BORDER" ]; then convert $PNGFILE -bordercolor $BORDER -border $(scale 1) $PNGFILE fi if [ "$MARGIN" ]; then convert $PNGFILE -bordercolor $BACKGROUND -border $(scale $MARGIN) $PNGFILE fi } # Remove temporary files. function clean { rm -rf $TMPDIR } # Get screen's dpi. # # @return the dpi resolution of the screen. function dpi { local VALUE if exists xdpyinfo; then VALUE=$(xdpyinfo 2> /dev/null | grep resolution | sed -r 's/^[^0-9]+([0-9]+)x.*$/\1/') fi if [ ! "$VALUE" ]; then VALUE=96 fi echo $VALUE } # Check if the specified command is installed on the system. # # @param $1 the command to test. # @return true if the command is installed on the system, false otherwise. function exists { local COMMAND=$1 return $(command -v $COMMAND &> /dev/null) } # Generate the png image. function generate { local BEGINENV local ENDENV local PREFIX local TMPDIR local SUFFIX TMPDIR="$(mktemp -d)" TEXFILE="$(mktemp -p $TMPDIR tXXX.tex)" if [ "$ENVIRONMENT" = '$' ] || [ "$ENVIRONMENT" = '$$' ]; then BEGINENV=$ENVIRONMENT ENDENV=$ENVIRONMENT else BEGINENV=$(echo $ENVIRONMENT | sed -e 's/\([^\[\{]*\)/\\begin\{\1\}/') ENDENV=$(echo $ENVIRONMENT | sed -e 's/\([^\[\{]*\).*/\\end\{\1\}/') fi echo "\documentclass[${SIZE}pt]{article}\pagestyle{empty}" > $TEXFILE { IFS=":" for P in $PACKAGES; do echo $P | sed -e 's/\([^\[\{]*\)/\\usepackage\{\1\}/' >> $TEXFILE done } if [ ! -z "$HEADER" ]; then cat $HEADER >> $TEXFILE fi echo "\begin{document}$BEGINENV" >> $TEXFILE if [ ! "$FORMULA" ]; then cat - >> $TEXFILE else echo $FORMULA >> $TEXFILE fi echo "$ENDENV\end{document}" >> $TEXFILE if [ "$LOGFILE" ]; then cat $TEXFILE > $LOGFILE fi latex -halt-on-error -interaction=nonstopmode -output-directory=$TMPDIR $TEXFILE \ | tee -a $LOGFILE \ | sed -n '/^!/,/^ /p' >&2 if [ ${PIPESTATUS[0]} -ne 0 ]; then clean exit 1 fi if [ ! "$PNGFILE" ]; then PNGFILE="$(mktemp -p $PWD fXXX.png)" fi dvipng -bg $BACKGROUND -D $DPI -fg $FOREGROUND -o $PNGFILE -q --strict -T tight ${TEXFILE%.tex}.dvi \ | tee -a $LOGFILE \ > /dev/null if [ "$PADDING" ] || [ "$BORDER" ] || [ "$MARGIN" ]; then box fi if [ $OPTIMIZE -eq 1 ]; then optimize fi if [ $SILENT -eq 0 ]; then readlink -f $PNGFILE fi clean } # Entry point of the script. function main { if ! exists latex || ! exists dvipng; then echo "pnglatex requires latex and dvipng packages." >&2 exit 1 fi properties parse "$@" if [ $HELP -eq 1 ]; then usage exit 0 fi if [ $SHOWVERSION -eq 1 ]; then version exit 0 fi if [ "$PADDING" ] || [ "$BORDER" ] || [ "$MARGIN" ]; then if ! exists convert; then echo "Paddings, borders and margins require imagemagick package." >&2 exit 1 fi fi if [ $OPTIMIZE -eq 1 ]; then if ! exists optipng; then echo "Optimization requires optipng package." >&2 exit 1 fi fi if [ ! -z "$HEADER" ] && [ ! -f "$HEADER" ]; then echo "File does not exist: $HEADER" >&2 exit 1 fi if [ -z "$BACKGROUND" ]; then BACKGROUND=White fi if [ -z "$DPI" ]; then DPI=$(dpi) else if ! match "$DPI" '^[1-9][0-9]*$'; then echo "Invalid dpi." >&2 exit 1 fi fi if [ -z "$ENVIRONMENT" ]; then ENVIRONMENT=\$ fi if [ -z "$FOREGROUND" ]; then FOREGROUND=Black fi if [ ! -z "$MARGIN" ] && ! match "$MARGIN" '^[0-9]+(x[0-9]+)?$'; then echo "Invalid margin." >&2 exit 1 fi if [ ! -z "$PADDING" ] && ! match "$PADDING" '^[0-9]+(x[0-9]+)?$'; then echo "Invalid padding." >&2 exit 1 fi if [ -z "$SIZE" ]; then SIZE=11 else SIZE=$(echo $SIZE | sed 's/pt//') if ! match "$SIZE" '^[1-9][0-9]*$'; then echo "Invalid font size." >&2 exit 1 fi fi generate } # Return true if the specified string matches the specified regular expression # # @param $1 The string to test. # @param $2 The pattern to match. # @return true if the string matches the pattern, false otherwise. function match { local PATTERN=$2 local TEXT=$1 return $(echo $TEXT | egrep $PATTERN &> /dev/null); } # Use optipng to optimize the image. # # @return echoes optipng output. function optimize { optipng -f0-5 -quiet -zc1-9 -zm1-9 -zs0-3 $PNGFILE } # Parse command line arguments. function parse { while getopts b:B:d:e:f:F:hH:l:m:Mo:Op:P:r:s:Sv ARG; do case $ARG in b) BACKGROUND=$OPTARG ;; B) BORDER=$OPTARG ;; d) DPI=$OPTARG ;; e) ENVIRONMENT=$OPTARG ;; f) FORMULA=$OPTARG ;; F) FOREGROUND=$OPTARG ;; h) HELP=1 ;; H) HEADER=$OPTARG ;; l) LOGFILE=$OPTARG ;; m) MARGIN=$OPTARG ;; o) PNGFILE=$OPTARG ;; O) OPTIMIZE=1 ;; p) PACKAGES=$OPTARG ;; P) PADDING=$OPTARG ;; s) SIZE=$OPTARG ;; S) SILENT=1 ;; v) SHOWVERSION=1 ;; ?) exit 1 esac done } function properties { local PROPERTIES=~/.pnglatex if [ -f $PROPERTIES ]; then while IFS="=" read -r KEY VALUE; do case $KEY in BACKGROUND) BACKGROUND=$VALUE ;; BORDER) BORDER=$VALUE ;; DPI) DPI=$VALUE ;; ENVIRONMENT) ENVIRONMENT=$VALUE ;; FOREGROUND) FOREGROUND=$VALUE ;; HEADER) HEADER=$VALUE ;; MARGIN) MARGIN=$VALUE ;; OPTIMIZE) OPTIMIZE=$VALUE ;; PACKAGES) PACKAGES=$VALUE ;; PADDING) PADDING=$VALUE ;; SIZE) SIZE=$(echo $VALUE | sed 's/pt//') ;; esac done < $PROPERTIES fi } # Scales the specified dimensions to the wanted dpi. # # @param $1 the dimension string, in the form of 'mxn', where m and n are integers. # @return echoes the scaled dimension string. function scale { local BASEDPI=$(dpi) local WIDTH=$(($(echo $1 | sed -r 's/x.*//') * $DPI / $BASEDPI)) local HEIGHT=$(($(echo $1 | sed -r 's/.*x//') * $DPI / $BASEDPI)) echo ${WIDTH}x${HEIGHT} } # Print usage message. # # @return echoes the usage message. function usage { echo "pnglatex $VERSION - Write LaTeX formulas into PNG files." echo "Copyright Massimo Neri and all the contributors." echo echo "List of options:" echo " -b Set the background color." echo " -B Set the border color." echo " -d Set the output resolution to the specified dpi." echo " -e Set the environment with [options] and {arguments}." echo " -f The LaTeX formula." echo " -F Set the foreground color." echo " -h Print this help message." echo " -H Insert the content of the specified file in the preamble." echo " -l Log file." echo " -m Set the margin." echo " -o Specify the output file name." echo " -O Optimize the image." echo " -p A colon separated list of LaTeX package names." echo " -P Set the padding." echo " -s Set the font size." echo " -S Silent mode: don't print image file name." echo " -v Show version." echo echo "Examples:" echo " pnglatex -f \"E=mc^2\"" echo " pnglatex -e displaymath -f \"\sum_{i=0}^n i=\frac{n(n+1)}{2}\"" echo " pnglatex -b Transparent -p amssymb:amsmath -P 5x5 -s 12 -f \"A\triangleq B\"" } # Print pnglatex version. # # @return echoes the version. function version { echo $VERSION } trap "clean" SIGINT SIGTERM main "$@"