Attachment 'sage-view.el'

Download

   1 ;;; sage-view.el --- Typeset SAGE output on the fly
   2 
   3 ;; Copyright (C) 2008  Matthias Meulien
   4 
   5 ;; Author: Matthias Meulien <[email protected]>
   6 ;; Keywords: sage math image
   7 
   8 ;; This program is free software; you can redistribute it and/or modify
   9 ;; it under the terms of the GNU General Public License as published by
  10 ;; the Free Software Foundation, either version 3 of the License, or
  11 ;; (at your option) any later version.
  12 
  13 ;; This program is distributed in the hope that it will be useful,
  14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 ;; GNU General Public License for more details.
  17 
  18 ;; You should have received a copy of the GNU General Public License
  19 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
  20 
  21 ;;; Commentary:
  22 
  23 ;; Put this file in a directory in `load-path' and add the following
  24 ;; line to `.emacs':
  25 
  26 ;; (add-hook 'inferior-sage-mode-hook 'sage-view)
  27 
  28 ;; This mode was inspired by doc-view.el by Tassilo Horn, preview.el
  29 ;; by David Kastrup, and imath.el by Yasuaki Honda.
  30 
  31 ;; The LaTex style used by preview.el is mandatory to use
  32 ;; sage-view.el. It is shipped with AUCTeX.
  33 
  34 ;;; Todo:
  35 ;; - Add a auto-reveal stuff to overlays so that one can copy-cut from
  36 ;;   them (use convertion from pdf to text?)
  37 ;; - Check that display is image capable
  38 ;; - Disabling sage preview should remove overlays
  39 ;; - Set color, center image, enlarge overlay to window full size
  40 ;; - Split output string according to <html> tags (split-string)
  41 ;; - Add zoom features to overlays
  42 ;; - Add XML parser error treatment
  43 ;; - Add horizontal scrolling
  44 ;; - Make variables local, so that one can run multiple instance of SAGE
  45 ;; - Delete files 
  46 
  47 ;; Bugs:
  48 ;; - Seems that latex produce buggy DVI when there are overlays... It
  49 ;;   happens with large matrices of polynomials
  50 ;; - error in process filter: Wrong type argument: number-or-marker-p, nil
  51 ;; - Multiple output
  52 ;; - Numpy output can be a text array... should not be inserted into $$ 
  53 ;;   signs
  54 
  55 ;;; Code:
  56 (require 'sage)
  57 
  58 (defvar sage-view-head
  59   "\\documentclass{article}\\usepackage[active, tightpage, pdftex, displaymath]{preview}\\begin{document}\\begin{preview}\$")
  60 ;; it should be possible to choose the conversion technique, and
  61 ;; change pdftex to dvips
  62 (defvar sage-view-tail
  63   "\$\\end{preview}\\end{document}\n")
  64 
  65 (defvar sage-view-text "")
  66 (defvar sage-view-temp-dir nil
  67   "Name of directory for temporary files")
  68 
  69 (defvar sage-view-conversion-process nil)
  70 (defvar sage-view-conversion-buffer "*sage-view*")
  71 
  72 (defvar sage-view-anti-aliasing-level 2)
  73 (defvar sage-view-ghostscript-program "gs")
  74 (defvar sage-view-ghostscript-options
  75   (list "-sDEVICE=png16m" 
  76 	(concat "-dTextAlphaBits=2" sage-view-anti-aliasing-level)
  77 	"-dBATCH"
  78 	(concat "-dGraphicsAlphaBits=" sage-view-anti-aliasing-level)
  79 	"-dSAFER"
  80 	"-q"
  81 	"-dNOPAUSE"))
  82 
  83 (defvar sage-view-resolution nil)
  84 (defvar sage-view-scale 1.1)
  85 (defvar sage-view-current-overlay nil)
  86 
  87 (defun sage-view-math-from-tree (string)
  88   (let* ((root (with-temp-buffer
  89 		 (insert string)
  90 		 (xml-parse-region (point-min) (point-max))))
  91 	 (html (car root))
  92 	 (span (car (xml-get-children html 'span)))
  93 	 (attrs (xml-node-attributes span))
  94 	 (text (car (xml-node-children span))))
  95     (cond
  96      ((equal (cdr (assq 'class attrs)) "math")
  97       ;; First node has class 'math'
  98       text)
  99      (t ""))))
 100 
 101 (defun sage-view-alt-math-from-tree (string)
 102   (let ((head (length "<html><span class=\"math\">"))
 103 	 (tail (1+ (length "</span></html>"))))
 104     (substring string head (- tail))))
 105 
 106 (defun sage-view-preoutput-filter (string)
 107   "Set `sage-view-text' to text extracted from a <span
 108 class=\"math\"> tag found in string; then return this text. If
 109 string is not in XML format or there's no such tag, just return
 110 string.
 111 
 112 Function to be inserted in `comint-preoutput-filter-functions'."
 113   ;; Turn string to an xml tree
 114   (cond
 115    ((and (length string)
 116 	 (not (string-match inferior-sage-prompt string))
 117 	 (equal (substring string 0 (min 6 (length string))) "<html>"))
 118     ;; FIXME: string could be  made of trees concatenated
 119     (setq sage-view-text (sage-view-alt-math-from-tree string))
 120     " \n")
 121    (t string)))
 122 
 123 (defun sage-view-latex->dvi (latex)
 124   "Convert LATEX to DVI asynchronously."
 125   (setq sage-view-conversion-process
 126 	(apply 'start-process
 127 	       (append (list "latex->dvi" sage-view-conversion-buffer
 128 			     "latex")
 129 		       (list (concat "--output-directory=" (shell-quote-argument sage-view-temp-dir))
 130 			     (concat "-interaction=" (shell-quote-argument "nonstopmode"))
 131 			     (concat "-output-format=" (shell-quote-argument "dvi")))
 132 		       (list latex))))
 133   (set-process-sentinel sage-view-conversion-process
 134 			'sage-view-latex->dvi-sentinel)
 135   (process-put sage-view-conversion-process 'dvi-file
 136 	       (concat (file-name-sans-extension latex) ".dvi")))
 137 
 138 (defun sage-view-latex->pdf (latex)
 139   "Convert LATEX to PDF asynchronously."
 140   (setq sage-view-conversion-process
 141 	(apply 'start-process
 142 	       (append (list "latex->pdf" sage-view-conversion-buffer
 143 			     "latex")
 144 		       (list (concat "--output-directory=" (shell-quote-argument sage-view-temp-dir))
 145 			     (concat "-interaction=" (shell-quote-argument "nonstopmode"))
 146 			     (concat "-output-format=" (shell-quote-argument "pdf")))
 147 		       (list latex))))
 148   (set-process-sentinel sage-view-conversion-process
 149 			'sage-view-latex->pdf-sentinel)
 150   (process-put sage-view-conversion-process 'pdf-file
 151 	       (concat (file-name-sans-extension latex) ".pdf")))
 152 
 153 (defun sage-view-dvi->ps (dvi ps)
 154   "Convert DVI to PS asynchronously."
 155   (setq sage-view-conversion-process
 156 	(start-process "dvi->ps" sage-view-conversion-buffer
 157 		       "dvips" "-Pwww" "-E" "-o" ps dvi))
 158   (set-process-sentinel sage-view-conversion-process
 159 			'sage-view-dvi->ps-sentinel)
 160   (process-put sage-view-conversion-process 'ps-file ps))
 161 
 162 (defun sage-view-pdf/ps->png (ps-pdf png)
 163   "Convert PDF-PS to PNG asynchronously."
 164   (setq sage-view-conversion-process
 165 	(apply 'start-process
 166 	       (append (list "pdf/ps->png" sage-view-conversion-buffer
 167 			     sage-view-ghostscript-program)
 168 		       sage-view-ghostscript-options
 169 		       (list (concat "-sOutputFile=" png))
 170 		       (list (concat "-r" (sage-view-compute-resolution)))
 171 		       (list ps-pdf))))
 172   (set-process-sentinel sage-view-conversion-process
 173 			'sage-view-pdf/ps->png-sentinel)
 174   (process-put sage-view-conversion-process 'png-file png))
 175 
 176 (defun sage-view-latex->dvi-sentinel (proc event)
 177   "If LATEX->DVI conversion was successful, convert the DVI to PS."
 178   (let* ((dvi (process-get proc 'dvi-file))
 179 	 (ps (concat (file-name-sans-extension dvi) ".dvi")))
 180     (if (string-match "finished" event)
 181 	(sage-view-dvi->ps dvi ps)
 182       (overlay-put sage-view-current-overlay 'display 
 183 		   (concat "SAGE View failed (see "
 184 			   (file-name-sans-extension dvi) ".log" ")")))))
 185 
 186 (defun sage-view-latex->pdf-sentinel (proc event)
 187   "If LATEX->PDF conversion was successful, convert the PDF to PNG."
 188   (let* ((pdf (process-get proc 'pdf-file))
 189 	 (png (concat (file-name-sans-extension pdf) ".png")))
 190     (if (string-match "finished" event)
 191 	(sage-view-pdf/ps->png pdf png)
 192       (overlay-put sage-view-current-overlay 'display 
 193 		   (concat "SAGE View failed (see "
 194 			   (file-name-sans-extension pdf) ".log" ")")))))
 195 
 196 (defun sage-view-dvi->ps-sentinel (proc event)
 197   "If DVI->PS conversion was successful, convert the PS to PNG."
 198   (let* ((ps (process-get proc 'ps-file))
 199 	 (png (concat (file-name-sans-extension ps) ".png")))
 200     (if (string-match "finished" event)
 201 	(sage-view-pdf/ps->png ps png)
 202       (overlay-put sage-view-current-overlay 'display 
 203 		   (concat "SAGE View failed (see "
 204 			   (file-name-sans-extension ps) ".log" ")")))))
 205 
 206 (defun sage-view-pdf/ps->png-sentinel (proc event)
 207   "If PDF/PS->PNG conversion was successful, update
 208   `sage-view-current-overlay' overlay."
 209   (let ((png (process-get proc 'png-file)))
 210     (if (string-match "finished" event)
 211 	(let ((image (if (and png (file-readable-p png))
 212 			(create-image png 'png))))
 213 	  (cond 
 214 	   (image
 215 	    (overlay-put sage-view-current-overlay 'display image)
 216 	    (sit-for 0))
 217 	   (t (overlay-put sage-view-current-overlay 'display 
 218 			   (concat "SAGE View failed (see "
 219 			 (file-name-sans-extension png) ".log" ")")))))
 220       (overlay-put sage-view-current-overlay 'display 
 221 		   (concat "SAGE View failed (see "
 222 			 (file-name-sans-extension png) ".log" ")")))))
 223 
 224 (defun sage-view-compute-resolution ()
 225   (let ((w (* sage-view-scale (/ (* 25.4 (display-pixel-width))
 226 	      (display-mm-width))))
 227 	(h (* sage-view-scale (/ (* 25.4 (display-pixel-height))
 228 	      (display-mm-height)))))
 229     (concat (int-to-string w) "x" (int-to-string h))))
 230 
 231 (defun sage-view-output-filter (string)
 232   "Generate and place an overlay image.
 233 This generates the filename for the image, and use it for the
 234 region between `comint-last-output-start' and `process-mark'.
 235 
 236 Function to be inserted in `comint-output-filter-function'."
 237   (if (and string sage-view-text)
 238       (let* ((start comint-last-output-start)
 239 	     (end (process-mark (get-buffer-process (current-buffer)))))
 240 	(setq sage-view-current-overlay (make-overlay start (- end 1) nil nil nil))
 241 	(overlay-put sage-view-current-overlay 'help-echo "Overlay made by View")
 242 	(if (not (file-exists-p sage-view-temp-dir))
 243 	    (make-directory sage-view-temp-dir))
 244 	(let* ((base (expand-file-name (make-temp-name "output_") 
 245 				       sage-view-temp-dir))
 246 	       (file (concat base ".tex")))
 247 	  (with-temp-file file
 248 	     (insert sage-view-head)
 249 	     (insert sage-view-text)
 250 	     (insert sage-view-tail))
 251 	  (sage-view-latex->pdf file))))
 252   (setq sage-view-text nil))
 253 
 254 (defun sage-view-gs-open ()
 255   "Start a Ghostscript conversion pass.")
 256 
 257 (defun sage-view-pretty-print-enable ()
 258   (comint-send-string 
 259    (get-buffer-process (current-buffer))
 260    "pretty_print_default()\n"))
 261 
 262 (defun sage-view-pretty-print-disable ()
 263   (comint-send-string 
 264    (get-buffer-process (current-buffer))
 265    "pretty_print_default(enable=false)\n"))
 266 
 267 (define-minor-mode sage-view
 268   "With this mode, output in SAGE interactive buffers is
 269   preprocessed and texify." nil
 270   :group 'sage
 271   :lighter "(View)"
 272   (if sage-view
 273       (progn
 274 	(sage-view-pretty-print-enable)
 275 	(setq sage-view-text nil
 276 	      sage-view-temp-dir 
 277 	      (make-temp-file (expand-file-name "tmp" "~/.sage/temp/") t))
 278 	(make-local-variable 'comint-preoutput-filter-functions)
 279 	(make-local-variable 'comint-output-filter-function)
 280 	(add-hook 'comint-preoutput-filter-functions 'sage-view-preoutput-filter)
 281 	(add-hook 'comint-output-filter-functions 'sage-view-output-filter))
 282     (progn
 283       (remove-hook 'comint-output-filter-functions 'sage-view-output-filter)
 284       (remove-hook 'comint-preoutput-filter-functions 'sage-view-preoutput-filter)
 285       (if (and sage-view-temp-dir (file-exists-p sage-view-temp-dir))
 286 	  (dired-delete-file sage-view-temp-dir 'always))
 287       (setq sage-view-text nil)
 288       (sage-view-pretty-print-disable))))
 289 
 290 (provide 'sage-view)
 291 ;;; sage-view.el ends here

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.

You are not allowed to attach a file to this page.