Texinfo compatibility fix
[maxima.git] / interfaces / emacs / imaxima / imaxima.el
blobd0714ea1a9be14853a19976a53cf615185535bac
1 ;; -*- mode: emacs-lisp; lexical-binding: t; -*-
2 ;;;; imaxima.el --- Maxima mode with images
4 ;; Created: 14 Nov 2001
5 ;; Version: See version.texi
6 ;; Keywords: maxima
8 ;; Copyright (C) 2001, 2002, 2003, 2004 Jesper Harder
9 ;; Copyright (C) 2006 Stephen Eglen (imaxima-print-buffer)
10 ;; Copyright (C) 2007, 2008 Yasuaki Honda (imaxima-to-html, inline graph)
11 ;; Copyright (C) 2020, 2021, 2022 Leo Butler (imaxima-gnuplot-replot, various improvements)
13 ;; Time-stamp: <16-01-2024 11:38:28 Leo Butler>
15 ;; This program is free software; you can redistribute it and/or
16 ;; modify it under the terms of the GNU General Public License as
17 ;; published by the Free Software Foundation; either version 2 of
18 ;; the License, or (at your option) any later version.
20 ;; This program is distributed in the hope that it will be
21 ;; useful, but WITHOUT ANY WARRANTY; without even the implied
22 ;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
23 ;; PURPOSE. See the GNU General Public License for more details.
25 ;; You should have received a copy of the GNU General Public
26 ;; License along with this program; if not, write to the Free
27 ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
30 ;;; Commentary:
32 ;; This file (and imaxima.lisp) provides image support for interacting
33 ;; with the computer algebra system Maxima
34 ;; <http://maxima.sourceforge.net/>
36 ;; The command `imaxima' (M-x imaxima) provides a simple comint
37 ;; derived CLI mode.
39 ;; To use imaxima with the Maxima mode from the Maxima distribution
40 ;; set `imaxima-use-maxima-mode-flag' to `t'.
42 ;; To turn off images, evaluate "display2d:true" in Maxima. To turn
43 ;; them on again, evaluate "display2d:imaxima".
45 ;; The command `imaxima-latex' prepares a LaTeX version of the Maxima
46 ;; buffer.
48 ;; The package requires Emacs 21 with image support.
50 ;; A fairly recent version of Ghostscript is recommended (at least
51 ;; newer than v. 8.56). If your version is too old, you can either set
52 ;; `imaxima-image-type' to 'ps or remove the options
53 ;; "-dTextAlphaBits=4" and "-dGraphicsAlphaBits=4" from
54 ;; `imaxima-gs-options'. The images won't look nearly as attractive,
55 ;; though -- the text looks ragged because it isn't anti aliased.
57 ;; The file "imaxima.lisp" is a slightly modified version of
58 ;; "texmacs.lisp" in the TeXmacs distribution. Several of the image
59 ;; routines are borrowed from David Kastrup's preview-latex.el.
61 ;; Version 1.0 beta and later supports inline graph. You can use
62 ;; the following six maxima commands.
63 ;; wxplot2d(), wxplot3d(), wxdraw2d(), wxdraw3d(), wximplicit_plot(),
64 ;; wxcontour_plot().
65 ;; GNUPLOT 4.2 or later is needed for this to work. The resulted image
66 ;; generated by gnuplot will be inserted as the output of the command.
69 ;; Installation:
70 ;; Take a look at the README file which comes with this file.
73 ;;; Code:
75 ;; modified to remove eval-when-compile form. Surrounding
76 ;; the eval-when-compile prevernts imaxima from running
77 ;; properly in the xemacs on cygwin environment.
78 (require 'imaxima-autoconf-variables)
79 (require 'advice)
81 (require 'comint)
82 (require 'cl-lib)
84 (require 'mylatex.ltx)
86 ;; XEmacs stuff
88 (defalias 'imaxima-image-type-available-p
89 (if (fboundp 'image-type-available-p)
90 'image-type-available-p
91 'featurep))
93 (defalias 'imaxima-display-pixel-width
94 (if (fboundp 'display-pixel-width)
95 'display-pixel-width
96 'device-pixel-width))
98 (defalias 'imaxima-display-pixel-height
99 (if (fboundp 'display-pixel-height)
100 'display-pixel-height
101 'device-pixel-height))
103 (defalias 'imaxima-display-mm-width
104 (if (fboundp 'display-mm-width)
105 'display-mm-width
106 'device-mm-width))
108 (defalias 'imaxima-display-mm-height
109 (if (fboundp 'display-mm-height)
110 'display-mm-height
111 'device-mm-height))
113 (defalias 'imaxima-get-window-width
114 (if (featurep 'xemacs)
115 'imaxima-get-window-width-xemacs
116 'imaxima-get-window-width-emacs))
118 (defalias 'imaxima-color-values
119 (if (fboundp 'color-values)
120 'color-values
121 '(lambda (color) (color-rgb-components
122 (if (stringp color)
123 (make-color-specifier color)
124 color)))))
126 (defun imaxima-get-bg-color ()
127 (if (featurep 'xemacs)
128 (face-property 'default 'background)
129 (frame-parameter nil 'background-color)))
131 (defun imaxima-get-fg-color ()
132 (if (featurep 'xemacs)
133 (face-property 'default 'foreground)
134 (frame-parameter nil 'foreground-color)))
136 ;; XEmacs doesn't have subst-char-in-string (sigh!).
138 (defun imaxima-subst-char-in-string (fromchar tochar string &optional inplace)
139 "Replace FROMCHAR with TOCHAR in STRING each time it occurs.
140 Unless optional argument INPLACE is non-nil, return a new string."
141 (let ((i (length string))
142 (newstr (if inplace string (copy-sequence string))))
143 (while (> i 0)
144 (setq i (1- i))
145 (if (eq (aref newstr i) fromchar)
146 (aset newstr i tochar)))
147 newstr))
150 (defconst imaxima-mouse2 (if (featurep 'xemacs)
151 [button2]
152 [mouse-2]))
154 (defconst imaxima-mouse3 (if (featurep 'xemacs)
155 [button3]
156 [mouse-3]))
158 ;; Options
160 (defgroup imaxima nil
161 "Image support for Maxima."
162 :version "21.1"
163 :link '(url-link "https://maxima.sourceforge.net/")
164 :link '(custom-manual "(imaxima)")
165 :prefix "imaxima-"
166 :group 'maxima)
168 (defvar process-connection-type-flag
169 (if (eql system-type 'darwin) t nil))
171 (defvar imaxima-image-types '(png postscript jpeg tiff))
173 (defcustom imaxima-image-type 'png
174 "Image type to used in Maxima buffer."
175 :group 'imaxima
176 :type (cons 'choice
177 (mapcar (lambda (type) (list 'const type))
178 (cl-remove-if-not 'imaxima-image-type-available-p
179 imaxima-image-types))))
181 (defcustom imaxima-pt-size 11
182 "*Point size used in LaTeX."
183 :group 'imaxima
184 :type '(choice (const 9)
185 (const 10)
186 (const 11)
187 (const 12)))
189 (defcustom imaxima-fnt-size "normalsize"
190 "*Default size of font."
191 :group 'imaxima
192 :type '(choice (const "small")
193 (const "normalsize")
194 (const "large")
195 (const "Large")
196 (const "LARGE")
197 (const "huge")
198 (const "Huge")))
200 (defcustom imaxima-scale-factor 1.0
201 "*All images are scaled by this factor."
202 :group 'imaxima
203 :type 'number)
205 (defcustom imaxima-label-color "red"
206 "*Color used in output labels."
207 :group 'imaxima
208 :type '(color))
210 (defcustom imaxima-equation-color (imaxima-get-fg-color)
211 "*Color used for equations."
212 :group 'imaxima
213 :type '(color))
215 (defcustom imaxima-bg-color nil
216 "Background color of imaxima buffer."
217 :group 'imaxima
218 :type '(choice (color)
219 (const :tag "None" nil)))
221 (defcustom imaxima-fg-color nil
222 "Foreground color of imaxima buffer."
223 :group 'imaxima
224 :type '(choice (color)
225 (const :tag "None" nil)))
227 (defcustom imaxima-latex-preamble ""
228 "*String inserted at the start of the document preamble.
229 This can be used to change, say, the document font.
230 E.g. `\\usepackage{concrete}' will use the Euler math fonts."
231 :group 'imaxima
232 :type '(string))
234 (defcustom imaxima-max-scale 0.85
235 "Maximum amount of scaling allowed to fit wide equations in the buffer.
236 nil means no scaling at all, t allows any scaling."
237 :group 'imaxima
238 :type 'number)
240 (defcustom imaxima-linearize-flag t
241 "Non-nil means that equations too wide to fit in the buffer are linearized."
242 :type '(boolean)
243 :group 'imaxima)
245 (defcustom imaxima-use-maxima-mode-flag nil
246 "Non-nil means that the major mode from `maxima.el' is used."
247 :type '(boolean)
248 :group 'imaxima)
250 (defcustom imaxima-maxima-program "maxima"
251 "Maxima executable."
252 :group 'imaxima
253 :type '(string))
256 (defcustom imaxima-initex-option "-ini"
257 "Option passed to TeX to start initex."
258 :group 'imaxima
259 :type '(string))
261 (defcustom imaxima-tex-program "latex"
262 "TeX executable."
263 :group 'imaxima
264 :type '(string))
266 (defcustom imaxima-gs-program "gs"
267 "Ghostscript executable."
268 :group 'imaxima
269 :type '(string))
271 (defcustom imaxima-gs-options '("-q" "-dNOPAUSE"
272 "-dSAFER"
273 "-dDELAYSAFER"
274 "-DNOPLATFONTS" "-dTextAlphaBits=4"
275 "-dGraphicsAlphaBits=4")
276 "Options passed to gs for conversion from EPS."
277 :group 'imaxima
278 :type '(repeat string))
280 (defcustom imaxima-dvips-program "dvips"
281 "Dvips executable."
282 :group 'imaxima
283 :type '(string))
285 (defcustom imaxima-cp-program "cp"
286 "cp executable."
287 :group 'imaxima
288 :type '(string))
290 (defcustom imaxima-dvips-options '("-E" "-R")
291 "Options passed to dvips for conversion from DVI to EPS."
292 :group 'imaxima
293 :type '(repeat string))
295 (defcustom imaxima-tmp-dir
296 (cond ((featurep 'xemacs)
297 (temp-directory))
298 ((eql system-type 'cygwin)
299 "/tmp/")
300 (t temporary-file-directory))
301 "*Directory used for temporary TeX and image files."
302 :type '(directory)
303 :group 'imaxima)
305 (defcustom imaxima-startup-hook nil
306 "A hook called at startup.
307 This hook is called after imaxima has started Maxima."
308 :group 'imaxima
309 :type 'hook)
311 (defcustom imaxima-exit-hook nil
312 "Hook called when exiting imaxima."
313 :group 'imaxima
314 :type 'hook)
316 (defvar imaxima-tmp-subdir ""
317 "Subdirectory for temporary files.")
319 (defcustom imaxima-lisp-file
320 (if (eq system-type 'windows-nt)
321 (imaxima-subst-char-in-string ?\\ ?/ (locate-library "imaxima.lisp"))
322 (locate-library "imaxima.lisp"))
323 "Location of `imaxima.lisp'."
324 :group 'imaxima
325 :type '(file))
327 (defcustom imaxima-maxima-options
328 (format "--preload-lisp=%s" imaxima-lisp-file)
329 "Arguments passed to Maxima."
330 :group 'imaxima
331 :type '(string))
333 (defcustom imaxima-latex-buffer-name
334 "*imaxima-latex*"
335 "Default name of buffer created by `imaxima-latex'."
336 :group 'imaxima
337 :type '(string))
339 (defcustom imaxima-latex-document-class
340 '("\\documentclass[%dpt,leqno]{article}" imaxima-pt-size)
341 "Default documentclass used by `imaxima-latex'. It should be a
342 valid argument to `format'."
343 :group 'imaxima
344 :type '(sexp))
346 (defcustom imaxima-latex-use-packages
348 \\usepackage{verbatim}
349 \\usepackage{color}
350 \\usepackage{exscale}
351 \\usepackage{amsmath}
352 \\usepackage[cmbase]{flexisym}
353 \\usepackage{breqn}
354 \\setkeys{breqn}{compact}
356 "Default latex packages and configuration used by `imaxima-latex'."
357 :group 'imaxima
358 :type '(string))
360 (defcustom imaxima-latex-document-dimensions
362 \\setlength{\\textwidth}{180mm}
363 \\setlength{\\oddsidemargin}{15mm}
364 \\addtolength{\\oddsidemargin}{-1in}
365 \\setlength{\\evensidemargin}{15mm}
366 \\addtolength{\\evensidemargin}{-1in}
368 "Default dimensions of document created by `imaxima-latex'."
369 :group 'imaxima
370 :type '(string))
372 (defcustom imaxima-latex-macros
374 \\newcommand{\\ifrac}[2]{\\frac{#1}{#2}}
375 \\newcommand{\\ifracd}[2]{\\frac{#1}{#2}}
376 \\newcommand{\\ifracn}[2]{\\frac{#1}{#2}}
377 \\newcommand{\\isubscript}[2]{{#1}_{#2}}
378 \\newcommand{\\iexpt}[2]{{#1}^{#2}}
379 \\newcommand{\\isqrt}[1]{\\sqrt{#1}}
381 "Default macros used by `imaxima-latex'."
382 :group 'imaxima
383 :type '(string))
385 (defcustom imaxima-latex-macros-linear
386 (concat
387 ;; braces in both denominator and numerator
388 "\\renewcommand{\\ifrac}[2]{\\left(#1\\right)/\\left(#2\\right)}"
389 ;; only braces denominator
390 "\\renewcommand{\\ifracd}[2]{#1/\\left(#2\\right)}"
391 ;; only braces in numerator
392 "\\renewcommand{\\ifracn}[2]{\\left(#1\\right)/#2}"
393 "\\renewcommand{\\isubscript}[2]{\\mathrm{subscript}\\left(#1,#2\\right)}"
394 "\\renewcommand{\\iexpt}[2]{\\mathrm{expt}\\left(#1,#2\\right)}"
395 "\\renewcommand{\\isqrt}[1]{\\left(#1\\right)^{1/2}}\n")
396 "Default linear macros used by `imaxima-latex'."
397 :group 'imaxima
398 :type '(string))
400 (defcustom imaxima-latex-macros-format-file
401 (concat
403 \\setlength{\\textheight}{200cm}
404 %% define \\boxed from amsmath.sty
405 \\makeatletter
406 \\providecommand\\boxed{}
407 \\providecommand\\operatorname{}
408 \\renewcommand{\\boxed}[1]{\\fbox{\\m@th$\\displaystyle#1$}}
409 \\renewcommand{\\operatorname}[1]{%
410 \\mathop{\\relax\\kern\\z@\\operator@font{#1}}}
411 \\makeatother
413 "Default macros used by `imaxima-dump-tex'."
414 :group 'imaxima
415 :type '(string))
417 (defcustom imaxima-create-image-options
418 '(:ascent center :mask (heuristic (color-values imaxima-bg-color)))
419 "Optional arguments passed to `imaxima-create-image'"
420 :group 'imaxima
421 :type '(alist))
423 (defcustom imaxima-latex-includegraphics
424 "\\includegraphics{%s}\n"
425 "Includegraphics command."
426 :group 'imaxima
427 :type '(string))
429 (defface imaxima-latex-error-face
430 '((t (:foreground "Blue" :underline t)))
431 "Face used for LaTeX errors."
432 :group 'imaxima)
434 (defvar imaxima-image-creators
435 '((postscript nil)
436 (png ("-sDEVICE=png16m"))
437 (jpeg ("-sDEVICE=jpeg"))
438 (tiff ("-sDEVICE=tiffpack")))
439 "Define functions for generating images.
440 Argument list is passed to gs.")
442 (defvar imaxima-resolution nil
443 "Screen resolution where rendering started.
444 Cons-cell of x and y resolution, given in
445 dots per inch. Buffer-local to rendering buffer.")
446 (make-variable-buffer-local 'imaxima-resolution)
448 (defvar imaxima-output ""
449 "Accumulator for `imaxima-filter'.")
451 (defvar imaxima-gs-output ""
452 "Accumulator for `imaxima-gs-filter'.")
454 (defvar imaxima-process nil)
455 (defvar imaxima-gs-process nil)
456 (defvar imaxima-gs-computing-p nil)
457 (defvar imaxima-gs-7.05-is-broken nil)
459 (defvar imaxima-error-map (make-sparse-keymap)
460 "Keymap for mouse clicks on LaTeX errors.")
462 (defvar imaxima-old-bg-color nil
463 "Old background color.")
465 (defvar imaxima-old-fg-color nil
466 "Old foreground color.")
468 (defvar imaxima-file-counter 0
469 "Counter used for naming temp files.")
471 (defvar imaxima-html-dir "~/")
473 (defvar imaxima-image-map
474 (let ((map (make-sparse-keymap)))
475 (set-keymap-parent map image-map)
476 (define-key map "l" #'imaxima-get-latex-src)
477 (define-key map "g" #'imaxima-gnuplot-replot)
478 (define-key map "s" #'imaxima-gnuplot-restart)
479 map)
480 "Keymap for images in the `imaxima' buffer. The `image-map' is
481 the parent map.")
483 (defcustom imaxima-latex-src-register ?l
484 "The register used by `imaxima-get-latex-src' to save the latex
485 source code for the image under point."
486 :group 'imaxima
487 :type '(character))
489 (defcustom imaxima-gnuplot-replot-term nil
490 "The default gnuplot terminal used by `imaxima-gnuplot-replot'
491 to replot a figure in an external window."
492 :group 'imaxima
493 :type '(choice (string :tag "Terminal") (symbol :tag "nil" nil)))
496 ;; Version
499 (defun imaxima-version ()
500 "Print the package name and the version in the mini buffer"
501 (interactive)
502 (message "%s %s" *imaxima-autoconf-package* *imaxima-autoconf-version*))
505 ;; Reset
508 (defvar imaxima-filter-running nil)
509 (defvar imaxima-continuation nil
510 "The variable is used between `maxima-to-image' and `get-image-from-imaxima' in `imath-mode'.
511 It is used in `imaxima-filter' and `imaxima-filter1' in `imaxima'.
513 The value is either nil or a list of (function buffer pos1 pos2),
514 where pos1 and pos2 are the beginning and end of current maxima
515 formula.")
518 (defun reinit-imaxima ()
519 "Re-initialize imaxima"
520 (interactive)
521 (setq imaxima-filter-running nil
522 imaxima-output nil
523 imaxima-continuation nil))
526 ;; Geometry
529 (defun imaxima-get-geometry (buffer)
530 "Transfer display geometry parameters from current display.
531 Those are put in local variable `imaxima-resolution'. Calculation is done
532 in source buffer specified by BUFF."
533 (let (res)
534 (with-current-buffer buffer
535 (setq res (cons (/ (* 25.4 (imaxima-display-pixel-width))
536 (imaxima-display-mm-width))
537 (/ (* 25.4 (imaxima-display-pixel-height))
538 (imaxima-display-mm-height)))))
539 (setq imaxima-resolution res)))
541 (defun imaxima-get-window-width-xemacs ()
542 "Return window width in mm.
543 XEmacs version."
544 (/ (* (window-text-area-pixel-width) (imaxima-display-mm-width))
545 (imaxima-display-pixel-width)))
547 (defun imaxima-get-window-width-emacs ()
548 "Return window width in mm.
549 Emacs version."
550 (/ (* (- (window-width) 1) (frame-char-width))
551 (/ (float (imaxima-display-pixel-width))
552 (imaxima-display-mm-width))))
554 (defun imaxima-bp-to-mm (bp)
555 "Convert PostScript big points to mm. BP is size in big points."
556 (* bp 0.352778))
558 (defun imaxima-color-to-rgb (str)
559 "Convert color name STR to rgb values understood by TeX."
560 (mapcar #'(lambda (x) (/ x 65535.0)) (imaxima-color-values str)))
562 (defmacro imaxima-with-temp-dir (dir &rest body)
563 "Change to DIR temporarily and execute BODY."
564 (let ((wd (make-symbol "wd")))
565 `(let ((,wd default-directory))
566 (cd ,dir)
567 (unwind-protect
568 (progn
569 ,@body)
570 (cd ,wd)))))
572 (defvar imaxima-silence-filter nil)
573 (defmacro imaxima-with-no-new-input-prompt (&rest body)
574 "Set `imaxima-silence-filter' to t to silence any output
575 through `imaxima-filter'."
576 `(progn (setq imaxima-silence-filter t) ,@body))
579 ;; Gs stuff
582 (defun imaxima-gs-filter (process str)
583 "Set `imaxima-gs-computing-p' to t when gs is done."
584 (setq imaxima-gs-output (concat imaxima-gs-output str))
585 (when (string-match "GS\\(<[0-9+]\\)?>" imaxima-gs-output)
586 (setq imaxima-gs-computing-p nil)
587 (setq imaxima-gs-output "")))
589 (defun imaxima-gs-wait ()
590 "Wait for gs to finish."
591 (while (and imaxima-gs-computing-p
592 (eq (process-status imaxima-gs-process) 'run))
593 (accept-process-output imaxima-gs-process 1)))
595 (defun imaxima-start-gs ()
596 "Start Ghostscript as an asynchronyous process."
597 ;; Are we using the broken GNU Ghostscript 7.05?
598 (setq imaxima-gs-7.05-is-broken
599 (string-match "\\(GNU\\|ESP\\) Ghostscript 7.05"
600 (shell-command-to-string
601 (concat imaxima-gs-program " --help"))))
602 (let* (output
603 (type (cadr (assq imaxima-image-type imaxima-image-creators)))
604 (gs-args (append imaxima-gs-options
605 type
606 (list (format "-r%gx%g" (car imaxima-resolution)
607 (cdr imaxima-resolution))))))
608 (when (and imaxima-gs-process
609 (processp imaxima-gs-process))
610 (delete-process imaxima-gs-process))
611 (setq imaxima-gs-computing-p t)
612 (condition-case nil
613 (setq imaxima-gs-process (apply 'start-process "imaxima-gs"
614 " *imaxima gs output*"
615 imaxima-gs-program gs-args))
616 (error (error
617 "Sorry, Ghostscript could not be started. Please check
618 that you have gs in your path or customize the value of
619 `imaxima-gs-program' (current values is \"%s\").
621 imaxima-gs-program
622 (if (imaxima-image-type-available-p 'postscript)
623 "If Ghostscript isn't installed you can set `imaxima-image-type' to `ps'."
624 ;; don't offer this advice in XEmacs, which doesn't support ps.
625 ""))))
626 (set-process-filter imaxima-gs-process 'imaxima-gs-filter)
627 (imaxima-gs-wait)
628 (set-process-query-on-exit-flag imaxima-gs-process nil)
629 (unless (eq (process-status imaxima-gs-process) 'run)
630 (setq output (shell-command-to-string (concat imaxima-gs-program " -h")))
631 (cond
632 ((null (string-match (car type) output))
633 (error
634 "Your version Ghostscript does not appear to support the image type %s.
635 The command \"gs -h\" lists the available devices.
636 You can change the image type in `imaxima-image-type' or the device name
637 associated with an image type in `imaxma-image-creators'" (car type)))
638 (t (error
639 "Some of the options passed to Ghostscript are probably not supported
640 by your version. In particular \"-dTextAlphaBits=4\" and \"-dGraphicsAlphaBits=4\"
641 are not supported by gs 5.5 or earlier. Please edit `imaxima-gs-options'"))))))
643 (defun imaxima-extract-bb (filename)
644 "Extract EPS bounding box vector from FILENAME.
645 Returns a list of bounding box, width, and height."
646 (with-temp-buffer
647 (insert-file-contents-literally filename nil 0 1024 t)
648 (goto-char (point-min))
649 (when (search-forward-regexp "%%BoundingBox:\
650 +\\([-+]?[0-9.]+\\)\
651 +\\([-+]?[0-9.]+\\)\
652 +\\([-+]?[0-9.]+\\)\
653 +\\([-+]?[0-9.]+\\)" nil t)
654 (let ((bb
655 (vector
656 (floor (string-to-number (match-string 1)))
657 (floor (string-to-number (match-string 2)))
658 (ceiling (string-to-number (match-string 3)))
659 (ceiling (string-to-number (match-string 4))))))
660 (list bb
661 (- (aref bb 2) (aref bb 0))
662 (- (aref bb 3) (aref bb 1)))))))
664 (defun imaxima-eps-scale (file bb scale)
665 "Scale the eps image in FILE with factor SCALE.
666 BB is the bounding box of the image. Returns a list of new bounding
667 box, width, and height."
668 (cl-multiple-value-bind (llx lly urx ury) (append bb nil)
669 (let ((x (round (* (- urx llx) scale)))
670 (y (round (* (- ury lly) scale)))
671 (buff (find-file-noselect file)))
672 (unwind-protect
673 (with-current-buffer buff
674 (goto-char (point-min))
675 (search-forward "%%BoundingBox")
676 (delete-region (line-beginning-position) (line-end-position))
677 (insert (format "%%%%BoundingBox: 0 0 %d %d\n" x y))
678 (search-forward "%%EndComments")
679 (forward-line)
680 (insert "%%BeginProcSet: imaxima 1 0\ngsave\n")
681 (insert (format "%f %f translate\n"
682 (- (* llx scale))
683 (- (* lly scale))))
684 (insert (format "%f %f scale\n" scale scale))
685 (insert "%%EndProcSet\n")
686 (goto-char (point-max))
687 (insert "\ngrestore\n")
688 (save-buffer))
689 (kill-buffer buff))
690 (list (vector 0 0 x y) x y))))
692 (defun imaxima-latex-document-class ()
693 (apply #'format (mapcar #'eval imaxima-latex-document-class)))
695 (defun imaxima-latex ()
696 "Convert Maxima buffer to LaTeX.
697 This command does not work in XEmacs."
698 (interactive)
699 (let (pos2 label (pos (make-marker))
700 (buf (generate-new-buffer imaxima-latex-buffer-name))
701 (oldbuf (current-buffer)))
702 (set-buffer buf)
703 (insert (imaxima-latex-document-class) "\n"
704 imaxima-latex-use-packages "\n"
705 imaxima-latex-document-dimensions "\n"
706 imaxima-latex-macros "\n"
707 "\\begin{document}\n"
709 (save-excursion
710 (insert-buffer-substring oldbuf))
711 (while (not (eobp))
712 (let* ((region-start (copy-marker (point)))
713 (region-end (copy-marker (next-single-property-change (point) 'display nil (point-max))))
714 (text-prop (get-text-property region-start 'display))
715 (latex-prop (get-text-property region-start 'latex)))
716 (if latex-prop
717 (progn
718 (delete-region region-start region-end)
719 (goto-char region-start)
720 (insert (concat latex-prop "\n\n")))
721 (progn
722 (goto-char region-start)
723 (insert "\n\\begin{verbatim}\n")
724 (goto-char region-end)
725 (insert "\n\\end{verbatim}\n\n")))))
726 (insert "\n\\end{document}")
727 (switch-to-buffer-other-window buf)
728 (latex-mode)))
730 (defun imaxima-process-sentinel (process event)
731 "Process sentinel for Maxima process."
732 (message "Process %s %s" process event)
733 (unless (eq (process-status process) 'run)
734 (imaxima-clean-up)))
736 (defun imaxima-restart-gs ()
737 "Kill Ghostscript process and start a new one."
738 (kill-process imaxima-gs-process)
739 (imaxima-start-gs))
741 (defun imaxima-maybe-restart-gs ()
742 "Unless `imaxima-gs-process' is running, call
743 `imaxima-start-gs' to (re)start GS."
744 (unless (and (processp imaxima-gs-process)
745 (eq (process-status imaxima-gs-process) 'run))
746 (imaxima-start-gs)))
748 (defun imaxima-ps-to-image (psfilename filename bb width height)
749 "Convert eps file PSFILENAME to a bitmap image file FILENAME.
750 BB is the bounding box for eps image. WIDTH and HEIGHT are the
751 dimensions of the image."
752 (imaxima-maybe-restart-gs)
753 (setq imaxima-gs-computing-p t)
754 (when (eq system-type 'windows-nt)
755 (setq psfilename (imaxima-subst-char-in-string ?\\ ?/ psfilename))
756 (setq filename (imaxima-subst-char-in-string ?\\ ?/ filename)))
757 (process-send-string imaxima-gs-process
758 (format
759 (if imaxima-gs-7.05-is-broken
760 "clear /imaxima-state save def \
761 << /PageSize [%d %d] /PageOffset [%d %d] /OutputFile (%s) >> \
762 setpagedevice (%s) run imaxima-state restore\n"
763 "clear \
764 << \
765 /PageSize [%d %d] /PageOffset [%d %d] /OutputFile (%s) \
766 >> setpagedevice [save] (%s) (r) file cvx \
767 systemdict /.runandhide known revision 700 ge and {.setsafe {.runandhide}} if \
768 stopped {handleerror quit} if count 1 ne {quit} if \
769 cleardictstack 0 get restore\n")
770 width
771 height
772 (- (aref bb 0))
773 (aref bb 1)
774 filename
775 psfilename))
776 (imaxima-gs-wait))
778 (defvar imaxima-latex-src nil)
780 (defun imaxima-check-plot-output (str)
781 "If the str is in the form ^Wpompt^W\\verb|plotfile filename|, then
782 filename is returned. Else, nil is returned."
783 ;; (if (string-match "\x17[^\x17]*\x17\\\\mathrm{wxxmltag}\\\\left\(\\\\verb|\\([^|]*\\)|.*" str)
784 ;; (if (string-match "\x17[^\x17]*\x17\\\\mathrm{wxxmltag}\\\\left[^/]*\\(.*\.eps\\).*" str)
785 ;; (if (string-match "\x17[^\x17]*\x17\\\\mathrm{wxxmltag}\\\\left\(\\\\verb|*\\(.*\.eps\\).*" str)
786 (when (string-match "\x17[^\x17]*\x17\\\\mathrm{wxxmltag}\\\\left\(\\(\\\\verb|\\)*\\(.*\.eps\\).*" str)
787 (let ((plot-file (match-string 2 str)))
788 (setq imaxima-latex-src (format imaxima-latex-includegraphics plot-file))
789 plot-file)))
791 (defun imaxima-create-image (filename type &rest options)
792 (apply #'create-image (append (list filename type) options imaxima-create-image-options)))
794 (defun imaxima-make-image (str eps-or-latex &optional no-label-p)
795 "Make image from STR. If no-label-p is specified t,
796 label becomes nil and passed to imaxima-tex-to-dvi."
797 (let ((res (imaxima-check-plot-output str)))
798 (if res
799 (setq str res
800 eps-or-latex 'eps)))
802 (let* ((filename (expand-file-name
803 (number-to-string (cl-incf imaxima-file-counter))
804 imaxima-tmp-subdir))
805 (psfilename (concat filename ".ps"))
806 (label "*"))
808 (cond ((eql eps-or-latex 'latex)
809 (when (string-match "\\(\x17\\([^\x17]*\\)\x17\\)" str)
810 (setq label (match-string 2 str))
811 (setq str (replace-match "" t t str 1)))
812 (if no-label-p (setq label nil)) ;; override label to nil if no-label-p
813 (imaxima-tex-to-dvi str label (concat filename ".tex"))
814 (imaxima-dvi-to-ps filename)
816 ((eql eps-or-latex 'eps)
817 (copy-file str psfilename)
818 (setq label nil)
821 (if (not (file-exists-p psfilename))
822 (imaxima-latex-error str filename)
823 (cl-multiple-value-bind (bb width height)
824 (imaxima-extract-bb psfilename)
825 (let ((ratio (/ (imaxima-get-window-width)
826 (imaxima-bp-to-mm width))))
827 (when (< ratio 1.0)
828 ;; image is wider than the buffer
829 (if (or (eql eps-or-latex 'eps)
830 (and imaxima-max-scale
831 (or (eq imaxima-max-scale t)
832 (> ratio imaxima-max-scale))))
833 ;; scale image
834 (cl-multiple-value-setq (bb width height)
835 (imaxima-eps-scale psfilename bb ratio))
836 (when imaxima-linearize-flag
837 ;; linearize image
838 (imaxima-tex-to-dvi str label (concat filename ".tex") t)
839 (imaxima-dvi-to-ps filename)
840 (cl-multiple-value-setq (bb width height)
841 (imaxima-extract-bb psfilename))))))
842 (unless (eq imaxima-image-type 'postscript)
843 (imaxima-ps-to-image psfilename filename bb width height)
844 ;; FIXME:
845 ;; Ghostscript on Windows doesn't flush the image to the file.
846 ;; So we have to kill the process and restart. What a kludge!
847 (when (eq system-type 'windows-nt)
848 (imaxima-restart-gs)))
849 (cond ((featurep 'xemacs)
850 ;; FIXME:
851 (when (eq system-type 'windows-nt)
852 (imaxima-restart-gs))
853 (xemacs-set-imagefile-properties filename imaxima-image-type str))
855 (prog1
856 (propertize (concat "(" label ") " str) 'display
857 (apply #'imaxima-create-image
858 (if (eq imaxima-image-type 'postscript)
859 (list psfilename imaxima-image-type nil (list :pt-width width :pt-height height :bounding-box bb))
860 (list filename imaxima-image-type nil)))
861 'keymap imaxima-image-map
862 'help-echo "o: save to file\ng: replot in window\nl: get latex source\nr: rotate by 90° clockwise\ns: restart gnuplot\n+: increase size by 20%\n-: decrease size by 20%"
863 'latex imaxima-latex-src)
864 (setq imaxima-latex-src nil))))))))
867 (defun imaxima-latex-error (str filename)
868 "Make clickable error message.
869 STR is offending LaTeX expression. FILENAME is name of the LaTeX file."
870 (let* ((msg "LaTeX error in: ")
871 (delim (if (featurep 'xemacs)
872 "; " "\n"))
873 imaxima-error-2
874 imaxima-error-3
875 (error-text (concat "mouse-2: view LaTeX error log" delim
876 "mouse-3: view LaTeX source")))
877 (fset 'imaxima-error-2
878 `(lambda ()
879 (interactive)
880 (view-file-other-window (concat ,filename ".log"))))
881 (fset 'imaxima-error-3
882 `(lambda ()
883 (interactive)
884 (view-file-other-window (concat ,filename ".tex"))))
885 (define-key imaxima-error-map imaxima-mouse2 'imaxima-error-2)
886 (define-key imaxima-error-map [(return)] 'imaxima-error-2)
887 (define-key imaxima-error-map imaxima-mouse3 'imaxima-error-3)
888 (define-key imaxima-error-map [(meta return)] 'imaxima-error-3)
889 (set-text-properties 0 14 `(face imaxima-latex-error-face
890 mouse-face highlight
891 help-echo ,error-text
892 keymap ,imaxima-error-map)
893 msg)
894 (concat msg str)))
896 (defun imaxima-dump-tex ()
897 "Dump a TeX format file preloaded with the required packages."
898 (if imaxima-mylatex
899 (with-temp-file (expand-file-name "mylatex.ltx" imaxima-tmp-subdir)
900 (insert imaxima-mylatex)))
901 (with-temp-file (expand-file-name "format.tex" imaxima-tmp-subdir)
902 (insert
903 (imaxima-latex-document-class)
904 imaxima-latex-preamble "\n"
905 imaxima-latex-use-packages "\n"
906 imaxima-latex-macros-format-file "\n"
907 imaxima-latex-macros "\n"
908 "\\begin{document}\n"
909 "\\end{document}"))
910 (imaxima-with-temp-dir
911 imaxima-tmp-subdir
912 (apply #'call-process imaxima-tex-program nil nil nil
913 (list imaxima-initex-option "&latex" "mylatex.ltx" "format"))))
915 (defun imaxima-latex-set-textwidth ()
916 (format "\\setlength{\\textwidth}{%dmm}\n"
917 (round (/ (imaxima-get-window-width)
918 imaxima-scale-factor))))
919 (defun imaxima-latex-set-pagecolor ()
920 (apply #'format "\\pagecolor[rgb]{%f,%f,%f}\n"
921 (imaxima-color-to-rgb (imaxima-get-bg-color))))
922 (defun imaxima-latex-set-labelcolor ()
923 (apply #'format "\\color[rgb]{%f,%f,%f}\n"
924 (imaxima-color-to-rgb imaxima-label-color)))
925 (defun imaxima-latex-format-label (str label)
926 (concat
927 (format "\\begin{dmath}[number={%s}]\n" label)
928 (apply #'format "\\color[rgb]{%f,%f,%f}\n"
929 (imaxima-color-to-rgb imaxima-equation-color))
930 str (format "\n\\end{dmath}\n")))
931 (defun imaxima-latex-format-output (str)
932 (concat
933 (apply #'format "\\color[rgb]{%f,%f,%f}\n"
934 (imaxima-color-to-rgb imaxima-equation-color))
935 "\\begin{math} \\displaystyle \n" str (format "\n\\end{math}\n")))
936 (defun imaxima-get-latex-src ()
937 (interactive)
938 (set-register imaxima-latex-src-register (get-text-property (point) 'latex)))
940 ;;;;;;;;;;;;;;;;;;;; GNUPLOT Support ;;;;;;;;;;;;;;;;;;;;
941 (defun imaxima-get-gnuplot-file ()
942 (interactive)
943 (let ((src (get-text-property (point) 'latex)))
944 (if (string-match (format "\\(%s/maxout_[0-9]+\\)" imaxima-tmp-subdir) src)
945 (concat (match-string 1 src) ".gnuplot"))))
947 (defcustom imaxima-gnuplot-buffer "*imaxima-gnuplot*"
948 "Name of the buffer created by `imaxima-start-gnuplot' and used by `imaxima-gnuplot-replot'. The sentinel `imaxima-gnuplot-sentinel' monitors the `gnuplot' process in this buffer.")
949 (defcustom imaxima-gnuplot-command "gnuplot"
950 "Name, or complete pathname, of the `gnuplot' binary. Used by `imaxima-start-gnuplot'.")
951 (defcustom imaxima-gnuplot-args ""
952 "Optional arguments passed to the `gnuplot' binary when started by `imaxima-start-gnuplot'.")
953 (defvar imaxima-gnuplot-process nil
954 "A holder for the `gnuplot' process started in `imaxima-start-gnuplot'.")
956 (defun imaxima-gnuplot-sentinel (process desc)
957 (let ((state (process-status process)))
958 (message "imaxima-gnuplot-sentinel: state: %s\ndescription: %s" state desc)))
959 (defun imaxima-start-gnuplot ()
960 (save-mark-and-excursion
961 (let* ((gbuf (get-buffer-create imaxima-gnuplot-buffer))
962 (proc (and gbuf (get-buffer-process gbuf)))
963 (state (and proc (process-status proc))))
964 (cond ((and gbuf (eq state 'run))
965 (message "Gnuplot running."))
966 (gbuf
967 (message "Re-starting gnuplot...")
968 (setq imaxima-gnuplot-process (start-process (buffer-name gbuf) gbuf imaxima-gnuplot-command "-" imaxima-gnuplot-args)))
970 ;; gbuf does not exist
971 (error "Failed to create buffer %s to run %s. Stop." imaxima-gnuplot-buffer imaxima-gnuplot-command)))
972 (if (and gbuf imaxima-gnuplot-process)
973 (set-process-sentinel imaxima-gnuplot-process #'imaxima-gnuplot-sentinel)
974 (error "imaxima-start-gnuplot failed.")))))
976 (defun imaxima-gnuplot-restart ()
977 "Forcibly restart `gnuplot' process."
978 (interactive)
979 (let ((kill-buffer-query-functions '())
980 (kill-buffer-hook '()))
981 (if (get-buffer imaxima-gnuplot-buffer) (kill-buffer imaxima-gnuplot-buffer)))
982 (imaxima-start-gnuplot))
984 (defun imaxima-gnuplot-replot ()
985 "Replot the Gnuplot graph under point in an external
986 window. Suppress a new input prompt."
987 (interactive)
988 (imaxima-start-gnuplot)
989 (let ((term (or imaxima-gnuplot-replot-term (read-from-minibuffer "Terminal? "))))
990 (when (and (null imaxima-gnuplot-replot-term) (y-or-n-p "Save this as the session default? "))
991 (setq imaxima-gnuplot-replot-term term))
992 (let ((gplt-src (imaxima-get-gnuplot-file)))
993 (when gplt-src
994 (message "file=%s" gplt-src)
995 (save-mark-and-excursion
996 (with-temp-file gplt-src
997 (insert-file-contents gplt-src)
998 (goto-char (point-min))
999 (while (re-search-forward "^set term.+" nil t)
1000 (replace-match "# \\&")
1001 (insert (concat "\nset terminal " term)))
1002 (goto-char (point-min))
1003 (while (re-search-forward "^set out.+" nil t)
1004 (replace-match "# \\&")))
1005 (with-current-buffer imaxima-gnuplot-buffer
1006 (comint-send-string imaxima-gnuplot-process (concat "load \"" (shell-quote-argument gplt-src) "\"\n")))
1007 )))))
1009 (defvar imaxima-latex-src nil "The LaTeX code to generate the
1010 current image. Used by `imaxima-make-image' to set the `latex'
1011 property of the inserted text/image.")
1013 (defun imaxima-tex-to-dvi (str label filename &optional linear)
1014 "Run LaTeX on STR.
1015 Argument LABEL is used as equation label. FILENAME is used for
1016 temporary files. Use linearized form if LINEAR is non-nil."
1017 (let ((latex-src
1018 (concat
1019 (imaxima-latex-set-pagecolor)
1020 "\\pagestyle{empty}\n"
1021 (format "\\begin{%s}\n" imaxima-fnt-size)
1022 (imaxima-latex-set-labelcolor)
1023 (if label
1024 (imaxima-latex-format-label str label)
1025 (imaxima-latex-format-output str))
1026 (format "\\end{%s}\n" imaxima-fnt-size))))
1027 (setq imaxima-latex-src latex-src)
1028 (with-temp-file filename
1029 (insert
1030 (imaxima-latex-document-class)
1031 "\n% mylatex\n" ; magic string
1032 (imaxima-latex-set-textwidth)
1033 (if linear imaxima-latex-macros-linear "")
1034 "\\begin{document}\n"
1035 latex-src
1036 "\\end{document}"))
1037 (imaxima-with-temp-dir imaxima-tmp-subdir
1038 (apply 'call-process imaxima-tex-program nil nil nil
1039 (list "&mylatex" filename)))))
1041 (defun imaxima-dvi-to-ps (filename)
1042 "Convert dvi file FILENAME to PostScript."
1043 (let ((dvips-args (append
1044 imaxima-dvips-options
1045 (list "-x" (format "%s" (* imaxima-scale-factor 1000))
1046 "-y" (format "%s" (* imaxima-scale-factor 1000))
1047 (concat filename ".dvi") "-o"))))
1048 (imaxima-with-temp-dir imaxima-tmp-subdir
1049 (apply 'call-process imaxima-dvips-program nil nil nil dvips-args))))
1051 (defun imaxima-clean-up ()
1052 "Kill gs process, delete temporary files and restore colors if applicable."
1053 (interactive)
1054 (ignore-errors
1055 (kill-process imaxima-gs-process))
1056 (mapc 'delete-file (directory-files imaxima-tmp-subdir t "^[^.].*"))
1057 (delete-directory imaxima-tmp-subdir)
1058 (if (featurep 'xemacs)
1059 (ad-deactivate 'comint-output-filter)
1060 ;; restore frame colors in Emacs
1061 (when imaxima-fg-color
1062 (modify-frame-parameters
1063 nil (list (cons 'foreground-color imaxima-old-fg-color))))
1064 (when imaxima-bg-color
1065 (modify-frame-parameters
1066 nil (list (cons 'background-color imaxima-old-bg-color)))))
1067 (run-hooks 'imaxima-exit-hook))
1069 (defvar *debug-imaxima-filter* nil
1070 "If `*debug-imaxima-filter*' is set to t, the string is
1071 appended to the end of the buffer *imaxima-work*. Used in
1072 `imaxima-filter1' and `debug-imaxima-filter'.")
1074 (defun debug-imaxima-filter (str)
1075 (if *debug-imaxima-filter*
1076 (with-current-buffer (get-buffer-create "*imaxima-work*")
1077 (insert str))))
1079 (cl-defun imaxima-filter (str)
1080 "Parse output from Maxima and make image from TeX parts.
1081 Argument STR contains output received from Maxima.
1083 imaxima-filter needs to be written in re-entrant manner.
1084 This is because during the creation of latex image, there
1085 observed a reentrant call of imaxima-filter. yhonda"
1086 (if imaxima-filter-running
1087 (progn
1088 (setq imaxima-output (concat imaxima-output str))
1089 (debug-imaxima-filter "reenter")
1090 (cl-return-from imaxima-filter "")))
1091 (setq imaxima-filter-running t)
1092 (debug-imaxima-filter str)
1093 (let* ((len (length str))
1094 main-output)
1095 (if (zerop len)
1096 (progn
1097 (setq imaxima-filter-running nil)
1098 (cl-return-from imaxima-filter ""))
1099 (setq imaxima-output (concat imaxima-output str))
1100 (let ((lastchar (aref str (1- len)))
1101 (output ""))
1102 (when (and (char-equal lastchar ?\n) (> len 1))
1103 (setq lastchar (aref str (- len 2))))
1105 (message "Processing Maxima output...")
1106 (while (not (string= imaxima-output ""))
1107 (let ((1stchar (substring imaxima-output 0 1)))
1108 (cond ((string= 1stchar "\x03")
1109 (if (string-match "\x03\\([^\x03\x04]*\\)\x04\\(\\(.\\|\n\\)*\\)" imaxima-output)
1110 (let ((iprompt (match-string 1 imaxima-output))
1111 (rest (match-string 2 imaxima-output)))
1112 (setq imaxima-output rest)
1113 (setq output (concat output iprompt))
1114 ;; All the output for a maxima command are processed.
1115 ;; We can call imaxima-continuation if necessary.
1116 (cond ((and imaxima-continuation main-output)
1117 (funcall (car imaxima-continuation) main-output))
1118 ((and imaxima-continuation (null main-output))
1119 (funcall (car imaxima-continuation) ""))))
1120 ;; imaxima-output is incomplete.
1121 (setq imaxima-filter-running nil)
1122 (cl-return-from imaxima-filter output)))
1123 ((string= 1stchar "\x02")
1124 (if (string-match "\x02\\([^\x02\x05]*\\)\x05\\(\\(.\\|\n\\)*\\)" imaxima-output)
1125 (let ((match (match-string 1 imaxima-output))
1126 (rest (match-string 2 imaxima-output))
1127 image)
1128 (setq imaxima-output rest)
1129 (setq output (concat output (setq image (imaxima-make-image match 'latex)) "\n"))
1130 ;; Remember the image into main-output if this is the first output.
1131 ;; This will be passed to imaxima-continuation
1132 (if (null main-output)
1133 (setq main-output image)))
1134 ;; imaxima-output is incomplete.
1135 (setq imaxima-filter-running nil)
1136 (cl-return-from imaxima-filter output)))
1137 ((string= 1stchar "\x15")
1138 (if (string-match "\x15\\([^\x15\x16]*\\)\x16\\(\\(.\\|\n\\)*\\)" imaxima-output)
1139 (let ((match (match-string 1 imaxima-output))
1140 (rest (match-string 2 imaxima-output)))
1141 (setq imaxima-output rest)
1142 (setq output (concat output (imaxima-make-image match 'latex) "\n")))
1143 ;; imaxima-output is incomplete.
1144 (setq imaxima-filter-running nil)
1145 (cl-return-from imaxima-filter output)))
1146 (t (if (string-match "\\([^\x02\x03\x15]*\\)\\(\\(.\\|\n\\)*\\)" imaxima-output)
1147 (let ((match (match-string 1 imaxima-output))
1148 (rest (match-string 2 imaxima-output)))
1149 (setq imaxima-output rest)
1150 (setq output (concat output match)))
1151 ;; This should not happen.
1152 (message "Unexpected error encountered in imaxima-filter"))))))
1153 (message "Processing Maxima output...done")
1154 (if imaxima-silence-filter (setq output "" imaxima-silence-filter nil))
1155 (setq imaxima-filter-running nil)
1156 (cl-return-from imaxima-filter output)))))
1158 (defun imaxima-filter1 (str)
1159 "Parse output from Maxima and make image from TeX parts.
1160 Argument STR contains output received from Maxima."
1161 (if *debug-imaxima-filter*
1162 (with-current-buffer (get-buffer-create "*imaxima-work*")
1163 (insert "****new string****
1165 (insert str)))
1166 (let* ((len (length str)))
1167 (if (zerop len)
1169 (setq imaxima-output (concat imaxima-output str))
1170 (let ((lastchar (aref str (1- len))))
1171 (when (and (char-equal lastchar ?\n) (> len 1))
1172 (setq lastchar (aref str (- len 2))))
1173 (cond
1174 ;; Plain text
1175 ((string-match "\\`[^\x02\x05\x03\x04\x15\x16]+\\'" imaxima-output)
1176 (prog1 imaxima-output
1177 (setq imaxima-output "")))
1178 ((or (char-equal lastchar ?\x04) (char-equal lastchar ?\x05))
1179 (let ((prompt "")
1180 (newline-char "
1182 (output "")
1183 (rest imaxima-output)
1184 text match)
1185 (message "Processing Maxima output...")
1186 (if (string-match "\\([^\x03\x04]*\\)\x03\\([^\x03\x04]*\\)\x04$" imaxima-output)
1187 (setq prompt (concat "" (match-string 2 imaxima-output))
1188 rest (match-string 1 imaxima-output)))
1189 (while (string-match "\\(\\([^\x02\x05]*\\)\x02\\([^\x02\x05]*\\)\x05\\)"
1190 rest)
1191 (setq text (match-string 2 rest))
1192 (setq match (match-string 3 rest))
1193 (setq rest (replace-match "" t t rest 1))
1194 (setq output (concat output (if (equal output "") "" newline-char) text (imaxima-make-image match 'latex))))
1195 (setq imaxima-output "")
1196 (message "Processing Maxima output...done")
1197 (if imaxima-continuation
1198 (funcall (car imaxima-continuation) output))
1199 (concat output rest prompt)))
1200 ;; Special prompt for demo() function.
1201 ;; _ is prompted.
1202 ((char-equal lastchar ?_)
1203 (let ((newline-char "
1205 (output "")
1206 (rest (substring imaxima-output 0 -1))
1207 match text)
1208 (message "Processing Maxima output...")
1209 (while (string-match "\\(\\([^\x02\x05]*\\)\x02\\([^\x02\x05]*\\)\x05\\)"
1210 rest)
1211 (setq text (match-string 2 rest))
1212 (setq match (match-string 3 rest))
1213 (setq rest (replace-match "" t t rest 1))
1214 (setq output (concat output (if (equal output "") "" newline-char) text (imaxima-make-image match 'latex))))
1215 (setq imaxima-output "")
1216 (message "Processing Maxima output...done")
1217 (if imaxima-continuation
1218 (funcall (car imaxima-continuation) output))
1219 (concat " " output rest newline-char "_")))
1220 ;; Special prompt, question.
1221 ((char-equal lastchar ?\x16)
1222 (string-match "\x15\\([^\x16]*\\)\x16" imaxima-output)
1223 (prog1 (imaxima-make-image (match-string 1 imaxima-output) 'latex)
1224 (setq imaxima-output "")))
1225 (t ""))))))
1227 (defun xemacs-set-imagefile-properties (filename img-type str)
1228 (let ((ext (make-extent 0 (length str) str)))
1229 (set-extent-property ext 'duplicable t)
1230 (set-extent-end-glyph ext
1231 (make-glyph (vector img-type
1232 :file filename)))
1233 (set-extent-property ext 'invisible t)
1234 (set-extent-property ext 'atomic t))
1235 str)
1237 (eval-when-compile
1238 (ignore-errors
1239 (require 'maxima)))
1241 (defun imaxima-setup-preoutput-filter ()
1242 "Set up `comint-preoutput-filter-functions' or the equivalent."
1243 (cond ((featurep 'xemacs)
1244 ;; XEmacs does not have comint-preoutput-filter-functions, so
1245 ;; we have to advice comint-output-filter instead
1246 (defadvice comint-output-filter (before preoutput-filter)
1247 "Run comint-preoutput-filter-functions."
1248 (ad-set-arg 1 (imaxima-filter (ad-get-arg 1))))
1249 (ad-activate 'comint-output-filter))
1251 (make-local-variable 'comint-preoutput-filter-functions)
1252 ;; This doesn't work due to a bug in comint.el
1253 ;; (add-hook 'comint-preoutput-filter-functions 'imaxima-filter nil t)
1254 (add-hook 'comint-preoutput-filter-functions 'imaxima-filter t))))
1256 (defun imaxima-change-color (buf)
1257 "Change background and foreground color if applicable.
1258 BUF is imaxima buffer."
1259 (cond
1260 ((featurep 'xemacs)
1261 (when imaxima-bg-color
1262 (set-face-background 'default imaxima-bg-color buf))
1263 (when imaxima-fg-color
1264 (set-face-foreground 'default imaxima-fg-color buf)))
1266 (when imaxima-bg-color
1267 (setq imaxima-old-bg-color (frame-parameter nil 'background-color))
1268 (modify-frame-parameters
1269 nil (list (cons 'background-color imaxima-bg-color))))
1270 (when imaxima-fg-color
1271 (setq imaxima-old-fg-color (frame-parameter nil 'foreground-color))
1272 (modify-frame-parameters
1273 nil (list (cons 'foreground-color imaxima-fg-color)))))))
1275 (defun imaxima-setup ()
1276 "Image support for maxima.el."
1277 (let ((mbuf (process-buffer inferior-maxima-process)))
1278 (with-current-buffer mbuf
1279 (imaxima-change-color mbuf)
1280 (imaxima-get-geometry mbuf)
1281 (imaxima-dump-tex)
1282 (unless (eq imaxima-image-type 'postscript)
1283 (imaxima-start-gs))
1284 (add-hook 'kill-buffer-hook 'imaxima-clean-up t t)
1285 (imaxima-setup-preoutput-filter)
1286 (imaxima-with-no-new-input-prompt
1287 (comint-send-string mbuf (format ":lisp (progn ($load \"%s\") (msetq $imaxima_tmp_subdir \"%s\"))\n" imaxima-lisp-file imaxima-tmp-subdir)))
1288 ;; maxima mode tries to run inferior-maxima-mode-hook twice
1289 ;; due to changes made in 5.9.2 release. To prevent this,
1290 ;; the following hook must be removed earlier than before.
1291 ;; y.honda
1292 (remove-hook 'inferior-maxima-mode-hook 'imaxima-setup)
1293 (goto-char (point-max)))))
1295 (cl-defun imaxima-delete-maxima-hooks ()
1296 (remove-hook 'comint-output-filter-functions 'inferior-maxima-output-filter)
1297 (remove-hook 'comint-output-filter-functions 'inferior-maxima-remove-double-input-prompt)
1298 (remove-hook 'comint-output-filter-functions 'inferior-maxima-remove-double-prompt))
1300 (cl-defun imaxima ()
1301 "Image support for Maxima.
1302 \"display2d:true\" in Maxima turns images off, \"display2d:imaxima\"
1303 turns them on. Set `imaxima-use-maxima-mode-flag' to t to use
1304 `maxima.el'."
1305 (interactive)
1306 (if (not window-system)
1307 (error "Emacs in terminal is not supported by Imaxima. You need to run Emacs in a window system, such as X window, Mac OS X, and Microsoft Windows OS."))
1308 (let ((imaxima-buffer))
1309 (setq imaxima-buffer
1310 (get-buffer (if imaxima-use-maxima-mode-flag
1311 "*maxima*"
1312 "*imaxima*")))
1313 (when imaxima-buffer
1314 (if (called-interactively-p 'any)
1315 (switch-to-buffer imaxima-buffer)
1316 (set-buffer imaxima-buffer))
1317 (cl-return-from imaxima t)))
1318 (reinit-imaxima)
1319 (unless (imaxima-image-type-available-p imaxima-image-type)
1320 (error "Your version of Emacs does not support the image type %s"
1321 imaxima-image-type))
1322 (unless imaxima-lisp-file
1323 (error "The file imaxima.lisp could not be found.
1324 Please customize the option `imaxima-lisp-file'."))
1325 (setq imaxima-file-counter 0)
1326 (make-directory
1327 (setq imaxima-tmp-subdir
1328 (make-temp-name (expand-file-name "imaxima" imaxima-tmp-dir))) t)
1329 (set-file-modes imaxima-tmp-subdir 448) ; 700 in octal
1330 (let ((process-connection-type process-connection-type-flag))
1331 (if imaxima-use-maxima-mode-flag
1332 (progn
1333 (require 'maxima)
1334 (setq inferior-maxima-prompt
1335 (concat "\\(^ ?(" maxima-inchar
1336 "[0-9]*) \\)\\|\\(^MAXIMA>+\\)\\|\\(^(dbm:[0-9]*) \\)"))
1337 (add-hook 'inferior-maxima-mode-hook 'imaxima-setup t)
1338 (maxima)
1339 (remove-hook 'inferior-maxima-mode-hook 'imaxima-setup))
1340 (imaxima-delete-maxima-hooks)
1341 (setq imaxima-output "")
1342 (let ((mbuf
1343 (apply 'make-comint
1344 "imaxima"
1345 imaxima-maxima-program
1347 (split-string
1348 imaxima-maxima-options))))
1349 (with-current-buffer mbuf
1350 (setq imaxima-process (get-buffer-process mbuf))
1351 (imaxima-get-geometry mbuf)
1352 (imaxima-change-color mbuf)
1353 (imaxima-dump-tex)
1354 (set-process-sentinel imaxima-process 'imaxima-process-sentinel)
1355 (imaxima-setup-preoutput-filter)
1356 (unless (eq imaxima-image-type 'postscript)
1357 (imaxima-start-gs)))
1358 (imaxima-with-no-new-input-prompt
1359 (let ((maxima-set-up (list 'progn (list 'msetq '$imaxima_tmp_subdir imaxima-tmp-subdir) "")))
1360 (comint-send-string mbuf (format ":lisp %S\n" maxima-set-up))))
1361 (switch-to-buffer mbuf))))
1362 (run-hooks 'imaxima-startup-hook))
1364 (defcustom imaxima-print-tex-file "imax"
1365 "Name of the LaTeX file name to be created by `imaxima-print-buffer'.
1366 Do not include \".tex\" suffix. This file will be stored in the
1367 directory `imaxima-tmp-dir'."
1368 :group 'imaxima
1369 :type 'string)
1371 (defcustom imaxima-print-tex-command
1372 "latex %s; dvips -o imax.ps %s; gv imax.ps"
1373 ;;"latex %s; xdvi %s"
1374 ;;"latex %s; dvipdf %s.dvi imax.pdf; open imax.pdf" for Mac OS X users.
1375 "Command to run LaTeX on the file created by `imaxima-print-buffer'.
1376 In the string %s is replaced by the name of the tex file. e.g.
1377 \"latex %s; xdvi %s\"
1379 :group 'imaxima
1380 :type 'string)
1382 (defun imaxima-print-buffer ()
1383 "Run LaTeX on the current buffer and show output.
1384 See `imaxima-print-tex-command' for how latex is run on the latex output."
1385 (interactive "")
1386 (let (( tex-file (concat imaxima-tmp-dir
1387 imaxima-print-tex-file ".tex"))
1388 (buf)
1389 (cmd)
1392 (imaxima-latex)
1393 (write-file tex-file)
1394 (setq buf (current-buffer))
1396 ;; Convert all %s into the tex file name.
1397 (setq cmd imaxima-print-tex-command)
1398 (while (string-match "%s" cmd)
1399 (setq cmd (replace-match imaxima-print-tex-file t nil cmd)))
1401 (shell-command cmd)
1402 (kill-buffer buf) ;kill the temp tex buffer
1406 ;;; The following codes implements export imath text to HTML.
1408 (defvar html-template
1409 "<HTML>
1410 <HEAD>
1411 <META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; CHARSET=UTF-8\">
1412 <TITLE></TITLE>
1413 </HEAD>
1414 <BODY>
1417 </BODY>
1418 </HTML>
1421 (cl-defun prepare-for-translation ()
1422 "If error occurs inside this function, multiple values nil nil
1423 will be returned."
1424 (interactive "")
1425 (save-excursion
1426 (let (original-buffer text current-buffer-file-name filename image-folder html-buffer)
1427 (setq original-buffer (current-buffer))
1428 (setq text (buffer-substring (point-min) (point-max)))
1429 (if (or (string= (buffer-name original-buffer)
1430 "*maxima*")
1431 (string= (buffer-name original-buffer)
1432 "*imaxima*"))
1433 (progn
1434 (setq current-buffer-file-name "")
1435 (setq filename (concat imaxima-html-dir "session.html"))
1436 (setq image-folder "session-images"))
1437 (setq current-buffer-file-name
1438 (if (buffer-file-name)
1439 (buffer-file-name)
1440 (cl-return-from prepare-for-translation (cl-values nil nil))))
1441 (setq filename (concat (file-name-sans-extension
1442 (file-name-nondirectory current-buffer-file-name))
1443 ".html"))
1444 (setq image-folder (concat (file-name-sans-extension
1445 (file-name-nondirectory current-buffer-file-name))
1446 "-images")))
1447 ;; HTML buffer preparation
1448 (setq html-buffer (find-file filename))
1449 (set-buffer html-buffer)
1450 ;; create image folder
1451 (let (old-files)
1452 (if (file-exists-p image-folder)
1453 ;; since image-folder already exists, let's reuse it.
1454 (progn
1455 (condition-case err
1456 (progn
1457 (setq old-files (directory-files image-folder nil "\\.png$"))
1458 ;; we need to delete all the files already there.
1459 (dolist (f old-files)
1460 (delete-file (concat image-folder "/" f))))
1461 (file-error (cl-return-from prepare-for-translation (cl-values nil nil)))))
1462 ;; since image-folder doest not exist, let's create it.
1463 (condition-case err
1464 (make-directory image-folder)
1465 (file-error (cl-return-from prepare-for-translation (cl-values nil nil))))))
1466 ;; buffer preparation
1467 (if buffer-read-only
1468 (cl-return-from prepare-for-translation (cl-values nil nil)))
1469 (erase-buffer)
1470 (insert html-template)
1471 (goto-char (point-min))
1472 (search-forward "<BODY>")
1473 (forward-line 1)
1474 (insert text)
1475 (cl-values html-buffer image-folder))))
1477 (cl-defun imath-to-html()
1478 "Translate imath minor mode buffer contents into HTML format."
1479 (interactive "")
1480 (save-excursion
1481 (cl-multiple-value-bind (html-buffer image-folder)
1482 (prepare-for-translation)
1483 (if (not (and html-buffer image-folder))
1484 (progn
1485 (message "Error during HTML buffer preparation.")
1486 (cl-return-from imath-to-html)))
1487 (set-buffer html-buffer)
1488 (goto-char (point-min))
1489 (condition-case err
1490 (loop
1491 (cl-multiple-value-bind (ftype start-pos end-pos)
1492 (find-next-formula)
1493 (if (not (and ftype start-pos end-pos))
1494 (return t)
1495 (let (filename dest-name)
1496 ;; copy image file to image sub folder
1497 (if (null (setq filename (get-image-filename (1- (point)))))
1498 (progn
1499 (message "Error: all formulas must be converted to images first.")
1500 (cl-return-from imath-to-html)))
1501 (setq dest-name (concat
1502 image-folder "/"
1503 (file-name-sans-extension
1504 (file-name-nondirectory filename))
1505 ".png"))
1506 ;;))
1507 (copy-file filename dest-name)
1508 ;; replace imath formula with HTML IMG tag
1509 (delete-region start-pos end-pos)
1510 (insert (concat "<IMG SRC=\""
1511 dest-name
1512 "\" style=\"vertical-align:middle;\"> "))))))
1513 ;; "\" align=\"middle\"> "))))))
1514 (search-failed nil)
1515 (file-error
1516 (progn
1517 (message "Error: File manipulation failed during processing.")
1518 (cl-return-from imath-to-html))))
1519 (condition-case err
1520 (let (start-mark end-mark)
1521 (goto-char (point-min))
1522 (search-forward "<BODY>")
1523 (forward-line 1)
1524 (beginning-of-line)
1525 (setq start-mark (point-marker))
1526 (search-forward "</BODY>")
1527 (setq end-mark (point-marker))
1528 (goto-char start-mark)
1529 (loop
1530 (re-search-forward "$" end-mark)
1531 (replace-match "<br>\n")
1532 (forward-line 1)))
1533 (search-failed nil)))))
1535 (cl-defun imaxima-to-html ()
1536 "Translate the imaxima buffer contents into HTML format."
1537 (interactive "")
1538 (imath-to-html))
1540 (cl-defun find-next-formula ()
1541 "Find next formula and return multiple values of
1542 formula type, start position and end position.
1543 If search failed, error search-failed is signaled."
1544 (interactive "")
1545 (if (equal (point) (point-max))
1546 (cl-return-from find-next-formula (cl-values nil nil nil)))
1547 (let* ((region-start (copy-marker (point)))
1548 (region-end (copy-marker (next-single-property-change (point) 'display nil (point-max))))
1549 (text-prop (get-text-property region-start 'display)))
1550 (goto-char region-end)
1551 (if text-prop
1552 (cl-return-from find-next-formula (cl-values 'any region-start region-end))
1553 (find-next-formula))))
1557 (cl-defun get-image-filename (pos)
1558 "If the pos of the buffer is associated with text a display property,
1559 it is obtained. Then image filename of the display property is
1560 extracted and returned."
1561 (interactive "")
1562 (if (null pos)
1563 (setq pos (point)))
1564 (let (filename)
1565 (if (setq filename (memq :file (get-text-property pos 'display)))
1566 (second filename))))
1568 (provide 'imaxima)
1570 ;;; imaxima.el ends here