;; -*- mode: emacs-lisp; lexical-binding: t; -*-
;;;; imaxima.el --- Maxima mode with images

;; Created: 14 Nov 2001
;; Version: See version.texi
;; Keywords: maxima

;; Copyright (C) 2001, 2002, 2003, 2004 Jesper Harder
;; Copyright (C) 2006 Stephen Eglen (imaxima-print-buffer)
;; Copyright (C) 2007, 2008 Yasuaki Honda (imaxima-to-html, inline graph)
;; Copyright (C) 2020, 2021, 2022 Leo Butler (imaxima-gnuplot-replot, various improvements)

;; Time-stamp: <16-01-2024 11:38:28 Leo Butler>

;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2 of
;; the License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be
;; useful, but WITHOUT ANY WARRANTY; without even the implied
;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE.  See the GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public
;; License along with this program; if not, write to the Free
;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
;;

;;; Commentary:
;;
;; This file (and imaxima.lisp) provides image support for interacting
;; with the computer algebra system Maxima
;; <http://maxima.sourceforge.net/>
;;
;; The command `imaxima' (M-x imaxima) provides a simple comint
;; derived CLI mode.
;;
;; To use imaxima with the Maxima mode from the Maxima distribution
;; set `imaxima-use-maxima-mode-flag' to `t'.
;;
;; To turn off images, evaluate "display2d:true" in Maxima.  To turn
;; them on again, evaluate "display2d:imaxima".
;;
;; The command `imaxima-latex' prepares a LaTeX version of the Maxima
;; buffer.
;;
;; The package requires Emacs 21 with image support.
;;
;; A fairly recent version of Ghostscript is recommended (at least
;; newer than v. 8.56).  If your version is too old, you can either set
;; `imaxima-image-type' to 'ps or remove the options
;; "-dTextAlphaBits=4" and "-dGraphicsAlphaBits=4" from
;; `imaxima-gs-options'.  The images won't look nearly as attractive,
;; though -- the text looks ragged because it isn't anti aliased.
;;
;; The file "imaxima.lisp" is a slightly modified version of
;; "texmacs.lisp" in the TeXmacs distribution.  Several of the image
;; routines are borrowed from David Kastrup's preview-latex.el.
;;
;; Version 1.0 beta and later supports inline graph. You can use
;; the following six maxima commands.
;; wxplot2d(), wxplot3d(), wxdraw2d(), wxdraw3d(), wximplicit_plot(),
;; wxcontour_plot().
;; GNUPLOT 4.2 or later is needed for this to work. The resulted image
;; generated by gnuplot will be inserted as the output of the command.
;;
;;
;; Installation:
;; Take a look at the README file which comes with this file.
;;

;;; Code:

;; modified to remove eval-when-compile form. Surrounding
;; the eval-when-compile prevernts imaxima from running
;; properly in the xemacs on cygwin environment.
(require 'imaxima-autoconf-variables)
(require 'advice)

(require 'comint)
(require 'cl-lib)

(require 'mylatex.ltx)

;; XEmacs stuff

(defalias 'imaxima-image-type-available-p
  (if (fboundp 'image-type-available-p)
      'image-type-available-p
    'featurep))

(defalias 'imaxima-display-pixel-width
  (if (fboundp 'display-pixel-width)
      'display-pixel-width
    'device-pixel-width))

(defalias 'imaxima-display-pixel-height
  (if (fboundp 'display-pixel-height)
      'display-pixel-height
    'device-pixel-height))

(defalias 'imaxima-display-mm-width
  (if (fboundp 'display-mm-width)
      'display-mm-width
    'device-mm-width))

(defalias 'imaxima-display-mm-height
  (if (fboundp 'display-mm-height)
      'display-mm-height
    'device-mm-height))

(defalias 'imaxima-get-window-width
  (if (featurep 'xemacs)
      'imaxima-get-window-width-xemacs
    'imaxima-get-window-width-emacs))

(defalias 'imaxima-color-values
  (if (fboundp 'color-values)
      'color-values
    '(lambda (color) (color-rgb-components
		      (if (stringp color)
			  (make-color-specifier color)
			color)))))

(defun imaxima-get-bg-color ()
  (if (featurep 'xemacs)
      (face-property 'default 'background)
    (frame-parameter nil 'background-color)))

(defun imaxima-get-fg-color ()
  (if (featurep 'xemacs)
      (face-property 'default 'foreground)
    (frame-parameter nil 'foreground-color)))

;; XEmacs doesn't have subst-char-in-string (sigh!).

(defun imaxima-subst-char-in-string (fromchar tochar string &optional inplace)
  "Replace FROMCHAR with TOCHAR in STRING each time it occurs.
Unless optional argument INPLACE is non-nil, return a new string."
  (let ((i (length string))
	(newstr (if inplace string (copy-sequence string))))
    (while (> i 0)
      (setq i (1- i))
      (if (eq (aref newstr i) fromchar)
	  (aset newstr i tochar)))
    newstr))


(defconst imaxima-mouse2 (if (featurep 'xemacs)
			   [button2]
			 [mouse-2]))

(defconst imaxima-mouse3 (if (featurep 'xemacs)
			   [button3]
			 [mouse-3]))

;; Options

(defgroup imaxima nil
  "Image support for Maxima."
  :version "21.1"
  :link '(url-link "https://maxima.sourceforge.net/")
  :link '(custom-manual "(imaxima)")
  :prefix "imaxima-"
  :group 'maxima)

(defvar process-connection-type-flag
  (if (eql system-type 'darwin) t nil))

(defvar imaxima-image-types '(png postscript jpeg tiff))

(defcustom imaxima-image-type 'png
  "Image type to used in Maxima buffer."
  :group 'imaxima
  :type (cons 'choice
	      (mapcar (lambda (type) (list 'const type))
		      (cl-remove-if-not 'imaxima-image-type-available-p
				     imaxima-image-types))))

(defcustom imaxima-pt-size 11
  "*Point size used in LaTeX."
  :group 'imaxima
  :type '(choice (const 9)
		 (const 10)
		 (const 11)
		 (const 12)))

(defcustom imaxima-fnt-size "normalsize"
  "*Default size of font."
  :group 'imaxima
  :type '(choice (const "small")
		 (const "normalsize")
		 (const "large")
		 (const "Large")
		 (const "LARGE")
		 (const "huge")
		 (const "Huge")))

(defcustom imaxima-scale-factor 1.0
  "*All images are scaled by this factor."
  :group 'imaxima
  :type 'number)

(defcustom imaxima-label-color "red"
  "*Color used in output labels."
  :group 'imaxima
  :type '(color))

(defcustom imaxima-equation-color (imaxima-get-fg-color)
  "*Color used for equations."
  :group 'imaxima
  :type '(color))

(defcustom imaxima-bg-color nil
    "Background color of imaxima buffer."
    :group 'imaxima
    :type '(choice (color)
		   (const :tag "None" nil)))

(defcustom imaxima-fg-color nil
    "Foreground color of imaxima buffer."
    :group 'imaxima
    :type '(choice (color)
		   (const :tag "None" nil)))

(defcustom imaxima-latex-preamble ""
  "*String inserted at the start of the document preamble.
This can be used to change, say, the document font.
E.g. `\\usepackage{concrete}' will use the Euler math fonts."
  :group 'imaxima
  :type '(string))

