;; 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)