commit 62e22ab9f310c2142327fc57dc9cf168ccc5d4da
parent 1d56a18e566afa42af47adfe32ad224533343b30
Author: Tomas Hlavaty <tom@logand.com>
Date:   Sun, 21 Jun 2020 23:39:10 +0200
support pdf
Diffstat:
| M | emacs-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