(defcustom imaxima-max-scale 0.85
  "Maximum amount of scaling allowed to fit wide equations in the buffer.
nil means no scaling at all, t allows any scaling."
  :group 'imaxima
  :type 'number)

(defcustom imaxima-linearize-flag t
  "Non-nil means that equations too wide to fit in the buffer are linearized."
  :type '(boolean)
  :group 'imaxima)

(defcustom imaxima-use-maxima-mode-flag nil
  "Non-nil means that the major mode from `maxima.el' is used."
  :type '(boolean)
  :group 'imaxima)

(defcustom imaxima-maxima-program "maxima"
  "Maxima executable."
  :group 'imaxima
  :type '(string))


(defcustom imaxima-initex-option "-ini"
  "Option passed to TeX to start initex."
  :group 'imaxima
  :type '(string))

(defcustom imaxima-tex-program "latex"
  "TeX executable."
  :group 'imaxima
  :type '(string))

(defcustom imaxima-gs-program "gs"
  "Ghostscript executable."
  :group 'imaxima
  :type '(string))

(defcustom imaxima-gs-options '("-q" "-dNOPAUSE"
				"-dSAFER"
				"-dDELAYSAFER"
				"-DNOPLATFONTS" "-dTextAlphaBits=4"
				"-dGraphicsAlphaBits=4")
  "Options passed to gs for conversion from EPS."
  :group 'imaxima
  :type '(repeat string))

(defcustom imaxima-dvips-program "dvips"
  "Dvips executable."
  :group 'imaxima
  :type '(string))

(defcustom imaxima-cp-program "cp"
  "cp executable."
  :group 'imaxima
  :type '(string))

(defcustom imaxima-dvips-options '("-E" "-R")
  "Options passed to dvips for conversion from DVI to EPS."
  :group 'imaxima
  :type '(repeat string))

(defcustom imaxima-tmp-dir
  (cond ((featurep 'xemacs)
	 (temp-directory))
	((eql system-type 'cygwin)
	 "/tmp/")
	(t temporary-file-directory))
  "*Directory used for temporary TeX and image files."
  :type '(directory)
  :group 'imaxima)

(defcustom imaxima-startup-hook nil
  "A hook called at startup.
This hook is called after imaxima has started Maxima."
  :group 'imaxima
  :type 'hook)

(defcustom imaxima-exit-hook nil
  "Hook called when exiting imaxima."
  :group 'imaxima
  :type 'hook)

(defvar imaxima-tmp-subdir ""
  "Subdirectory for temporary files.")

(defcustom imaxima-lisp-file 
  (if (eq system-type 'windows-nt)
      (imaxima-subst-char-in-string ?\\ ?/ (locate-library "imaxima.lisp"))
    (locate-library "imaxima.lisp"))
  "Location of `imaxima.lisp'."
  :group 'imaxima
  :type '(file))

(defcustom imaxima-maxima-options
  (format "--preload-lisp=%s" imaxima-lisp-file)
  "Arguments passed to Maxima."
  :group 'imaxima
  :type '(string))

(defcustom imaxima-latex-buffer-name
  "*imaxima-latex*"
  "Default name of buffer created by `imaxima-latex'."
  :group 'imaxima
  :type '(string))

(defcustom imaxima-latex-document-class
  '("\\documentclass[%dpt,leqno]{article}" imaxima-pt-size)
  "Default documentclass used by `imaxima-latex'. It should be a
valid argument to `format'."
  :group 'imaxima
  :type '(sexp))

(defcustom imaxima-latex-use-packages
  "
\\usepackage{verbatim}
\\usepackage{color}
\\usepackage{exscale}
\\usepackage{amsmath}
\\usepackage[cmbase]{flexisym}
\\usepackage{breqn}
\\setkeys{breqn}{compact}
"
  "Default latex packages and configuration used by `imaxima-latex'."
  :group 'imaxima
  :type '(string))

(defcustom imaxima-latex-document-dimensions
  "
\\setlength{\\textwidth}{180mm}
\\setlength{\\oddsidemargin}{15mm}
\\addtolength{\\oddsidemargin}{-1in}
\\setlength{\\evensidemargin}{15mm}
\\addtolength{\\evensidemargin}{-1in}
"
   "Default dimensions of document created by `imaxima-latex'."
  :group 'imaxima
  :type '(string))

(defcustom imaxima-latex-macros
  "
\\newcommand{\\ifrac}[2]{\\frac{#1}{#2}}
\\newcommand{\\ifracd}[2]{\\frac{#1}{#2}}
\\newcommand{\\ifracn}[2]{\\frac{#1}{#2}}
\\newcommand{\\isubscript}[2]{{#1}_{#2}}
\\newcommand{\\iexpt}[2]{{#1}^{#2}}
\\newcommand{\\isqrt}[1]{\\sqrt{#1}}
"
  "Default macros used by `imaxima-latex'."
  :group 'imaxima
  :type '(string))

(defcustom imaxima-latex-macros-linear
  (concat
   ;; braces in both denominator and numerator
   "\\renewcommand{\\ifrac}[2]{\\left(#1\\right)/\\left(#2\\right)}"
   ;; only braces denominator
   "\\renewcommand{\\ifracd}[2]{#1/\\left(#2\\right)}"
   ;; only braces in numerator
   "\\renewcommand{\\ifracn}[2]{\\left(#1\\right)/#2}"
   "\\renewcommand{\\isubscript}[2]{\\mathrm{subscript}\\left(#1,#2\\right)}"
   "\\renewcommand{\\iexpt}[2]{\\mathrm{expt}\\left(#1,#2\\right)}"
   "\\renewcommand{\\isqrt}[1]{\\left(#1\\right)^{1/2}}\n")
  "Default linear macros used by `imaxima-latex'."
  :group 'imaxima
  :type '(string))

(defcustom imaxima-latex-macros-format-file
  (concat
   "
\\setlength{\\textheight}{200cm}
%% define \\boxed from amsmath.sty
\\makeatletter
\\providecommand\\boxed{}
\\providecommand\\operatorname{}
\\renewcommand{\\boxed}[1]{\\fbox{\\m@th$\\displaystyle#1$}}
\\renewcommand{\\operatorname}[1]{%
\\mathop{\\relax\\kern\\z@\\operator@font{#1}}}
\\makeatother
")
  "Default macros used by `imaxima-dump-tex'."
  :group 'imaxima
  :type '(string))

(defcustom imaxima-create-image-options
  '(:ascent center :mask (heuristic (color-values imaxima-bg-color)))
  "Optional arguments passed to `imaxima-create-image'"
  :group 'imaxima
  :type '(alist))

(defcustom imaxima-latex-includegraphics
  "\\includegraphics{%s}\n"
  "Includegraphics command."
  :group 'imaxima
  :type '(string))

(defface imaxima-latex-error-face
  '((t (:foreground "Blue" :underline t)))
  "Face used for LaTeX errors."
  :group 'imaxima)

(defvar imaxima-image-creators
  '((postscript nil)
    (png ("-sDEVICE=png16m"))
    (jpeg ("-sDEVICE=jpeg"))
    (tiff ("-sDEVICE=tiffpack")))
  "Define functions for generating images.
Argument list is passed to gs.")

(defvar imaxima-resolution nil
  "Screen resolution where rendering started.
Cons-cell of x and y resolution, given in
dots per inch.  Buffer-local to rendering buffer.")
(make-variable-buffer-local 'imaxima-resolution)

(defvar imaxima-output ""
  "Accumulator for `imaxima-filter'.")

(defvar imaxima-gs-output ""
  "Accumulator for `imaxima-gs-filter'.")

(defvar imaxima-process nil)
(defvar imaxima-gs-process nil)
(defvar imaxima-gs-computing-p nil)
(defvar imaxima-gs-7.05-is-broken nil)

(defvar imaxima-error-map (make-sparse-keymap)
  "Keymap for mouse clicks on LaTeX errors.")

(defvar imaxima-old-bg-color nil
  "Old background color.")

(defvar imaxima-old-fg-color nil
  "Old foreground color.")

(defvar imaxima-file-counter 0
  "Counter used for naming temp files.")

(defvar imaxima-html-dir "~/")

(defvar imaxima-image-map
  (let ((map (make-sparse-keymap)))
    (set-keymap-parent map image-map)
    (define-key map "l" #'imaxima-get-latex-src)
    (define-key map "g" #'imaxima-gnuplot-replot)
    (define-key map "s" #'imaxima-gnuplot-restart)
    map)
  "Keymap for images in the `imaxima' buffer. The `image-map' is
  the parent map.")

(defcustom imaxima-latex-src-register ?l
  "The register used by `imaxima-get-latex-src' to save the latex
  source code for the image under point."
  :group 'imaxima
  :type '(character))

(defcustom imaxima-gnuplot-replot-term nil
  "The default gnuplot terminal used by `imaxima-gnuplot-replot'
to replot a figure in an external window."
  :group 'imaxima
  :type '(choice (string :tag "Terminal") (symbol :tag "nil" nil)))

;;
;; Version
;;

(defun imaxima-version ()
  "Print the package name and the version in the mini buffer"
  (interactive)
  (message "%s %s" *imaxima-autoconf-package* *imaxima-autoconf-version*))

;;
;; Reset
;;

(defvar imaxima-filter-running nil)
(defvar imaxima-continuation nil
  "The variable is used between `maxima-to-image' and `get-image-from-imaxima' in `imath-mode'.
It is used in `imaxima-filter' and `imaxima-filter1' in `imaxima'.

The value is either nil or a list of (function buffer pos1 pos2),
where pos1 and pos2 are the beginning and end of current maxima
formula.")


(defun reinit-imaxima ()
  "Re-initialize imaxima"
  (interactive)
  (setq imaxima-filter-running nil
    imaxima-output nil
    imaxima-continuation nil))

;;
;; Geometry
;;

(defun imaxima-get-geometry (buffer)
  "Transfer display geometry parameters from current display.
Those are put in local variable `imaxima-resolution'.  Calculation is done
in source buffer specified by BUFF."
  (let (res)
    (with-current-buffer buffer
      (setq res (cons (/ (* 25.4 (imaxima-display-pixel-width))
			 (imaxima-display-mm-width))
		      (/ (* 25.4 (imaxima-display-pixel-height))
			 (imaxima-display-mm-height)))))
    (setq imaxima-resolution res)))

(defun imaxima-get-window-width-xemacs ()
  "Return window width in mm.
XEmacs version."
  (/ (* (window-text-area-pixel-width) (imaxima-display-mm-width))
     (imaxima-display-pixel-width)))

(defun imaxima-get-window-width-emacs ()
  "Return window width in mm.
Emacs version."
  (/ (* (- (window-width) 1) (frame-char-width))
     (/ (float (imaxima-display-pixel-width))
	(imaxima-display-mm-width))))

(defun imaxima-bp-to-mm (bp)
  "Convert PostScript big points to mm.  BP is size in big points."
  (* bp 0.352778))

(defun imaxima-color-to-rgb (str)
  "Convert color name STR to rgb values understood by TeX."
  (mapcar #'(lambda (x) (/ x 65535.0)) (imaxima-color-values str)))

(defmacro imaxima-with-temp-dir (dir &rest body)
  "Change to DIR temporarily and execute BODY."
  (let ((wd (make-symbol "wd")))
    `(let ((,wd  default-directory))
       (cd ,dir)
       (unwind-protect
	   (progn
	     ,@body)
	 (cd ,wd)))))

(defvar imaxima-silence-filter nil)
(defmacro imaxima-with-no-new-input-prompt (&rest body)
  "Set `imaxima-silence-filter' to t to silence any output
through `imaxima-filter'."
  `(progn (setq imaxima-silence-filter t) ,@body))

;;
;; Gs stuff
;;

(defun imaxima-gs-filter (process str)
  "Set `imaxima-gs-computing-p' to t when gs is done."
  (setq imaxima-gs-output (concat imaxima-gs-output str))
  (when (string-match "GS\\(<[0-9+]\\)?>" imaxima-gs-output)
    (setq imaxima-gs-computing-p nil)
    (setq imaxima-gs-output "")))

(defun imaxima-gs-wait ()
  "Wait for gs to finish."
  (while (and imaxima-gs-computing-p
	      (eq (process-status imaxima-gs-process) 'run))
    (accept-process-output imaxima-gs-process 1)))

(defun imaxima-start-gs ()
  "Start Ghostscript as an asynchronyous process."
  ;; Are we using the broken GNU Ghostscript 7.05?
  (setq imaxima-gs-7.05-is-broken
	(string-match "\\(GNU\\|ESP\\) Ghostscript 7.05"
		      (shell-command-to-string
		       (concat imaxima-gs-program " --help"))))
  (let* (output
	 (type (cadr (assq imaxima-image-type imaxima-image-creators)))
	 (gs-args (append imaxima-gs-options
			  type
			  (list (format "-r%gx%g" (car imaxima-resolution)
					(cdr imaxima-resolution))))))
    (when (and imaxima-gs-process
	       (processp imaxima-gs-process))
      (delete-process imaxima-gs-process))
    (setq imaxima-gs-computing-p t)
    (condition-case nil
	(setq imaxima-gs-process (apply 'start-process "imaxima-gs"
					" *imaxima gs output*"
					imaxima-gs-program gs-args))
      (error (error
	      "Sorry, Ghostscript could not be started.  Please check
that you have gs in your path or customize the value of
`imaxima-gs-program' (current values is \"%s\").
%s"
	      imaxima-gs-program
	      (if (imaxima-image-type-available-p 'postscript)
		  "If Ghostscript isn't installed you can set `imaxima-image-type' to `ps'."
		;; don't offer this advice in XEmacs, which doesn't support ps.
		""))))
    (set-process-filter imaxima-gs-process 'imaxima-gs-filter)
    (imaxima-gs-wait)
    (set-process-query-on-exit-flag imaxima-gs-process nil)
    (unless (eq (process-status imaxima-gs-process) 'run)
      (setq output (shell-command-to-string (concat imaxima-gs-program " -h")))
      (cond
       ((null (string-match (car type) output))
	(error
	 "Your version Ghostscript does not appear to support the image type %s.
The command \"gs -h\" lists the available devices.
You can change the image type in `imaxima-image-type' or the device name
associated with an image type in `imaxma-image-creators'" (car type)))
       (t (error
	   "Some of the options passed to Ghostscript are probably not supported
by your version.  In particular \"-dTextAlphaBits=4\" and \"-dGraphicsAlphaBits=4\"
are not supported by gs 5.5 or earlier.  Please edit `imaxima-gs-options'"))))))

(defun imaxima-extract-bb (filename)
  "Extract EPS bounding box vector from FILENAME.
Returns a list of bounding box, width, and height."
  (with-temp-buffer
    (insert-file-contents-literally filename nil 0 1024 t)
    (goto-char (point-min))
    (when (search-forward-regexp "%%BoundingBox:\
 +\\([-+]?[0-9.]+\\)\
 +\\([-+]?[0-9.]+\\)\
 +\\([-+]?[0-9.]+\\)\
 +\\([-+]?[0-9.]+\\)" nil t)
      (let ((bb
	    (vector
	     (floor (string-to-number (match-string 1)))
	     (floor (string-to-number (match-string 2)))
	     (ceiling (string-to-number (match-string 3)))
	     (ceiling (string-to-number (match-string 4))))))
      (list bb
	    (- (aref bb 2) (aref bb 0))
	    (- (aref bb 3) (aref bb 1)))))))

(defun imaxima-eps-scale (file bb scale)
  "Scale the eps image in FILE with factor SCALE.
BB is the bounding box of the image.  Returns a list of new bounding
box, width, and height."
  (cl-multiple-value-bind (llx lly urx ury) (append bb nil)
    (let ((x (round (* (- urx llx) scale)))
          (y (round (* (- ury lly) scale)))
	  (buff (find-file-noselect file)))
      (unwind-protect
	  (with-current-buffer buff
	    (goto-char (point-min))
	    (search-forward "%%BoundingBox")
	    (delete-region (line-beginning-position) (line-end-position))
	    (insert (format "%%%%BoundingBox: 0 0 %d %d\n" x y))
	    (search-forward "%%EndComments")
	    (forward-line)
	    (insert "%%BeginProcSet: imaxima 1 0\ngsave\n")
	    (insert (format "%f %f translate\n"
			    (- (* llx scale))
			    (- (* lly scale))))
	    (insert (format "%f %f scale\n" scale scale))
	    (insert "%%EndProcSet\n")
	    (goto-char (point-max))
	    (insert "\ngrestore\n")
	    (save-buffer))
	(kill-buffer buff))
      (list (vector 0 0 x y) x y))))

(defun imaxima-latex-document-class ()
  (apply #'format (mapcar #'eval imaxima-latex-document-class)))

(defun imaxima-latex ()
  "Convert Maxima buffer to LaTeX.
This command does not work in XEmacs."
  (interactive)
  (let (pos2 label (pos (make-marker))
	     (buf (generate-new-buffer imaxima-latex-buffer-name))
	     (oldbuf (current-buffer)))
    (set-buffer buf)
    (insert (imaxima-latex-document-class) "\n"
	    imaxima-latex-use-packages "\n"
	    imaxima-latex-document-dimensions "\n"
	    imaxima-latex-macros "\n"
	    "\\begin{document}\n"
	    )
    (save-excursion
      (insert-buffer-substring oldbuf))
    (while (not (eobp))
      (let* ((region-start (copy-marker (point)))
	     (region-end (copy-marker (next-single-property-change (point) 'display nil (point-max))))
	     (text-prop (get-text-property region-start 'display))
	     (latex-prop (get-text-property region-start 'latex)))
	(if latex-prop
	    (progn
	      (delete-region region-start region-end)
	      (goto-char region-start)
	      (insert (concat latex-prop "\n\n")))
	  (progn
	    (goto-char region-start)
	    (insert "\n\\begin{verbatim}\n")
	    (goto-char region-end)
	    (insert "\n\\end{verbatim}\n\n")))))
    (insert "\n\\end{document}")
    (switch-to-buffer-other-window buf)
    (latex-mode)))

(defun imaxima-process-sentinel (process event)
  "Process sentinel for Maxima process."
  (message "Process %s %s" process event)
  (unless (eq (process-status process) 'run)
    (imaxima-clean-up)))

(defun imaxima-restart-gs ()
  "Kill Ghostscript process and start a new one."
  (kill-process imaxima-gs-process)
  (imaxima-start-gs))

(defun imaxima-maybe-restart-gs ()
  "Unless `imaxima-gs-process' is running, call
`imaxima-start-gs' to (re)start GS."
    (unless (and (processp imaxima-gs-process)
		 (eq (process-status imaxima-gs-process) 'run))
      (imaxima-start-gs)))

(defun imaxima-ps-to-image (psfilename filename bb width height)
  "Convert eps file PSFILENAME to a bitmap image file FILENAME.
BB is the bounding box for eps image.  WIDTH and HEIGHT are the
dimensions of the image."
  (imaxima-maybe-restart-gs)
  (setq imaxima-gs-computing-p t)
  (when (eq system-type 'windows-nt)
    (setq psfilename (imaxima-subst-char-in-string ?\\ ?/ psfilename))
    (setq filename (imaxima-subst-char-in-string ?\\ ?/ filename)))
  (process-send-string imaxima-gs-process
		       (format
			(if imaxima-gs-7.05-is-broken
			    "clear /imaxima-state save def \
<< /PageSize [%d %d] /PageOffset [%d %d] /OutputFile (%s) >> \
setpagedevice (%s) run imaxima-state restore\n"
			  "clear \
<< \
/PageSize [%d %d] /PageOffset [%d %d] /OutputFile (%s) \
>> setpagedevice [save] (%s) (r) file cvx \
systemdict /.runandhide known revision 700 ge and {.setsafe {.runandhide}} if \
stopped {handleerror quit} if count 1 ne {quit} if \
cleardictstack 0 get restore\n")
			width
			height
			(- (aref bb 0))
			(aref bb 1)
			filename
			psfilename))
  (imaxima-gs-wait))

(defvar imaxima-latex-src nil)

(defun imaxima-check-plot-output (str)
  "If the str is in the form ^Wpompt^W\\verb|plotfile filename|, then
   filename is returned. Else, nil is returned."
;;  (if (string-match "[^]*\\\\mathrm{wxxmltag}\\\\left\(\\\\verb|\\([^|]*\\)|.*" str)
;;  (if (string-match "[^]*\\\\mathrm{wxxmltag}\\\\left[^/]*\\(.*\.eps\\).*" str)
;;  (if (string-match "[^]*\\\\mathrm{wxxmltag}\\\\left\(\\\\verb|*\\(.*\.eps\\).*" str)
  (when (string-match "[^]*\\\\mathrm{wxxmltag}\\\\left\(\\(\\\\verb|\\)*\\(.*\.eps\\).*" str)
    (let ((plot-file (match-string 2 str)))
      (setq imaxima-latex-src (format imaxima-latex-includegraphics plot-file))
      plot-file)))

(defun imaxima-create-image (filename type &rest options)
  (apply #'create-image (append (list filename type) options imaxima-create-image-options)))

(defun imaxima-make-image (str eps-or-latex &optional no-label-p)
  "Make image from STR. If no-label-p is specified t,
   label becomes nil and passed to imaxima-tex-to-dvi."
  (let ((res (imaxima-check-plot-output str)))
    (if res
	(setq str res
	      eps-or-latex 'eps)))
      
  (let* ((filename (expand-file-name
		    (number-to-string (cl-incf imaxima-file-counter))
		    imaxima-tmp-subdir))
	 (psfilename (concat filename ".ps"))
	 (label "*"))

    (cond ((eql eps-or-latex 'latex)
	   (when (string-match "\\(\\([^]*\\)\\)" str)
	      (setq label (match-string 2 str))
	      (setq str (replace-match "" t t str 1)))
	   (if no-label-p (setq label nil)) ;; override label to nil if no-label-p
	   (imaxima-tex-to-dvi str label (concat filename ".tex"))
	   (imaxima-dvi-to-ps filename)
	   )
	  ((eql eps-or-latex 'eps)
	   (copy-file str psfilename)
	   (setq label nil)
	   )
	  )
    (if (not (file-exists-p psfilename))
	(imaxima-latex-error str filename)
      (cl-multiple-value-bind (bb  width height)
	    (imaxima-extract-bb psfilename)
	  (let ((ratio (/  (imaxima-get-window-width)
			   (imaxima-bp-to-mm width))))
	    (when (< ratio 1.0)
	      ;; image is wider than the buffer
	      (if (or (eql eps-or-latex 'eps)
		      (and imaxima-max-scale
			   (or (eq imaxima-max-scale t)
			       (> ratio imaxima-max-scale))))
		  ;; scale image
		  (cl-multiple-value-setq (bb width height)
		    (imaxima-eps-scale psfilename bb ratio))
		(when imaxima-linearize-flag
		  ;; linearize image
		  (imaxima-tex-to-dvi str label (concat filename ".tex") t)
		  (imaxima-dvi-to-ps filename)
		  (cl-multiple-value-setq (bb width height)
		    (imaxima-extract-bb psfilename))))))
	  (unless (eq imaxima-image-type 'postscript)
	    (imaxima-ps-to-image psfilename filename bb width height)
	    ;; FIXME:
	    ;; Ghostscript on Windows doesn't flush the image to the file.
	    ;; So we have to kill the process and restart.  What a kludge!
	    (when (eq system-type 'windows-nt)
	      (imaxima-restart-gs)))
	  (cond ((featurep 'xemacs)
		 ;; FIXME:
		 (when (eq system-type 'windows-nt)
		   (imaxima-restart-gs))
		 (xemacs-set-imagefile-properties filename imaxima-image-type str))
		(t
		 (prog1
		     (propertize (concat "(" label ") " str) 'display
				 (apply #'imaxima-create-image
					(if (eq imaxima-image-type 'postscript)
					    (list psfilename imaxima-image-type nil (list :pt-width width :pt-height height :bounding-box bb))
					  (list filename imaxima-image-type nil)))
				 'keymap imaxima-image-map
				 '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%"
				 'latex imaxima-latex-src)
		   (setq imaxima-latex-src nil))))))))


(defun imaxima-latex-error (str filename)
  "Make clickable error message.
STR is offending LaTeX expression.  FILENAME is name of the LaTeX file."
  (let* ((msg "LaTeX error in: ")
	(delim (if (featurep 'xemacs)
		   "; " "\n"))
	imaxima-error-2
	imaxima-error-3
	(error-text (concat "mouse-2: view LaTeX error log" delim
					     "mouse-3: view LaTeX source")))
    (fset 'imaxima-error-2
	  `(lambda ()
	     (interactive)
	     (view-file-other-window (concat ,filename ".log"))))
    (fset 'imaxima-error-3
	  `(lambda ()
	     (interactive)
	     (view-file-other-window (concat ,filename ".tex"))))
    (define-key imaxima-error-map imaxima-mouse2 'imaxima-error-2)
    (define-key imaxima-error-map [(return)] 'imaxima-error-2)
    (define-key imaxima-error-map imaxima-mouse3 'imaxima-error-3)
    (define-key imaxima-error-map [(meta return)] 'imaxima-error-3)
    (set-text-properties 0 14 `(face imaxima-latex-error-face
				     mouse-face highlight
				     help-echo ,error-text
				     keymap ,imaxima-error-map)
			 msg)
    (concat msg str)))

(defun imaxima-dump-tex ()
  "Dump a TeX format file preloaded with the required packages."
 (if imaxima-mylatex
   (with-temp-file (expand-file-name "mylatex.ltx" imaxima-tmp-subdir)
     (insert imaxima-mylatex)))
  (with-temp-file (expand-file-name "format.tex" imaxima-tmp-subdir)
    (insert
     (imaxima-latex-document-class)
     imaxima-latex-preamble "\n"
     imaxima-latex-use-packages "\n"
     imaxima-latex-macros-format-file "\n"
     imaxima-latex-macros "\n"
     "\\begin{document}\n"
     "\\end{document}"))
  (imaxima-with-temp-dir
   imaxima-tmp-subdir
   (apply #'call-process imaxima-tex-program nil nil nil
	  (list imaxima-initex-option "&latex" "mylatex.ltx" "format"))))

(defun imaxima-latex-set-textwidth ()
  (format "\\setlength{\\textwidth}{%dmm}\n"
	  (round (/ (imaxima-get-window-width)
		    imaxima-scale-factor))))
(defun imaxima-latex-set-pagecolor ()
  (apply #'format "\\pagecolor[rgb]{%f,%f,%f}\n"
	 (imaxima-color-to-rgb (imaxima-get-bg-color))))
(defun imaxima-latex-set-labelcolor ()
  (apply #'format "\\color[rgb]{%f,%f,%f}\n"
	 (imaxima-color-to-rgb imaxima-label-color)))
(defun imaxima-latex-format-label (str label)
  (concat
   (format "\\begin{dmath}[number={%s}]\n" label)
   (apply #'format "\\color[rgb]{%f,%f,%f}\n"
 	  (imaxima-color-to-rgb imaxima-equation-color))
   str (format "\n\\end{dmath}\n")))
(defun imaxima-latex-format-output (str)
  (concat
   (apply #'format "\\color[rgb]{%f,%f,%f}\n"
 	  (imaxima-color-to-rgb imaxima-equation-color))
   "\\begin{math} \\displaystyle \n" str (format "\n\\end{math}\n")))
(defun imaxima-get-latex-src ()
  (interactive)
  (set-register imaxima-latex-src-register (get-text-property (point) 'latex)))

;;;;;;;;;;;;;;;;;;;; GNUPLOT Support ;;;;;;;;;;;;;;;;;;;;
(defun imaxima-get-gnuplot-file ()
  (interactive)
  (let ((src (get-text-property (point) 'latex)))
    (if (string-match (format "\\(%s/maxout_[0-9]+\\)" imaxima-tmp-subdir) src)
      (concat (match-string 1 src) ".gnuplot"))))

(defcustom imaxima-gnuplot-buffer "*imaxima-gnuplot*"
  "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.")
(defcustom imaxima-gnuplot-command "gnuplot"
  "Name, or complete pathname, of the `gnuplot' binary. Used by `imaxima-start-gnuplot'.")
(defcustom imaxima-gnuplot-args ""
  "Optional arguments passed to the `gnuplot' binary when started by `imaxima-start-gnuplot'.")
(defvar imaxima-gnuplot-process nil
  "A holder for the `gnuplot' process started in `imaxima-start-gnuplot'.")

(defun imaxima-gnuplot-sentinel (process desc)
  (let ((state (process-status process)))
    (message "imaxima-gnuplot-sentinel: state: %s\ndescription: %s" state desc)))
(defun imaxima-start-gnuplot ()
  (save-mark-and-excursion
    (let* ((gbuf (get-buffer-create imaxima-gnuplot-buffer))
	   (proc (and gbuf (get-buffer-process gbuf)))
	   (state (and proc (process-status proc))))
      (cond ((and gbuf (eq state 'run))
	     (message "Gnuplot running."))
	    (gbuf
	     (message "Re-starting gnuplot...")
	     (setq imaxima-gnuplot-process (start-process (buffer-name gbuf) gbuf imaxima-gnuplot-command "-" imaxima-gnuplot-args)))	     
	    (t
	    ;; gbuf does not exist
	     (error "Failed to create buffer %s to run %s. Stop." imaxima-gnuplot-buffer imaxima-gnuplot-command)))
      (if (and gbuf imaxima-gnuplot-process)
	  (set-process-sentinel imaxima-gnuplot-process #'imaxima-gnuplot-sentinel)
	(error "imaxima-start-gnuplot failed.")))))

(defun imaxima-gnuplot-restart ()
  "Forcibly restart `gnuplot' process."
  (interactive)
  (let ((kill-buffer-query-functions '())
	(kill-buffer-hook '()))
    (if (get-buffer imaxima-gnuplot-buffer) (kill-buffer imaxima-gnuplot-buffer)))
  (imaxima-start-gnuplot))

(defun imaxima-gnuplot-replot ()
  "Replot the Gnuplot graph under point in an external
window. Suppress a new input prompt."
  (interactive)
  (imaxima-start-gnuplot)
  (let ((term (or imaxima-gnuplot-replot-term (read-from-minibuffer "Terminal? "))))
    (when (and (null imaxima-gnuplot-replot-term) (y-or-n-p "Save this as the session default? "))
      (setq imaxima-gnuplot-replot-term term))
    (let ((gplt-src (imaxima-get-gnuplot-file)))
      (when gplt-src
	(message "file=%s" gplt-src)
	(save-mark-and-excursion
	  (with-temp-file gplt-src
	    (insert-file-contents gplt-src)
	    (goto-char (point-min))
	    (while (re-search-forward "^set term.+" nil t)
	      (replace-match "# \\&")
	      (insert (concat "\nset terminal " term)))
	    (goto-char (point-min))
	    (while (re-search-forward "^set out.+" nil t)
	      (replace-match "# \\&")))
	  (with-current-buffer imaxima-gnuplot-buffer
	    (comint-send-string imaxima-gnuplot-process (concat "load \"" (shell-quote-argument gplt-src) "\"\n")))
	  )))))

(defvar imaxima-latex-src nil "The LaTeX code to generate the
current image. Used by `imaxima-make-image' to set the `latex'
property of the inserted text/image.")

(defun imaxima-tex-to-dvi (str label filename &optional linear)
  "Run LaTeX on STR.
Argument LABEL is used as equation label.  FILENAME is used for
temporary files.  Use linearized form if LINEAR is non-nil."
  (let ((latex-src
	 (concat
	  (imaxima-latex-set-pagecolor)
	  "\\pagestyle{empty}\n"
	  (format "\\begin{%s}\n" imaxima-fnt-size)
	  (imaxima-latex-set-labelcolor)
	  (if label
 	      (imaxima-latex-format-label str label)
	    (imaxima-latex-format-output str))
	  (format "\\end{%s}\n" imaxima-fnt-size))))
    (setq imaxima-latex-src latex-src)
    (with-temp-file filename
      (insert
       (imaxima-latex-document-class)
       "\n% mylatex\n" ; magic string
       (imaxima-latex-set-textwidth)
       (if linear imaxima-latex-macros-linear "")
       "\\begin{document}\n"
       latex-src
       "\\end{document}"))
    (imaxima-with-temp-dir imaxima-tmp-subdir
      (apply 'call-process imaxima-tex-program nil nil nil
	     (list "&mylatex" filename)))))

(defun imaxima-dvi-to-ps (filename)
  "Convert dvi file FILENAME to PostScript."
  (let ((dvips-args (append
		     imaxima-dvips-options
		     (list "-x" (format "%s" (* imaxima-scale-factor 1000))
			   "-y" (format "%s" (* imaxima-scale-factor 1000))
			   (concat filename ".dvi") "-o"))))
    (imaxima-with-temp-dir imaxima-tmp-subdir
      (apply 'call-process imaxima-dvips-program nil nil nil dvips-args))))

(defun imaxima-clean-up ()
  "Kill gs process, delete temporary files and restore colors if applicable."
  (interactive)
  (ignore-errors
    (kill-process imaxima-gs-process))
  (mapc 'delete-file (directory-files imaxima-tmp-subdir t "^[^.].*"))
  (delete-directory imaxima-tmp-subdir)
  (if (featurep 'xemacs)
      (ad-deactivate 'comint-output-filter)
    ;; restore frame colors in Emacs
    (when imaxima-fg-color
      (modify-frame-parameters
       nil (list (cons 'foreground-color imaxima-old-fg-color))))
    (when imaxima-bg-color
      (modify-frame-parameters
       nil (list (cons 'background-color imaxima-old-bg-color)))))
  (run-hooks 'imaxima-exit-hook))

(defvar *debug-imaxima-filter* nil
  "If `*debug-imaxima-filter*' is set to t, the string is
  appended to the end of the buffer *imaxima-work*. Used in
  `imaxima-filter1' and `debug-imaxima-filter'.")

(defun debug-imaxima-filter (str)
  (if *debug-imaxima-filter*
      (with-current-buffer (get-buffer-create "*imaxima-work*")
	(insert str))))

(cl-defun imaxima-filter (str)
  "Parse output from Maxima and make image from TeX parts.
Argument STR contains output received from Maxima.

   imaxima-filter needs to be written in re-entrant manner.
   This is because during the creation of latex image, there
   observed a reentrant call of imaxima-filter. yhonda"
  (if imaxima-filter-running
      (progn
	(setq imaxima-output (concat imaxima-output str))
	(debug-imaxima-filter "reenter")
	(cl-return-from imaxima-filter "")))
  (setq imaxima-filter-running t)
  (debug-imaxima-filter str)
  (let* ((len (length str))
	 main-output)
    (if (zerop len)
	(progn
	  (setq imaxima-filter-running nil)
	  (cl-return-from imaxima-filter ""))
      (setq imaxima-output (concat imaxima-output str))
      (let ((lastchar (aref str (1- len)))
	    (output ""))
	(when (and (char-equal lastchar ?\n) (> len 1))
	  (setq lastchar (aref str (- len 2))))

	(message "Processing Maxima output...")
	(while (not (string= imaxima-output ""))
	  (let ((1stchar (substring imaxima-output 0 1)))
	    (cond ((string= 1stchar "")
		   (if (string-match "\\([^]*\\)\\(\\(.\\|\n\\)*\\)" imaxima-output)
		       (let ((iprompt (match-string 1 imaxima-output))
			     (rest (match-string 2 imaxima-output)))
			 (setq imaxima-output rest)
			 (setq output (concat output iprompt))
			 ;; All the output for a maxima command are processed.
			 ;; We can call imaxima-continuation if necessary.
			 (cond ((and imaxima-continuation main-output)
				(funcall (car imaxima-continuation) main-output))
			       ((and imaxima-continuation (null main-output))
				(funcall (car imaxima-continuation) ""))))
		     ;; imaxima-output is incomplete.
		     (setq imaxima-filter-running nil)
		     (cl-return-from imaxima-filter output)))
		  ((string= 1stchar "")
		   (if (string-match "\\([^]*\\)\\(\\(.\\|\n\\)*\\)" imaxima-output)
		       (let ((match (match-string 1 imaxima-output))
			     (rest (match-string 2 imaxima-output))
			     image)
			 (setq imaxima-output rest)
			 (setq output (concat output (setq image (imaxima-make-image match 'latex)) "\n"))
			 ;; Remember the image into main-output if this is the first output.
			 ;; This will be passed to imaxima-continuation
			 (if (null main-output)
			     (setq main-output image)))
		     ;; imaxima-output is incomplete.
		     (setq imaxima-filter-running nil)
		     (cl-return-from imaxima-filter output)))
		  ((string= 1stchar "")
		   (if (string-match "\\([^]*\\)\\(\\(.\\|\n\\)*\\)" imaxima-output)
		       (let ((match (match-string 1 imaxima-output))
			     (rest (match-string 2 imaxima-output)))
			 (setq imaxima-output rest)
			 (setq output (concat output (imaxima-make-image match 'latex) "\n")))
		     ;; imaxima-output is incomplete.
		     (setq imaxima-filter-running nil)
		     (cl-return-from imaxima-filter output)))
		  (t (if (string-match "\\([^]*\\)\\(\\(.\\|\n\\)*\\)" imaxima-output)
			 (let ((match (match-string 1 imaxima-output))
			       (rest (match-string 2 imaxima-output)))
			   (setq imaxima-output rest)
			   (setq output (concat output match)))
		       ;; This should not happen.
		       (message "Unexpected error encountered in imaxima-filter"))))))
	(message "Processing Maxima output...done")
	(if imaxima-silence-filter (setq output "" imaxima-silence-filter nil))
	(setq imaxima-filter-running nil)
	(cl-return-from imaxima-filter output)))))

(defun imaxima-filter1 (str)
  "Parse output from Maxima and make image from TeX parts.
Argument STR contains output received from Maxima."
  (if *debug-imaxima-filter*
      (with-current-buffer (get-buffer-create "*imaxima-work*")
	(insert "****new string****
")
	(insert str)))
  (let* ((len (length str)))
    (if (zerop len)
	""
      (setq imaxima-output (concat imaxima-output str))
      (let ((lastchar (aref str (1- len))))
	(when (and (char-equal lastchar ?\n) (> len 1))
	  (setq lastchar (aref str (- len 2))))
	(cond
	 ;; Plain text
	 ((string-match "\\`[^]+\\'" imaxima-output)
	  (prog1 imaxima-output
	    (setq imaxima-output "")))
	 ((or (char-equal lastchar ?) (char-equal lastchar ?))
	  (let ((prompt "")
		(newline-char "
")
		(output "")
		(rest imaxima-output)
		text match)
	    (message "Processing Maxima output...")
	    (if (string-match "\\([^]*\\)\\([^]*\\)$" imaxima-output)
		(setq prompt (concat "" (match-string 2 imaxima-output))
		      rest (match-string 1 imaxima-output)))
	    (while (string-match "\\(\\([^]*\\)\\([^]*\\)\\)"
				 rest)
	      (setq text (match-string 2 rest))
	      (setq match (match-string 3 rest))
	      (setq rest (replace-match "" t t rest 1))
	      (setq output (concat output (if (equal output "") "" newline-char) text (imaxima-make-image match 'latex))))
	    (setq imaxima-output "")
	    (message "Processing Maxima output...done")
	    (if imaxima-continuation
		(funcall (car imaxima-continuation) output))
	    (concat output rest prompt)))
	 ;; Special prompt for demo() function.
	 ;; _ is prompted.
	 ((char-equal lastchar ?_)
	  (let ((newline-char "
")
		(output "")
		(rest (substring imaxima-output 0 -1))
		match text)
	    (message "Processing Maxima output...")
	    (while (string-match "\\(\\([^]*\\)\\([^]*\\)\\)"
				 rest)
	      (setq text (match-string 2 rest))
	      (setq match (match-string 3 rest))
	      (setq rest (replace-match "" t t rest 1))
	      (setq output (concat output (if (equal output "") "" newline-char) text (imaxima-make-image match 'latex))))
	    (setq imaxima-output "")
	    (message "Processing Maxima output...done")
	    (if imaxima-continuation
		(funcall (car imaxima-continuation) output))
	    (concat " " output rest newline-char "_")))
	 ;; Special prompt, question.
	 ((char-equal lastchar ?)
	  (string-match "\\([^]*\\)" imaxima-output)
	  (prog1 (imaxima-make-image (match-string 1 imaxima-output) 'latex)
	    (setq imaxima-output "")))
	 (t ""))))))

(defun xemacs-set-imagefile-properties (filename img-type str)
  (let ((ext (make-extent 0 (length str) str)))
    (set-extent-property ext 'duplicable t)
    (set-extent-end-glyph ext
			  (make-glyph (vector img-type
					      :file filename)))
    (set-extent-property ext 'invisible t)
    (set-extent-property ext 'atomic t))
  str)

(eval-when-compile
  (ignore-errors
    (require 'maxima)))

(defun imaxima-setup-preoutput-filter ()
  "Set up `comint-preoutput-filter-functions' or the equivalent."
  (cond ((featurep 'xemacs)
	 ;; XEmacs does not have comint-preoutput-filter-functions, so
	 ;; we have to advice comint-output-filter instead
	 (defadvice comint-output-filter (before preoutput-filter)
	   "Run comint-preoutput-filter-functions."
	   (ad-set-arg 1 (imaxima-filter (ad-get-arg 1))))
	 (ad-activate 'comint-output-filter))
	(t
	 (make-local-variable 'comint-preoutput-filter-functions)
	 ;; This doesn't work due to a bug in comint.el
	 ;; (add-hook 'comint-preoutput-filter-functions 'imaxima-filter nil t)
	 (add-hook 'comint-preoutput-filter-functions 'imaxima-filter t))))

(defun imaxima-change-color (buf)
  "Change background and foreground color if applicable.
BUF is imaxima buffer."
  (cond
   ((featurep 'xemacs)
    (when imaxima-bg-color
      (set-face-background 'default imaxima-bg-color buf))
    (when imaxima-fg-color
      (set-face-foreground 'default imaxima-fg-color buf)))
   (t
    (when imaxima-bg-color
      (setq imaxima-old-bg-color (frame-parameter nil 'background-color))
      (modify-frame-parameters
       nil (list (cons 'background-color imaxima-bg-color))))
    (when imaxima-fg-color
      (setq imaxima-old-fg-color (frame-parameter nil 'foreground-color))
      (modify-frame-parameters
       nil (list (cons 'foreground-color imaxima-fg-color)))))))

(defun imaxima-setup ()
  "Image support for maxima.el."
  (let ((mbuf (process-buffer inferior-maxima-process)))
    (with-current-buffer mbuf
      (imaxima-change-color mbuf)
      (imaxima-get-geometry mbuf)
      (imaxima-dump-tex)
      (unless (eq imaxima-image-type 'postscript)
	(imaxima-start-gs))
      (add-hook 'kill-buffer-hook 'imaxima-clean-up t t)
      (imaxima-setup-preoutput-filter)
      (imaxima-with-no-new-input-prompt
       (comint-send-string mbuf (format ":lisp (progn ($load \"%s\") (msetq $imaxima_tmp_subdir \"%s\"))\n" imaxima-lisp-file imaxima-tmp-subdir)))
      ;; maxima mode tries to run inferior-maxima-mode-hook twice
      ;; due to changes made in 5.9.2 release. To prevent this,
      ;; the following hook must be removed earlier than before.
      ;; y.honda
      (remove-hook 'inferior-maxima-mode-hook 'imaxima-setup)
      (goto-char (point-max)))))

(cl-defun imaxima-delete-maxima-hooks ()
  (remove-hook 'comint-output-filter-functions 'inferior-maxima-output-filter)
  (remove-hook 'comint-output-filter-functions 'inferior-maxima-remove-double-input-prompt)
  (remove-hook 'comint-output-filter-functions 'inferior-maxima-remove-double-prompt))

(cl-defun imaxima ()
  "Image support for Maxima.
\"display2d:true\" in Maxima turns images off, \"display2d:imaxima\"
turns them on.  Set `imaxima-use-maxima-mode-flag' to t to use
`maxima.el'."
  (interactive)
  (if (not window-system)
      (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."))
  (let ((imaxima-buffer))
    (setq imaxima-buffer
	  (get-buffer (if imaxima-use-maxima-mode-flag
			  "*maxima*"
			"*imaxima*")))
    (when imaxima-buffer
      (if (called-interactively-p 'any)
	  (switch-to-buffer imaxima-buffer)
	(set-buffer imaxima-buffer))
      (cl-return-from imaxima t)))
  (reinit-imaxima)
  (unless (imaxima-image-type-available-p imaxima-image-type)
    (error "Your version of Emacs does not support the image type %s"
	   imaxima-image-type))
  (unless imaxima-lisp-file
    (error "The file imaxima.lisp could not be found.
Please customize the option `imaxima-lisp-file'."))
  (setq imaxima-file-counter 0)
  (make-directory
   (setq imaxima-tmp-subdir
	 (make-temp-name (expand-file-name "imaxima" imaxima-tmp-dir))) t)
  (set-file-modes imaxima-tmp-subdir 448) ; 700 in octal
  (let ((process-connection-type process-connection-type-flag))
    (if imaxima-use-maxima-mode-flag
	(progn
	  (require 'maxima)
	  (setq inferior-maxima-prompt
		(concat "\\(^ ?(" maxima-inchar 
			"[0-9]*) \\)\\|\\(^MAXIMA>+\\)\\|\\(^(dbm:[0-9]*) \\)"))
	  (add-hook 'inferior-maxima-mode-hook 'imaxima-setup t)
	  (maxima)
	  (remove-hook 'inferior-maxima-mode-hook 'imaxima-setup))
      (imaxima-delete-maxima-hooks)
      (setq imaxima-output "")
      (let ((mbuf
	     (apply 'make-comint
	      "imaxima"
	      imaxima-maxima-program
	      nil
	      (split-string
	       imaxima-maxima-options))))
	(with-current-buffer mbuf
	  (setq imaxima-process (get-buffer-process mbuf))
	  (imaxima-get-geometry mbuf)
	  (imaxima-change-color mbuf)
	  (imaxima-dump-tex)
	  (set-process-sentinel imaxima-process 'imaxima-process-sentinel)
	  (imaxima-setup-preoutput-filter)
	  (unless (eq imaxima-image-type 'postscript)
	    (imaxima-start-gs)))
	(imaxima-with-no-new-input-prompt
	 (let ((maxima-set-up (list 'progn (list 'msetq '$imaxima_tmp_subdir imaxima-tmp-subdir) "")))
	   (comint-send-string mbuf (format ":lisp %S\n" maxima-set-up))))
      (switch-to-buffer mbuf))))
  (run-hooks 'imaxima-startup-hook))

(defcustom imaxima-print-tex-file "imax"
  "Name of the LaTeX file name to be created by `imaxima-print-buffer'.
Do not include \".tex\" suffix.  This file will be stored in the
directory `imaxima-tmp-dir'."
  :group 'imaxima
  :type 'string)

(defcustom imaxima-print-tex-command 
  "latex %s; dvips -o imax.ps %s; gv imax.ps"
  ;;"latex %s; xdvi %s"
  ;;"latex %s; dvipdf %s.dvi imax.pdf; open imax.pdf" for Mac OS X users.
  "Command to run LaTeX on the file created by `imaxima-print-buffer'.
In the string %s is replaced by the name of the tex file. e.g.
\"latex %s; xdvi %s\"
"
  :group 'imaxima
  :type 'string)

(defun imaxima-print-buffer ()
  "Run LaTeX on the current buffer and show output.
See `imaxima-print-tex-command' for how latex is run on the latex output."
  (interactive "")
  (let (( tex-file (concat imaxima-tmp-dir 
			   imaxima-print-tex-file ".tex"))
	(buf)
	(cmd)
	)
    
    (imaxima-latex)
    (write-file tex-file)
    (setq buf (current-buffer))
    
    ;; Convert all %s into the tex file name.
    (setq cmd imaxima-print-tex-command)
    (while (string-match "%s" cmd)
      (setq cmd (replace-match imaxima-print-tex-file t nil cmd)))

    (shell-command cmd)
    (kill-buffer buf)			;kill the temp tex buffer
    ))


;;; The following codes implements export imath text to HTML.

(defvar html-template
"<HTML> 
 <HEAD> 
 <META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; CHARSET=UTF-8\"> 
 <TITLE></TITLE> 
 </HEAD> 
 <BODY> 


 </BODY> 
 </HTML>
")

(cl-defun prepare-for-translation ()
  "If error occurs inside this function, multiple values nil nil
   will be returned."
  (interactive "")
  (save-excursion
    (let (original-buffer text current-buffer-file-name filename image-folder html-buffer)
      (setq original-buffer (current-buffer))
      (setq text (buffer-substring (point-min) (point-max)))
      (if (or (string= (buffer-name original-buffer)
		       "*maxima*")
	      (string= (buffer-name original-buffer)
		       "*imaxima*"))
	  (progn
	    (setq current-buffer-file-name "")
	    (setq filename (concat imaxima-html-dir "session.html"))
	    (setq image-folder "session-images"))
	(setq current-buffer-file-name
	      (if (buffer-file-name)
		  (buffer-file-name)
		(cl-return-from prepare-for-translation (cl-values nil nil))))
	(setq filename (concat (file-name-sans-extension
				(file-name-nondirectory current-buffer-file-name))
			       ".html"))
	(setq image-folder (concat (file-name-sans-extension
				    (file-name-nondirectory current-buffer-file-name))
				   "-images")))
      ;; HTML buffer preparation
      (setq html-buffer (find-file filename))
      (set-buffer html-buffer)
      ;; create image folder
      (let (old-files)
	(if (file-exists-p image-folder)
	    ;; since image-folder already exists, let's reuse it.
	    (progn
	      (condition-case err
		  (progn
		    (setq old-files (directory-files image-folder nil "\\.png$"))
		    ;; we need to delete all the files already there.
		    (dolist (f old-files)
		      (delete-file (concat image-folder "/" f))))
		(file-error (cl-return-from prepare-for-translation (cl-values nil nil)))))
	  ;; since image-folder doest not exist, let's create it.
	  (condition-case err
	      (make-directory image-folder)
	    (file-error (cl-return-from prepare-for-translation (cl-values nil nil))))))
      ;; buffer preparation
      (if buffer-read-only
	  (cl-return-from prepare-for-translation (cl-values nil nil)))
      (erase-buffer)
      (insert html-template)
      (goto-char (point-min))
      (search-forward "<BODY>")
      (forward-line 1)
      (insert text)
      (cl-values html-buffer image-folder))))

(cl-defun imath-to-html()
  "Translate imath minor mode buffer contents into HTML format."
  (interactive "")
  (save-excursion
    (cl-multiple-value-bind (html-buffer image-folder)
	(prepare-for-translation)
      (if (not (and html-buffer image-folder))
	  (progn
	    (message "Error during HTML buffer preparation.")
	    (cl-return-from imath-to-html)))
      (set-buffer html-buffer)
      (goto-char (point-min))
      (condition-case err
	  (loop 
	   (cl-multiple-value-bind (ftype start-pos end-pos)
	       (find-next-formula)
	     (if (not (and ftype start-pos end-pos))
		 (return t) 
	       (let (filename dest-name)
		 ;; copy image file to image sub folder
		 (if (null (setq filename (get-image-filename (1- (point)))))
		     (progn
		       (message "Error: all formulas must be converted to images first.")
		       (cl-return-from imath-to-html)))
		 (setq dest-name (concat
				  image-folder "/"
				  (file-name-sans-extension
				   (file-name-nondirectory filename))
				  ".png"))
				  ;;))
		 (copy-file filename dest-name)
		 ;; replace imath formula with HTML IMG tag
		 (delete-region start-pos end-pos)
		 (insert (concat "<IMG SRC=\""
				 dest-name
				 "\" style=\"vertical-align:middle;\"> "))))))
				 ;; "\" align=\"middle\"> "))))))
	(search-failed nil)
	(file-error
	 (progn
	   (message "Error: File manipulation failed during processing.")
	   (cl-return-from imath-to-html))))
      (condition-case err
	  (let (start-mark end-mark)
	    (goto-char (point-min))
	    (search-forward "<BODY>")
	    (forward-line 1)
	    (beginning-of-line)
	    (setq start-mark (point-marker))
	    (search-forward "</BODY>")
	    (setq end-mark (point-marker))
	    (goto-char start-mark)
	    (loop
	     (re-search-forward "$" end-mark)
	     (replace-match "<br>\n")
	     (forward-line 1)))
	(search-failed nil)))))

(cl-defun imaxima-to-html ()
  "Translate the imaxima buffer contents into HTML format."
  (interactive "")
  (imath-to-html))

(cl-defun find-next-formula ()
  "Find next formula and return multiple values of
   formula type, start position and end position.
   If search failed, error search-failed is signaled."
  (interactive "")
  (if (equal (point) (point-max))
      (cl-return-from find-next-formula (cl-values nil nil nil)))
  (let* ((region-start (copy-marker (point)))
	 (region-end (copy-marker (next-single-property-change (point) 'display nil (point-max))))
	 (text-prop (get-text-property region-start 'display)))
    (goto-char region-end)
    (if text-prop
	(cl-return-from find-next-formula (cl-values 'any region-start region-end))
      (find-next-formula))))

  

(cl-defun get-image-filename (pos)
  "If the pos of the buffer is associated with text a display property,
   it is obtained. Then image filename of the display property is 
   extracted and returned."
  (interactive "")
  (if (null pos)
      (setq pos (point)))
  (let (filename)
    (if (setq filename (memq :file (get-text-property pos 'display)))
	(second filename))))

(provide 'imaxima)

;;; imaxima.el ends here
