;; texdrive.el
;; Time-stamp: <2008-11-26 17:18:34 (djcb)>
;; Copyright (C) 2008 Dirk-Jan C. Binnema.
;; This file is free software; you can redistribute it and/or modify
;; it undr the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; Overview:
;; texdrive creates png images from TeX formulas, and makes it really
;; easy to create html-pages with formulae. the real work is done by
;; latex and imagemagick, so you'll need to install those.
;; Usage:
;; suppose you are writing an html page, and you need to insert some formula.
;; you type:
;; M-x texdrive-insert-formula[RET]a^2 + b^2 = c^2[RET]abc[RET]
;; now, at point, the following will be inserted:
;;
;;
;;
;; the 'abc.png' is the image that will be created; it does not exist yet though.
;; after you have inserted one or more formulae, you type:
;; M-x texdrive-generate-images-from-html[RET]
;; this will search the document for formulae as specified above, and generate
;; the images.
;; NOTE: while texdrive was specifically written to include the images in
;; webpages, it can be use for general formula-to-png conversion as well, using e.g.:
;; (texdrive-create-png "abc" "a^2 + b^2 = c^2")
;; Installation / customization:
;; copy texdrive.el to a place where emacs can find it, and in your .emacs
;; put something like
;; (require 'texdrive)
;; then, to enable the texdrive minor mode:
;; (texdrive-mode)
;; this will enable two keybindings:
;;
;; "C-c TAB f" for texdrive-insert-formula
;; "C-c TAB g" for texdrive-generate-images
;;
;; these keybindings fit in with the html-helper-mode keybindings.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar texdrive-img-path ""
"determines where to store the output image (relative to pwd)")
(defvar texdrive-tmp-path "/tmp/"
"determines where to store temporary files")
(defvar texdrive-dvips "dvips"
"name of the 'dvips' program on your system")
(defvar texdrive-convert "convert"
"name of the 'convert' program on your system")
(defvar texdrive-identify "identify"
"name of the 'identify' program on your system")
(defvar texdrive-latex "latex"
"name of the 'latex' program on your system")
(defvar texdrive-minor-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c TAB f") 'texdrive-insert-formula)
(define-key map (kbd "C-c TAB g") 'texdrive-generate-images)
map)
"keymap for texdrive-minor-mode.")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defconst texdrive-output "*texdrive-output*"
"name of texdrive output buffer")
(defconst texdrive-version "0.0.1")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun texdrive-basename-from-title-lst (lst)
(when lst
(concat
(if (string< (car lst) "0") "-" (downcase (car lst)))
(texdrive-basename-from-title-lst (cdr lst)))))
(defun texdrive-basename-from-title (title)
"convert the formula title into a filename basename (without extension)"
(texdrive-basename-from-title-lst (split-string title "" t)))
(defun texdrive-target-png (title)
"target png file"
(concat texdrive-img-path (texdrive-basename-from-title title) ".png"))
(defun texdrive-insert-formula (tex title)
(interactive "sFormula (TeX):\nsTitle:")
(insert
(concat
"")))
(defun texdrive-formulae-assoc ()
"return an assoc of ( ) starting from point"
(when
(re-search-forward
" ) starting for the whole buffer"
(save-excursion
(goto-char 0)
(texdrive-formulae-assoc)))
(defun texdrive-create-tmp-tex (name formula)
"create a tex file with the the latex for some formula;"
"returns the filename created, or nil in case of error"
(let ((texfile (concat texdrive-tmp-path name ".tex"))
(tex (concat
"\\documentclass[10pt,notitlepage]{article}"
"\\usepackage{amsmath}"
"\\usepackage{amsmath}"
"\\usepackage{amsfonts}"
"\\usepackage{amssymb}"
;; to change the font used, one could uncommment, e.g.
;; "\\usepackage{mathpazo}" ;; or
;; "\\usepackage{cmbright}" ;; or
;; "\\usepackage[math]{iwona}"
;; see: http://ctan.tug.org/tex-archive/info/Free_Math_Font_Survey/survey.html
"\\pagestyle{empty}"
"\\begin{document}$" formula "$\\end{document}\n")))
(with-temp-file texfile (insert tex))
texfile))
(defun texdrive-has-required-tools ()
(let ((tools '(texdrive-latex texdrive-convert texdrive-dvips texdrive-identify))
(ok t))
(mapc (lambda (tool)
(when (not (executable-find (symbol-value tool)))
(setq ok nil)
(message (concat "texdrive error: " (symbol-value tool)
" not found; please install it."))))
tools)
ok))
(defun texdrive-create-tmp-dvi (name formula)
"create a dvi file with some formula;"
"returns the filename created, or nil in case of error"
(when (texdrive-has-required-tools)
(let ((texfile (texdrive-create-tmp-tex name formula))
(dvifile (concat texdrive-tmp-path name ".dvi")))
(when texfile
(if (and (= 0 (call-process texdrive-latex nil texdrive-output nil
"-draftmode" "-file-line-error" "-halt-on-error"
"-interaction=nonstopmode"
(concat "-output-directory=" texdrive-tmp-path) texfile))
(file-exists-p dvifile))
dvifile
(progn (message
"texdrive: LaTeX error; see the *texdrive-output* buffer") nil))))))
(defun texdrive-create-tmp-ps (name formula)
"create a ps file with some formula;"
"returns the filename created, or nil in case of error"
(let ((dvifile (texdrive-create-tmp-dvi name formula))
(psfile (concat texdrive-tmp-path name ".ps")))
(when dvifile
(if (= 0 (call-process
texdrive-dvips nil texdrive-output nil "-o" psfile dvifile))
psfile
(progn (message (concat "texdrive: error while creating " psfile
"; see *texdrive-output*")) nil)))))
(defun texdrive-png-is-uptodate (pngfile comment)
"determine whether png file is up-to-date, by checking the comment field"
(when (file-exists-p pngfile)
(let ((png-comment (shell-command-to-string
(concat texdrive-identify " -verbose " pngfile
"| grep '^ *Comment: ' "
"| sed 's/^ *Comment: //'"
"| tr -d '\n'"))))
(when (string= png-comment comment)
(message (concat "\ntexdrive: up-to-date: " pngfile)) t))))
(defun texdrive-create-png (name formula)
"create a ps file with some formula;"
"returns the filename created, or nil in case of error"
(let ((pngfile (texdrive-target-png name))
;; we put the formula as a 'cookie' in a comment, so we can use
;; it later to determine if the file needs updating
;; we use base64 to avoid quoting issues
;; TODO: check if there is some max length; a hash may be better.
(cookie (base64-encode-string formula t)))
(when (not (texdrive-png-is-uptodate pngfile cookie))
(let ((psfile (texdrive-create-tmp-ps name formula)))
(when psfile
(message (concat "texdrive: creating: " pngfile))
(if (= 0 (call-process
texdrive-convert nil texdrive-output nil
"-comment" cookie
"-density" "128"
psfile
"-trim"
;; "-resize" "50%"
"-transparent" "#ffffff"
pngfile))
(progn (message (concat "texdrive: created: " pngfile)) pngfile)
(progn (message (concat "texdrive: error while creating " pngfile
"; see *texdrive-output*")) nil)))))))
(defun texdrive-generate-images-lst (lst)
(when lst
(let ((name (car (car lst))) (formula (cadr (car lst))))
(when (texdrive-create-png name formula)
(texdrive-generate-images-lst (cdr lst))))))
(defun texdrive-generate-images ()
"generate png images for all the texdrive formulae in the buffer"
(interactive)
(let ((eqs (texdrive-buffer-formulae-assoc)))
(texdrive-generate-images-lst eqs)))
(defun texdrive-version ()
"show the texdrive version"
(interactive)
(message (concat "texdrive version: " texdrive-version)))
(define-minor-mode texdrive-mode
"Toggle texdrive mode.
With no argument, this command toggles the mode.
A non-null prefix turns the mode on, while a null
argument turns the mode off.
When texdrive-mode is enabled,
C-c TAB f
inserts an element for a new formula (texdrive-insert-formula), while
C-c TAB g
generates the images.
For more information, see: http://www.djcbsoftware.nl/code/texdrive/
"
;; the initial value
:init-value nil
;; modeline indicator
:lighter " TX"
;; minor mode bindings
:keymap texdrive-minor-mode-map)
(provide 'texdrive)