diff --git a/README.md b/README.md index c91a555..c3b3c87 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ A language and parser written in Emacs Lisp for my custom extension of Markdown, - [ ] Superscript (Will not implement) ### md4tj's Custom Extensions -- [ ] Custom Divs +- [x] Custom Divs - [ ] RSS Feeds - [x] LaTeX image generation and embedding - [x] Including other md4tj files diff --git a/pnglatex b/pnglatex new file mode 100755 index 0000000..77cf77e --- /dev/null +++ b/pnglatex @@ -0,0 +1,424 @@ +#!/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 "$@"