emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: "Jan Böcker" <jan.boecker@jboecker.de>
To: Sebastian Berchtold <Sebastian.Berchtold@campus.lmu.de>
Cc: emacs-orgmode@gnu.org
Subject: Re: [WISH] Latex WYSIWYG
Date: Fri, 24 Jun 2011 16:14:42 +0200	[thread overview]
Message-ID: <4E049BD2.10309@jboecker.de> (raw)
In-Reply-To: <10933380.173491308615732676.JavaMail.tomcat@tomcat2>

[-- Attachment #1: Type: text/plain, Size: 1294 bytes --]

On 06/21/2011 02:22 AM, Sebastian Berchtold wrote:
> Hi, my first post here: So big shout outs to all org-mode hackers.
> Great Stuff!
> 
> There is one feature I'd really like to have, but i couldn't find
> anything that does what i want. I guess it's just a small hack, but
> with my retarded elisp skills it would take me days to implement it.
> 
> WYSIWYG Latex is probably not the correct term for this. What i have
> in mind is more like seeing the formula, while editing the
> ASCII/Unicode representation. I'll try to explain the idea more
> detailed.

Hi Sebastian,

I have attached a quick and dirty hack I wrote in December 2010.
It works by updating an image file whenever the formula at point in your
Org buffer changes.
If that image file is currently open in an image buffer, that buffer is
automatically refreshed.

If LaTeX reports an error, the image will not be updated, so you have to
check if the image updates when you expect it to to avoid syntax errors.

See the comments at the top of the file for usage instructions.

Unfortunately, due to lack of time and knowledge of org-mode and elisp,
this thing likely still has a lot of bugs and is not implemented
properly (I guess it should be a minor mode).
However, it seems to work in simple cases.

Hope this helps,
  Jan

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: latex-live-preview.el --]
[-- Type: text/x-emacs-lisp; name="latex-live-preview.el", Size: 8274 bytes --]

;; latex-live-preview.el by Jan Böcker <jan.boecker@jboecker.de>
;; Feel free to improve this.
;; License: Pick one of GNU GPL v3, BSD, MIT

;; This is a quick and dirty hack to show an automatically updated preview image
;; of the LaTeX formula at point in an org-mode buffer.

;; Usage:
;; * Eval the elisp code in this file in some way (e.g. M-x load-file or M-x eval-buffer)
;; * In your org buffer: M-x jb/toggle-latex-live-preview
;; * Split the window (e.g. C-x 2)
;; * open the exported image file (default: /tmp/livepreview.png) in
;;   the second buffer
;; * edit your Org file

;; To speed things up slightly, you might want to move the tempdir to a ramdisk.



;; the tempdir will contain the exported image and might be cluttered
;; with temporary files from running LaTeX if something goes wrong

(defconst jb/latex-live-preview-tempdir "/tmp/")  ; must include final /
(defconst jb/latex-live-preview-filename "livepreview.png")

(defvar jb/current-texfilebase "")
(defvar jb/current-tmpdir nil)
(defvar jb/current-tofile "")
(defvar jb/latex-preview-job nil)
(defvar jb/enable-live-preview nil)



(defun jb/ocfi (string tofile options buffer)
  "Modified version of org-create-formula-image"
  (require 'org-latex)
  (let* ((tmpdir (if (featurep 'xemacs)
		     (temp-directory)
		   temporary-file-directory))
	 (texfilebase (make-temp-name
		       (expand-file-name "orgtex" tmpdir)))
	 (texfile (concat texfilebase ".tex"))
	 (dvifile (concat texfilebase ".dvi"))
	 (pngfile (concat texfilebase ".png"))
	 (fnh (if (featurep 'xemacs)
                  (font-height (get-face-font 'default))
                (face-attribute 'default :height nil)))
	 (scale (or (plist-get options (if buffer :scale :html-scale)) 1.0))
	 (dpi (number-to-string (* scale (floor (* 0.9 (if buffer fnh 140.))))))
	 (fg (or (plist-get options (if buffer :foreground :html-foreground))
		 "Black"))
	 (bg (or (plist-get options (if buffer :background :html-background))
		 "Transparent")))
    (if (eq fg 'default) (setq fg (org-dvipng-color :foreground)))
    (if (eq bg 'default) (setq bg (org-dvipng-color :background)))
    (with-temp-file texfile
      (insert (org-splice-latex-header
	       org-format-latex-header
	       org-export-latex-default-packages-alist
	       org-export-latex-packages-alist t
	       org-format-latex-header-extra))
      (insert "\n\\begin{document}\n" string "\n\\end{document}\n")
      (require 'org-latex)
      (org-export-latex-fix-inputenc))
	(setq jb/current-texfilebase texfilebase)
	(setq jb/current-tmpdir tmpdir)
	(setq jb/current-tofile tofile)
	(jb/call-latex)
))

(defun jb/call-latex ()
  (cd tmpdir)
  (set-process-sentinel
   (start-process "live-latex-preview-latex" ; process name
				  nil ; no buffer
				  "latex"
				  "--halt-on-error"
				  (concat jb/current-texfilebase ".tex"))
   'jb/call-latex-sentinel)
  ;(message "latex-live-preview: started latex")
)

(defun jb/call-latex-sentinel (process event)
  ;(message "latex-sentinel: %s" event)
  (when (string= event "finished\n")
	(jb/call-dvipng))
  (when (string= event "exited abnormally with code 1\n")
	(setq jb/current-tmpdir nil)
	(jb/update-latex-preview)))

(defun jb/call-dvipng ()
  (let* ((tmpdir jb/current-tmpdir)
		 (buffer nil)
		 (options org-format-latex-options)
		 (texfilebase jb/current-texfilebase)
		 (texfile (concat texfilebase ".tex"))
		 (dvifile (concat texfilebase ".dvi"))
		 (pngfile (concat texfilebase ".png"))
		 (fnh (if (featurep 'xemacs)
                  (font-height (get-face-font 'default))
                (face-attribute 'default :height nil)))
		 (scale (or (plist-get options (if buffer :scale :html-scale)) 1.0))
		 (dpi (number-to-string (* scale (floor (* 0.9 (if buffer fnh 140.))))))
		 (fg (or (plist-get options (if buffer :foreground :html-foreground))
				 "Black"))
		 (bg (or (plist-get options (if buffer :background :html-background))
				 "Transparent")))
    (if (eq fg 'default) (setq fg (org-dvipng-color :foreground)))
    (if (eq bg 'default) (setq bg (org-dvipng-color :background)))

	(cd tmpdir)
	(set-process-sentinel
	 (start-process "live-latex-preview-dvipng"
					nil
					"dvipng"
					"-fg" fg "-bg" bg
					"-D" dpi
					;;"-x" scale "-y" scale
					"-T" "tight"
					"-o" pngfile
					dvifile)
	 'jb/call-dvipng-sentinel)
))



(defun jb/call-dvipng-sentinel (process event)
  (when (string= event "finished\n")
	(copy-file (concat jb/current-texfilebase ".png") jb/current-tofile 'replace)

	(loop for e in '(".dvi" ".tex" ".aux" ".log" ".png") do
	      (delete-file (concat jb/current-texfilebase e)))
	
	(when (get-buffer jb/latex-live-preview-filename)
	  (set-buffer jb/latex-live-preview-filename)	
	  (jb/refresh-current-image-buffer))
	
	(setq jb/current-tmpdir nil)
	(jb/update-latex-preview)
))

(defun jb/refresh-current-image-buffer ()
	(flet ((message (msg &rest args) nil))
	  (clear-image-cache)
	  (revert-buffer nil t t)
	  (image-mode)
))

(defun jb/update-latex-preview ()
  (when (and jb/latex-preview-job
			 (not jb/current-tmpdir))
	(jb/preview-fragment jb/latex-preview-job)
	(setq jb/latex-preview-job nil)))
	
(defun jb/preview-fragment (latex)
(save-excursion
 (let ((temporary-file-directory jb/latex-live-preview-tempdir))
   (jb/ocfi latex (concat jb/latex-live-preview-tempdir jb/latex-live-preview-filename) org-format-latex-options nil))
))



(defun jb/plfp ()
"Preview the latex fragment at point"
  (interactive)
  (catch 'exit
	(save-excursion
	  (save-restriction
		(let ((at (org-inside-LaTeX-fragment-p))
			  beg end msg start-delimiter end-delimiter
			  (add-end-delimiter nil))
		  (if (not at)
			  (progn
				(setq jb/latex-preview-job "$ $")
				(jb/update-latex-preview))
			(progn
			  (goto-char (cdr at))
			  (setq beg (point))
			  (setq start-delimiter (car at))
		;;	  (message "start-delimiter is %s " start-delimiter)

			  ;; Determine what the end of this block must look like
			  (setq end-delimiter (cdr (assoc start-delimiter
											  '(("$$" . "$$"),
												("\\[" . "\\]"),
												("\\(" . "\\)")))))
			  
			  ;; Set end-delimiter to \end{region} if block starts
			  ;; with \begin{region}
			  (when (not end-delimiter)
				(save-match-data
				  (if (not (string-match "\\\\begin{\\([^}]*\\)}" start-delimiter))
					  (throw 'exit nil))
				  (setq end-delimiter (concat "\\end{" (match-string 1 start-delimiter) "}"))))

		;;	  (message "end-delimiter is %s " end-delimiter)
			  
			  ;; set end of our region
			  (goto-char (1+ beg))
			  (setq end (re-search-forward (regexp-quote end-delimiter) nil t))
			  
			  ;; If there is no end delimiter, make a note that we
			  ;; need to add it later before creating the image.
			  ;; This will make the preview work on unfinished LaTeX blocks.
			  (when (not end) 
				(setq end (point-max))
				(setq add-end-delimiter t))

			  (narrow-to-region beg end)
		;;	  (message "-%s-" (buffer-string))
			  (goto-char beg)
			  
			  (setq jb/latex-preview-job (concat (buffer-string) (if add-end-delimiter end-delimiter "")  ))
			  (jb/update-latex-preview))))))))

(defun pseudo-message (format-string &rest args)
  nil)

(defun jb/ach (a b c)
  "hook for after-change-functions"
  (when jb/enable-live-preview
	(let ((message (lambda (s &rest args) nil)))
	  (jb/plfp)
	  (setq deactivate-mark nil))))

(defun jb/pch ()
  "hook for post-command-hook"
  (when jb/enable-live-preview
	(let ((message (lambda (s &rest args) nil)))
	  (jb/plfp)
	  (setq deactivate-mark nil))))

(defun jb/toggle-latex-live-preview ()
  "toggles the buffer-local variable jb/enable-live-preview"
  (interactive)
  (setq jb/current-tmpdir nil)
  (make-variable-buffer-local 'jb/enable-live-preview)
  (setq jb/enable-live-preview (not jb/enable-live-preview)))

(defun jb/clear-current-tmpdir ()
  (interactive)
  (setq jb/current-tmpdir nil))

(remove-hook 'after-change-functions 'jb/ach)
(remove-hook 'post-command-hook 'jb/pch)

(add-hook 'post-command-hook 'jb/pch)
(add-hook 'after-change-functions 'jb/ach)


      parent reply	other threads:[~2011-06-24 14:14 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-06-21  0:22 [WISH] Latex WYSIWYG Sebastian Berchtold
2011-06-21 15:30 ` Eric S Fraga
2011-06-24 14:14 ` Jan Böcker [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4E049BD2.10309@jboecker.de \
    --to=jan.boecker@jboecker.de \
    --cc=Sebastian.Berchtold@campus.lmu.de \
    --cc=emacs-orgmode@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).