yuuji@518: ;;; yatexflt.el --- YaTeX filter command utilizer -*- coding: sjis -*- yuuji@518: ;;; yuuji@518: ;;; (c)1993-2018 by HIROSE Yuuji.[yuuji@yatex.org] yuuji@547: ;;; Last modified Sat Jun 2 18:12:41 2018 on firestorm yuuji@518: ;;; $Id$ yuuji@518: yuuji@518: ;;; Commentary: yuuji@518: ;;; yuuji@518: ;;; This lisp enables passing inline text to some external filter yuuji@518: ;;; command to generate files such as graphic files. yuuji@518: ;;; yuuji@518: ;;; Typical situation is using blockdiag/dot(graphviz) command to yuuji@518: ;;; generate png/pdf file. yuuji@518: ;;; yuuji@518: ;;; Example: yuuji@518: ;;; yuuji@518: ;;; [[LaTeX Source]] yuuji@518: ;;; %#BEGIN FILTER{foo.pdf}{dot -T %t -o o} yuuji@518: ;;; \if0 yuuji@518: ;;; === yuuji@518: ;;; digraph { yuuji@518: ;;; A -> B; yuuji@518: ;;; B -> C; yuuji@518: ;;; } yuuji@518: ;;; === yuuji@518: ;;; \fi yuuji@518: ;;; %#END yuuji@518: ;;; \includegraphics{foo.pdf} yuuji@518: ;;; yuuji@518: ;;; In this case above, when you type `[prefix] t e' between two yuuji@518: ;;; `===' lines, the content in a region are fed to dot command as yuuji@518: ;;; follows: yuuji@518: ;;; yuuji@518: ;;; echo TEXT | dot -T pdf -o foo.pdf yuuji@518: ;;; yuuji@518: ;;; Then foo.pdf file will be generated and the image (as PNG) will yuuji@518: ;;; be displayed in the next window. yuuji@518: yuuji@521: yuuji@518: ;;; Code: yuuji@521: (require 'yatexlib) yuuji@521: (defvar YaTeX-filter-special-env-alist-default yuuji@521: '((".blockdiag" yuuji@521: "blockdiag -T %t -o %o -" yuuji@521: "blockdiag { yuuji@521: default_fontsize = 32; yuuji@521: A -> B; yuuji@521: }") yuuji@521: (".seqdiag" "seqdiag -T %t -o %o -" yuuji@521: "seqdiag { yuuji@521: client -> server [label = \"SYN\"]; yuuji@521: client <- server [label = \"SYN/ACK\"]; yuuji@521: client -> server [label = \"ACK\"];}") yuuji@521: (".actdiag" "actdiag -T %t -o %o -" yuuji@521: "actdiag { yuuji@521: sayHo -> ho -> hohoho yuuji@521: lane dj { yuuji@521: label = \"DJ\" yuuji@521: sayHo [label = \"Say Ho\"]; hohoho [label = \"Ho Ho Ho!\"]; } yuuji@521: lane mc { label = \"MC\"; ho [label = \"Hooooh!\"]}}") yuuji@521: (".nwdiag" "nwdiag -T %t -o %o -" yuuji@521: "nwdiag { yuuji@521: network ext { yuuji@521: address = \"10.1.2.0/24\" yuuji@521: router [address = \"10.1.2.1\"] yuuji@521: } yuuji@521: network int { yuuji@521: address = \"192.168.22.0/24\" yuuji@521: router [address = \"192.168.22.1\"] yuuji@521: websrv [address = \"192.168.22.80\"] yuuji@521: cli-1; cli-2 yuuji@521: } yuuji@521: }") yuuji@521: (".rackdiag" "rackdiag -T %t -o %o -" yuuji@521: "rackdiag { yuuji@521: 16U; yuuji@521: 1: UPS [4U]; 5: Storage [3U]; 8: PC [2U]; 8: PC [2U]; yuuji@521: }") yuuji@521: (".dot" yuuji@521: "dot -T %t -o %o" yuuji@521: "digraph { yuuji@521: graph [charset=\"utf-8\"] yuuji@547: A -> B yuuji@547: }" yuuji@521: ))) yuuji@521: yuuji@544: ;;;###autoload yuuji@544: (defun YaTeX-filter-goto-source (file other-win) yuuji@544: "Go to corresponding text source of the graphic file" yuuji@544: (cond yuuji@544: ((file-exists-p file) yuuji@544: (let ((buf (find-file-noselect file))) yuuji@544: (funcall (cond (other-win 'YaTeX-switch-to-buffer-other-window) yuuji@544: ((get-buffer-window buf) 'goto-buffer-window) yuuji@544: (t 'YaTeX-switch-to-buffer)) yuuji@544: buf))))) yuuji@544: yuuji@521: (defvar YaTeX-filter-special-env-alist-private nil) yuuji@521: (defvar YaTeX-filter-special-env-alist yuuji@521: (append YaTeX-filter-special-env-alist-private yuuji@521: YaTeX-filter-special-env-alist-default)) yuuji@521: yuuji@518: (defun YaTeX-filter-filter-set-conversion-flag () yuuji@518: (let ((ovl (get 'YaTeX-filter-filter-sentinel 'overlay))) yuuji@518: (if ovl ;; When successful conversion met, yuuji@518: (progn ;; (1)Set conversion complete flag yuuji@518: (add-hook ;; (2)Add hook of seim-automatic yuuji@518: 'write-file-hooks ;; update of convert to write- yuuji@518: 'YaTeX-filter-update-all) ;; file hook. yuuji@518: (overlay-put ovl 'converted t))))) yuuji@518: yuuji@518: (defun YaTeX-filter-filter-unset-conversion-flag yuuji@518: (ovl after beg end &optional length) yuuji@518: (if after (overlay-put ovl 'converted nil))) yuuji@518: yuuji@518: yuuji@518: (defun YaTeX-filter-pngify-sentinel (proc msg) yuuji@518: (save-excursion yuuji@518: (let ((b (process-buffer proc)) (selw (selected-window)) yuuji@518: img) yuuji@518: (set-buffer b) yuuji@518: (cond yuuji@518: ((eq (process-status proc) 'run) yuuji@518: (put-text-property (point-min) (point-max) 'invisible t)) yuuji@518: ((eq (process-status proc) 'exit) yuuji@518: (set-buffer b) yuuji@518: (YaTeX-popup-image yuuji@518: (YaTeX-buffer-substring yuuji@518: (get 'YaTeX-filter-pngify-sentinel 'start) (point-max)) yuuji@518: b) yuuji@518: (YaTeX-filter-filter-set-conversion-flag)) yuuji@518: (t yuuji@518: (set-buffer b) yuuji@518: (remove-text-properties (point-min) (point-max) '(invisible t)) yuuji@518: (insert "\nProcess aborted %s\n" msg)))))) yuuji@518: yuuji@518: (defvar YaTeX-filter-pdf2png-stdout yuuji@518: (cond yuuji@518: ((YaTeX-executable-find "convert") "convert -trim %s PNG:-") yuuji@518: (t yuuji@518: "gs -dNOPAUSE -sDEVICE=png256 -sOutputFile=- -dBATCH -q -r75 %s")) yuuji@518: "Command line syntax to convert PDF file to PNG stream") yuuji@518: yuuji@518: (defun YaTeX-filter-modified-BEGEND-regions () yuuji@518: "Return the list of overlays which contains un-converted text." yuuji@518: (save-excursion yuuji@518: (save-restriction yuuji@518: (widen) yuuji@524: (let (r prop dest src pl (list (overlays-in (point-min) (point-max)))) yuuji@518: (while list yuuji@524: (setq prop (overlay-properties (car list))) yuuji@524: (if (setq dest (plist-get prop 'filter-output)) yuuji@524: (if (if (setq src (plist-get prop 'filter-source)) yuuji@524: (file-newer-than-file-p src dest) yuuji@524: (and (setq pl (plist-member prop 'converted)) yuuji@524: (not (plist-get pl 'converted)))) yuuji@524: (setq r (cons (car list) r)))) yuuji@518: (setq list (cdr list))) yuuji@518: (nconc r) yuuji@518: r)))) yuuji@518: yuuji@518: (defun YaTeX-filter-update-all () yuuji@518: "Update all destination files from built-in source text." yuuji@518: (interactive) yuuji@518: (let ((timeout 4) yuuji@518: ans ovl (update-list (YaTeX-filter-modified-BEGEND-regions))) yuuji@518: (if update-list yuuji@518: (save-excursion yuuji@518: (save-window-excursion yuuji@518: (catch 'abort yuuji@518: (while update-list yuuji@518: (goto-char (overlay-start (setq ovl (car update-list)))) yuuji@518: (or (pos-visible-in-window-p) yuuji@518: (set-window-start nil (point))) yuuji@518: (unwind-protect yuuji@518: (progn yuuji@518: (overlay-put ovl 'face 'YaTeX-on-the-fly-activated-face) yuuji@518: (message "Non-update source found: Update here: %s " yuuji@524: "Y)es N)o S)top-watching-Here A)bort") yuuji@518: (setq ans (read-char)) yuuji@518: (cond yuuji@518: ((memq ans '(?Y ?y)) yuuji@518: (YaTeX-filter-BEGEND) yuuji@518: (while (and (> (setq timeout (1- timeout))) yuuji@518: (eq (process-status "Filter") 'run)) yuuji@518: (message "Waiting for conversion process to finish") yuuji@518: (sit-for 1))) yuuji@518: ((memq ans '(?A ?a)) (throw 'abort t)) yuuji@524: ((memq ans '(?S ?s)) (delete-overlay ovl)) yuuji@518: (t nil))) yuuji@518: (overlay-put ovl 'face nil)) yuuji@518: (setq update-list (cdr update-list))))))) yuuji@518: ;; Write file hook should return nil yuuji@518: nil)) yuuji@518: yuuji@518: (defun YaTeX-filter-filter-sentinel (proc msg) yuuji@518: (put 'YaTeX-filter-pngify-sentinel 'start nil) yuuji@518: (let ((b (process-buffer proc)) yuuji@518: (imagefile (get 'YaTeX-filter-filter-sentinel 'outfile)) yuuji@518: ovl yuuji@518: (selw (selected-window))) yuuji@518: (save-excursion yuuji@518: (cond yuuji@518: ((eq (process-status proc) 'run)) yuuji@518: ((eq (process-status proc) 'exit) yuuji@518: (set-buffer b) yuuji@518: (remove-images (point-min) (point-max)) yuuji@518: (if (and (file-regular-p imagefile) yuuji@518: (file-readable-p imagefile)) yuuji@518: (save-excursion yuuji@518: (setq buffer-read-only nil) yuuji@518: (cond yuuji@518: ((string-match "\\.\\(jpg\\|png\\)" imagefile) yuuji@518: (erase-buffer) yuuji@518: (YaTeX-popup-image imagefile b) yuuji@518: (YaTeX-filter-filter-set-conversion-flag)) yuuji@518: (t ;Convert again to PNG file yuuji@518: (goto-char (point-max)) yuuji@518: (insert "\nConvert Again to PNG file...\n") yuuji@518: (put 'YaTeX-filter-pngify-sentinel 'start (point)) yuuji@518: (set-process-sentinel yuuji@518: (start-process yuuji@518: "Filter" b ;Safe to reuse yuuji@518: shell-file-name YaTeX-shell-command-option yuuji@518: (format YaTeX-filter-pdf2png-stdout imagefile)) yuuji@518: 'YaTeX-filter-pngify-sentinel) yuuji@518: (set-buffer-multibyte nil) yuuji@518: )) yuuji@518: (select-window selw))) yuuji@518: (YaTeX-preview-image-mode) yuuji@518: ) yuuji@518: (t ;Other status might be an error yuuji@518: (set-buffer b) yuuji@518: (goto-char (point-max)) yuuji@518: (insert (format "%s\n" (process-status proc)))))))) yuuji@518: yuuji@547: (defvar YaTeX-filter-block-marker "===" yuuji@547: "Begining and Ending marker for contents for external filter program") yuuji@547: (defvar YaTeX-filter-src "#SRC" yuuji@547: "Keyword for input filename for external filter program") yuuji@547: yuuji@524: (defun YaTeX-filter-parse-filter-region (begend-info) yuuji@524: "Return the list of SpecialFilter region. If not on, return nil. yuuji@524: BEGEND-INFO is a value from the function YaTeX-in-BEGEND-p. yuuji@524: Return the alist of: yuuji@524: '((outfile $OutPutFileName) yuuji@524: (source $InputFileName) ; or nil for embeded data source yuuji@524: (cmdline $CommandLine) yuuji@524: (begin $TextRegionBeginning) yuuji@524: (end TextRegionEnd))" yuuji@524: (if begend-info yuuji@524: (let ((b (car begend-info)) (e (nth 1 begend-info)) yuuji@524: delim (args (nth 2 begend-info)) yuuji@547: (p (point)) openb closeb outfile source cmdline point-beg point-end yuuji@547: (src-ptn (format "^\\s *%s%s" yuuji@547: (regexp-quote comment-start) yuuji@547: (regexp-quote YaTeX-filter-src)))) yuuji@524: (save-excursion yuuji@524: (and yuuji@524: (string-match "FILTER" args) ;easy test yuuji@524: (goto-char (car begend-info)) yuuji@524: (re-search-forward yuuji@524: "FILTER\\s *{\\([^}]+\\)}" e t) yuuji@524: (setq outfile (YaTeX-match-string 1)) yuuji@524: (goto-char (match-end 0)) yuuji@524: (prog2 ;Step into the second brace yuuji@524: (skip-chars-forward "\t ") yuuji@524: (looking-at "{") ;Check if 2nd brace surely exists yuuji@524: (skip-chars-forward "{") yuuji@524: (skip-chars-forward "\t")) yuuji@524: (setq openb (point)) yuuji@524: (condition-case nil yuuji@524: (progn (up-list 1) t) yuuji@524: (error nil)) yuuji@524: (setq closeb (1- (point)) yuuji@524: cmdline (YaTeX-buffer-substring openb closeb)) yuuji@524: (cond yuuji@524: ((re-search-forward "^\\\\if0\\>" p t) ;; Embedded source yuuji@524: (forward-line 1) yuuji@547: (setq point-beg (if (looking-at YaTeX-filter-block-marker) yuuji@524: (progn (setq delim (YaTeX-match-string 0)) yuuji@524: (forward-line 1) yuuji@524: (point)) yuuji@524: (point))) yuuji@524: (re-search-forward "^\\\\fi\\>" e t) yuuji@524: (goto-char (match-beginning 0)) yuuji@524: (setq point-end (if delim yuuji@524: (progn yuuji@524: (re-search-backward yuuji@524: (concat "^" (regexp-quote delim)) yuuji@524: (1+ point-beg) t) yuuji@524: (match-beginning 0)) yuuji@524: (point)))) yuuji@547: ((re-search-forward yuuji@547: (format "%s{\\(.*\\)}" src-ptn) e t) ; external file yuuji@524: (setq source (YaTeX-match-string 1) yuuji@524: point-beg (match-beginning 0) yuuji@524: point-end (match-end 0))) yuuji@524: (t ;; If source notation not found, yuuji@524: (let ((ovl (overlays-in b e))) ;; clear all remaining overlays yuuji@524: (while ovl yuuji@524: (delete-overlay (car ovl)) yuuji@524: (setq ovl (cdr ovl)))))) ;; Return nil yuuji@524: yuuji@524: ;; Then return all values yuuji@524: (list (cons 'outfile outfile) yuuji@524: (cons 'source source) yuuji@524: (cons 'cmdline cmdline) yuuji@524: (cons 'begin point-beg) yuuji@524: (cons 'end point-end))))))) yuuji@524: yuuji@524: ;;debug;; (YaTeX-filter-parse-filter-region (YaTeX-in-BEGEND-p)) yuuji@518: (defun YaTeX-filter-pass-to-filter (begend-info) yuuji@518: "Pass current BEGIN FILTER environment to external command." yuuji@518: (put 'YaTeX-filter-filter-sentinel 'outfile nil) yuuji@518: ;; begend-info is from YaTeX-in-BEGEND-p: (BEG END ARGS) yuuji@518: (let ((b (car begend-info)) (e (nth 1 begend-info)) yuuji@545: (r (YaTeX-filter-parse-filter-region begend-info)) yuuji@545: insmark) yuuji@518: (save-excursion yuuji@524: (if r (let*((case-fold-search t) yuuji@524: (outfile (cdr (assq 'outfile r))) yuuji@524: (source (cdr (assq 'source r))) yuuji@524: (type (cond yuuji@524: ((string-match "\\.png$" outfile) "png") yuuji@524: ((string-match "\\.svg$" outfile) "svg") yuuji@524: ((string-match "\\.tex$" outfile) "tex") yuuji@524: (t "pdf"))) yuuji@524: (newcmdline (YaTeX-replace-formats yuuji@524: (cdr (assq 'cmdline r)) yuuji@524: (list (cons "t" type) yuuji@524: (cons "o" outfile) yuuji@524: (cons "i" source)))) yuuji@524: (text-start (cdr (assq 'begin r))) yuuji@524: (text-end (cdr (assq 'end r))) yuuji@524: (text (and (numberp text-start) yuuji@524: (numberp text-end) yuuji@524: (YaTeX-buffer-substring text-start text-end))) yuuji@524: ;; yuuji@524: ;; Now it's time to start filter process yuuji@524: ;; yuuji@524: (procbuf (YaTeX-system newcmdline "Filter" 'force)) yuuji@524: (proc (get-buffer-process procbuf)) yuuji@524: ;;(procbuf (get-buffer-create " *Filter*")) yuuji@524: (ovl (progn yuuji@524: (remove-overlays text-start text-end) yuuji@524: (make-overlay text-start text-end))) yuuji@524: (ovlmodhook ;hook function to reset conv-success flag yuuji@524: 'YaTeX-filter-filter-unset-conversion-flag)) yuuji@524: (if proc yuuji@524: (progn yuuji@524: (overlay-put ovl 'filter-output outfile) yuuji@524: (overlay-put ovl 'filter-source source) yuuji@524: (overlay-put ovl 'converted nil) yuuji@524: (overlay-put ovl 'modification-hooks (list ovlmodhook)) yuuji@524: (set-process-coding-system proc 'undecided 'utf-8) yuuji@524: (set-process-sentinel proc 'YaTeX-filter-filter-sentinel) yuuji@524: (YaTeX-showup-buffer procbuf) yuuji@524: (set-buffer procbuf) yuuji@524: (setq buffer-read-only nil) yuuji@524: (erase-buffer) yuuji@524: (insert (format "Starting process `%s'...\n" newcmdline)) yuuji@524: (set-marker (process-mark proc) (point-max)) yuuji@545: (setq insmark (point-max)) yuuji@524: (cond yuuji@524: (text yuuji@545: (process-send-string yuuji@545: proc yuuji@545: (if source yuuji@545: (progn yuuji@545: (insert-file-contents-literally source) yuuji@545: (YaTeX-buffer-substring insmark (point-max))) yuuji@545: text)) yuuji@524: (process-send-string proc "\n") yuuji@524: (process-send-eof proc) ;Notify stream chunk end yuuji@524: (process-send-eof proc))) ;Notify real EOF yuuji@524: (put 'YaTeX-filter-filter-sentinel 'outfile outfile) yuuji@524: (put 'YaTeX-filter-filter-sentinel 'overlay ovl)))))))) yuuji@518: yuuji@518: (defun YaTeX-insert-filter-special (filter list &optional region-p) yuuji@524: (let*((f (YaTeX-read-string-or-skip yuuji@524: "Output file(Maybe *.(pdf|png|jpg|tex)): ")) yuuji@524: (insert-default-directory) yuuji@524: (cmdargs (car list)) yuuji@524: (template-text (car (cdr list))) yuuji@524: (ifile (read-file-name "Data source(Default: in this buffer): " nil)) yuuji@524: (in-line (string= "" ifile))) yuuji@518: (if region-p yuuji@518: (if (< (point) (mark)) (exchange-point-and-mark))) yuuji@524: (save-excursion yuuji@524: (insert (if in-line "===\n\\fi\n" "") yuuji@524: "%#END\n" yuuji@524: (cond yuuji@524: ((string-match "\\.tex$" f) yuuji@524: (format "\\input{%s}\n" (substring f 0 (match-beginning 0)))) yuuji@524: ((string-match "\\.\\(pdf\\|png\\|jpe?g\\|tiff?\\)$" f) yuuji@524: (format "%%# \\includegraphics{%s}\n" f))))) yuuji@518: (and region-p (exchange-point-and-mark)) yuuji@547: (insert (format "%%#BEGIN FILTER{%s}{%s}\n%s%s" yuuji@524: f (or cmdargs "") yuuji@547: (format "%%#%% If you call program in yatex, type `%se'\n" yuuji@547: (key-description yuuji@547: (car (where-is-internal 'YaTeX-typeset-menu)))) yuuji@524: (if in-line "\\if0\n===\n" ""))) yuuji@524: (save-excursion yuuji@545: (insert yuuji@545: (if in-line yuuji@545: (cond (template-text yuuji@545: (concat template-text yuuji@545: (or (string-match "\n$" template-text) "\n"))) yuuji@545: (t "\n")) yuuji@545: (format "%%#SRC{%s}\n" ifile)))))) yuuji@518: yuuji@518: (provide 'yatexflt) yuuji@518: yuuji@518: ; Local variables: yuuji@518: ; fill-prefix: ";;; " yuuji@518: ; paragraph-start: "^$\\| \\|;;;$" yuuji@518: ; paragraph-separate: "^$\\| \\|;;;$" yuuji@518: ; End: