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 <matthias.meulien@xlim.fr>
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.