emacs-framebuffer

Emacs library to show images and documents in console using Linux framebuffer
Log | Files | Refs

commit 62e22ab9f310c2142327fc57dc9cf168ccc5d4da
parent 1d56a18e566afa42af47adfe32ad224533343b30
Author: Tomas Hlavaty <tom@logand.com>
Date:   Sun, 21 Jun 2020 23:39:10 +0200

support pdf

Diffstat:
Memacs-framebuffer.el | 253++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 205 insertions(+), 48 deletions(-)

diff --git a/emacs-framebuffer.el b/emacs-framebuffer.el @@ -48,8 +48,8 @@ :type 'string :group 'framebuffer) -(defcustom framebuffer-image-mode-refresh-delay "2 sec" - "Specify the delay after which to refresh the image on the framebuffer." +(defcustom framebuffer-cache-directory nil + "Specify the diretory where to store cache files." :type 'string :group 'framebuffer) @@ -59,6 +59,7 @@ (let ((ext (file-name-extension file))) (cond ((equal ext "pbm") 'pbm) + ((equal ext "pdf") 'pdf) ((equal ext "pgm") 'pgm) ((equal ext "ppm") 'ppm) ))) @@ -89,13 +90,43 @@ (cons (read (match-string 1)) (read (match-string 2)))) (cons framebuffer-default-width framebuffer-default-height)))))) -(defun framebuffer-draw-now (x y w h file) +(defun framebuffer-file-hash (file) (with-temp-buffer - (insert (format "0;1;%d;%d;%d;%d;;;;;%s\n" x y w h (expand-file-name file))) + (call-process "sha256sum" nil t nil (expand-file-name file)) + (buffer-substring (point-min) (+ (point-min) 64)))) + +(defun framebuffer-cache-to-pdf (format file) + (framebuffer-cache-abiword + format + file + (concat (or framebuffer-cache-directory (temporary-file-directory)) + "/" + (framebuffer-file-hash file) ".pdf"))) + +(defun framebuffer-cache-pdf-to-png (file page) + (let* ((page (format "%s" (or page 1))) + (output (concat (or framebuffer-cache-directory (temporary-file-directory)) + "/" + (framebuffer-file-hash file) "-" page)) + (ofile (concat output ".png"))) + (unless (file-readable-p ofile) + (call-process "pdftocairo" nil nil nil "-singlefile" "-f" page "-l" page "-png" (expand-file-name file) output)) + ofile)) + +(defun framebuffer-draw-now (x1 y1 w1 h1 x2 y2 w2 h2 file) + (case (framebuffer-file-format file) + (pdf + (setq file (framebuffer-cache-pdf-to-png + file + framebuffer-image-mode-current-page)))) + (with-temp-buffer + (insert (format "0;1;%d;%d;%d;%d;%s;%s;%s;%s;%s\n" x1 y1 w1 h1 + (or x2 "") (or y2 "") (or w2 "") (or h2 "") + (expand-file-name file))) (call-process-region (point-min) (point-max) "w3mimgdisplay"))) (defun framebuffer-draw (x y w h file) - (run-at-time framebuffer-draw-delay nil 'framebuffer-draw-now x y w h file)) + (framebuffer-draw-now x y w h nil nil nil nil file)) (defun framebuffer-buffer-brook () (lambda () @@ -127,6 +158,13 @@ (+ (framebuffer-next-u16le brook) (* 65536 (framebuffer-next-u16le brook)))) +(defun framebuffer-pdf-page-size (file) + (with-temp-buffer + (call-process "pdfinfo" nil t nil (expand-file-name file)) + (goto-char (point-min)) + (when (search-forward-regexp "^Page size:[ ]*\\([0-9]+[.]?[0-9]*\\) x \\([0-9]+[.]?[0-9]*\\) pts") + (cons (read (match-string 1)) (read (match-string 2)))))) + (defun framebuffer-image-size (file) (with-temp-buffer (set-buffer-multibyte nil) @@ -134,6 +172,16 @@ (let* ((brook (framebuffer-buffer-brook)) (a (framebuffer-next-u8 brook))) (case a + (?% ;; pdf + (case (framebuffer-next-u8 brook) + (?P + (case (framebuffer-next-u8 brook) + (?D + (case (framebuffer-next-u8 brook) + (?F + (case (framebuffer-next-u8 brook) + (?- + (framebuffer-pdf-page-size file)))))))))) (137 ;; png (when (and (= ?P (framebuffer-next-u8 brook)) (= ?N (framebuffer-next-u8 brook)) @@ -234,7 +282,7 @@ (and f (not (file-directory-p f)) (string-match - "jpe?g\\|JPE?G\\|png\\|PNG\\|bmp\\|BMP\\|gif\\|GIF\\|tiff?\\|TIFF?\\|ppm\\|PPM\\|pnm\\|PNM\\|xpm\\|XPM" + "jpe?g\\|JPE?G\\|png\\|PNG\\|bmp\\|BMP\\|gif\\|GIF\\|tiff?\\|TIFF?\\|ppm\\|PPM\\|pnm\\|PNM\\|xpm\\|XPM\\|pdf\\|PDF" (or (file-name-extension f) ""))))) (defun framebuffer-image-file-dired-next (arg) @@ -249,80 +297,189 @@ (interactive "^p") (framebuffer-image-file-dired-next (if arg (- arg) -1))) -(defvar framebuffer-image-mode-image-size) - -(defun framebuffer-image-mode-draw-image (buffer) +(make-variable-buffer-local + (defvar framebuffer-image-mode-image-size)) +(make-variable-buffer-local + (defvar framebuffer-image-mode-npages)) +(make-variable-buffer-local + (defvar framebuffer-image-mode-current-page)) +(make-variable-buffer-local + (defvar framebuffer-image-mode-scale)) +(make-variable-buffer-local + (defvar framebuffer-image-mode-scroll)) + +(defun framebuffer-image-mode-draw-image (&optional buffer) (interactive) - (let ((file (buffer-file-name))) + (let ((buffer (or buffer (current-buffer))) + (file (buffer-file-name))) (destructuring-bind (w &rest h) framebuffer-image-mode-image-size (destructuring-bind (fbw &rest fbh) (framebuffer-size) - (let ((window (get-buffer-window buffer 'visible))) ;; TODO for all visible windows - (when window - (destructuring-bind (x1 y1 x2 y2) (window-edges window t) - (let* ((fw (frame-width)) - (fh (frame-height)) - (cw (floor fbw fw)) - (ch (floor fbh fh)) - (wx (* x1 cw)) - (wy (* y1 ch)) - (ww (* (- x2 x1) cw)) - (wh (* (- y2 y1) ch)) - (scale (min (/ ww 1.0 w) (/ wh 1.0 h))) - (zw (floor (* scale w))) - (zh (floor (* scale h))) - (zx (+ wx (floor (- ww zw) 2))) - (zy (+ wy (floor (- wh zh) 2)))) - (framebuffer-draw zx zy zw zh file))))))))) + (dolist (window (get-buffer-window-list buffer nil 'visible)) + (destructuring-bind (x1 y1 x2 y2) (window-edges window t) + (let* ((fw (frame-width)) + (fh (frame-height)) + (cw (floor fbw fw)) + (ch (floor fbh fh)) + (wx (* x1 cw)) + (wy (* y1 ch)) + (ww (* (- x2 x1) cw)) + (wh (* (- y2 y1) ch)) + (scale (ecase framebuffer-image-mode-scale + (:fit-page (min (/ ww 1.0 w) (/ wh 1.0 h))) + (:fit-width (/ ww 1.0 w)) + (:fit-height (/ wh 1.0 h)))) + (zw (floor (* scale w))) + (zh (floor (* scale h))) + (zx (+ wx (floor (- ww zw) 2))) + (zy (+ wy (floor (- wh zh) 2)))) + (when (minusp zx) + (setq zx 0)) + (when (minusp zy) + (setq zy 0)) + (framebuffer-draw-now zx zy zw zh wx wy ww wh file)))))))) (defun framebuffer-image-mode-draw-image-repeatedly (buffer) (when (buffer-live-p buffer) (with-current-buffer buffer - (framebuffer-image-mode-draw-image buffer) - (run-at-time framebuffer-image-mode-refresh-delay - nil - 'framebuffer-image-mode-draw-image-repeatedly - buffer)))) + (framebuffer-image-mode-draw-image buffer)))) -(defun framebuffer-image-mode-kill-buffer () - (interactive) - (kill-buffer)) +(defun framebuffer-pdf-npages (file) + (with-temp-buffer + (call-process "pdfinfo" nil t nil (expand-file-name file)) + (goto-char (point-min)) + (when (search-forward-regexp "^Pages:[ ]*\\([0-9]+\\)$" nil t) + (let ((z (read (match-string 1)))) + (when (plusp z) + z))))) + +(defun framebuffer-image-npages (file) + (let ((format (framebuffer-file-format file))) + (case format + (pdf + (framebuffer-pdf-npages file)) + (t 1)))) (defvar framebuffer-image-mode-hook nil) (define-derived-mode framebuffer-image-mode fundamental-mode "fbi" "Major mode for viewing images in framebuffer." - (set (make-local-variable 'framebuffer-image-mode-image-size) - (framebuffer-image-size (buffer-file-name))) + (setq framebuffer-image-mode-image-size + (framebuffer-image-size (buffer-file-name))) + (setq framebuffer-image-mode-npages + (framebuffer-image-npages (buffer-file-name))) + (setq framebuffer-image-mode-current-page 1) + (setq framebuffer-image-mode-scale :fit-page) + (setq framebuffer-image-mode-scroll 0) (with-silent-modifications (erase-buffer) (insert "file: ") (insert (buffer-file-name)) - (insert "\n\n") - (insert "width: ") + (insert "\npages: ") + (insert (format "%s" framebuffer-image-mode-npages)) + (insert "\nwidth: ") (insert (format "%s" (car framebuffer-image-mode-image-size))) (insert "\nheight: ") (insert (format "%s" (cdr framebuffer-image-mode-image-size))) - (insert "\n\npress:\n") - (insert "- i: to draw the image\n") - (insert "- q: to kill the buffer\n")) + (insert "\npress:\n") + (insert "- b: beginning (first page)\n") + (insert "- d: (re)draw\n") + (insert "- e: end (last page)\n") + (insert "- f: fit page\n") + (insert "- g: go to page\n") + (insert "- h: fit height\n") + (insert "- k: kill the buffer\n") + (insert "- n: next page\n") + (insert "- p: previous page\n") + (insert "- q: quit\n") + (insert "- space: scroll down\n") + (insert "- u: scroll up\n") + (insert "- w: fit width\n") + ) (setq buffer-read-only t) (goto-char (point-min)) - (framebuffer-image-mode-draw-image-repeatedly (current-buffer)) - (run-hooks 'framebuffer-image-mode-hook)) + (run-hooks 'framebuffer-image-mode-hook) + (run-at-time framebuffer-draw-delay + nil + 'framebuffer-image-mode-draw-image-repeatedly + (current-buffer))) + +(defun framebuffer-image-mode-scroll-down () + (interactive) + ) + +(defun framebuffer-image-mode-scroll-up () + (interactive) + ) + +(defun framebuffer-image-mode-goto-page (&optional n) + (interactive "nPage: ") + (unless (plusp n) + (setq n 1)) + (unless (< n framebuffer-image-mode-npages) + (setq n framebuffer-image-mode-npages)) + (setq framebuffer-image-mode-current-page n) + (force-mode-line-update) + (framebuffer-image-mode-draw-image (current-buffer))) + +(defun framebuffer-image-mode-next-page () + (interactive) + (framebuffer-image-mode-goto-page (1+ framebuffer-image-mode-current-page))) + +(defun framebuffer-image-mode-previous-page () + (interactive) + (framebuffer-image-mode-goto-page (1- framebuffer-image-mode-current-page))) + +(defun framebuffer-image-mode-first-page () + (interactive) + (framebuffer-image-mode-goto-page 1)) + +(defun framebuffer-image-mode-last-page () + (interactive) + (framebuffer-image-mode-goto-page framebuffer-image-mode-npages)) + +(defun framebuffer-image-mode-change-scale (scale) + (setq framebuffer-image-mode-scale scale) + (framebuffer-image-mode-draw-image (current-buffer))) + +(defun framebuffer-image-mode-fit-page () + (interactive) + (framebuffer-image-mode-change-scale :fit-page)) + +(defun framebuffer-image-mode-fit-width () + (interactive) + (framebuffer-image-mode-change-scale :fit-width)) + +(defun framebuffer-image-mode-fit-height () + (interactive) + (framebuffer-image-mode-change-scale :fit-height)) (add-hook 'framebuffer-image-mode-hook (lambda () - (define-key framebuffer-image-mode-map "q" 'framebuffer-image-mode-kill-buffer))) + (define-key framebuffer-image-mode-map "b" 'framebuffer-image-mode-first-page) + (define-key framebuffer-image-mode-map "d" 'framebuffer-image-mode-draw-image) + (define-key framebuffer-image-mode-map "e" 'framebuffer-image-mode-last-page) + (define-key framebuffer-image-mode-map "f" 'framebuffer-image-mode-fit-page) + (define-key framebuffer-image-mode-map "g" 'framebuffer-image-mode-goto-page) + (define-key framebuffer-image-mode-map "h" 'framebuffer-image-mode-fit-height) + (define-key framebuffer-image-mode-map "k" 'kill-buffer) + (define-key framebuffer-image-mode-map "n" 'framebuffer-image-mode-next-page) + (define-key framebuffer-image-mode-map "p" 'framebuffer-image-mode-previous-page) + (define-key framebuffer-image-mode-map "q" 'quit-window) + (define-key framebuffer-image-mode-map "spc" 'framebuffer-image-mode-scroll-down) + (define-key framebuffer-image-mode-map "u" 'framebuffer-image-mode-scroll-up) + (define-key framebuffer-image-mode-map "w" 'framebuffer-image-mode-fit-width) + )) (defun framebuffer-install () (interactive) - (add-to-list 'auto-mode-alist '("\\.png\\'" . framebuffer-image-mode)) - (add-to-list 'auto-mode-alist '("\\.jpe?g\\'" . framebuffer-image-mode)) (add-to-list 'auto-mode-alist '("\\.bmp\\'" . framebuffer-image-mode)) (add-to-list 'auto-mode-alist '("\\.gif\\'" . framebuffer-image-mode)) - (add-to-list 'auto-mode-alist '("\\.ppm\\'" . framebuffer-image-mode)) + (add-to-list 'auto-mode-alist '("\\.jpe?g\\'" . framebuffer-image-mode)) + (add-to-list 'auto-mode-alist '("\\.pdf\\'" . framebuffer-image-mode)) + (add-to-list 'auto-mode-alist '("\\.png\\'" . framebuffer-image-mode)) (add-to-list 'auto-mode-alist '("\\.pnm\\'" . framebuffer-image-mode)) + (add-to-list 'auto-mode-alist '("\\.ppm\\'" . framebuffer-image-mode)) (add-to-list 'auto-mode-alist '("\\.tiff?\\'" . framebuffer-image-mode)) (add-to-list 'auto-mode-alist '("\\.xpm\\'" . framebuffer-image-mode)) (with-eval-after-load 'dired