emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master 63f4f02: If requested, use external image converter


From: Lars Ingebrigtsen
Subject: [Emacs-diffs] master 63f4f02: If requested, use external image converters for exotic formats
Date: Sat, 28 Sep 2019 19:26:19 -0400 (EDT)

branch: master
commit 63f4f02aa78e6b48f7ee346d31a813ca0978651e
Author: Lars Ingebrigtsen <address@hidden>
Commit: Lars Ingebrigtsen <address@hidden>

    If requested, use external image converters for exotic formats
    
    * doc/lispref/display.texi (Defining Images): Document it.
    
    * lisp/image.el (convert-images-externally): New variable.
    (image-type): Use it.
    (create-image): Convert images.
    
    * lisp/image/image-converter.el (image-converter--convert): New file.
---
 doc/lispref/display.texi      |   7 ++
 etc/NEWS                      |   7 ++
 lisp/image.el                 |  34 +++++++-
 lisp/image/image-converter.el | 191 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 235 insertions(+), 4 deletions(-)

diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index fd68208..ec288b1 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -6044,6 +6044,13 @@ properties---for example,
 (create-image "foo.xpm" 'xpm nil :heuristic-mask t)
 @end example
 
+@vindex convert-images-externally
+If Emacs doesn't have native support for the image format in question,
+and @code{convert-images-externally} is non-@code{nil}, Emacs will try
+to determine whether there are external utilities that can be used to
+transform the image in question to @acronym{PNG} before displaying.
+GraphicsMagick, ImageMagick and ffmpeg are currently supported.
+
 The function returns @code{nil} if images of this type are not
 supported.  Otherwise it returns an image descriptor.
 @end defun
diff --git a/etc/NEWS b/etc/NEWS
index 34b7a5a..677b710 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2656,6 +2656,13 @@ data about creation times and orientation and the like.
 'exif-parse-file' and 'exif-parse-buffer' are the main interface
 functions.
 
+*** New library image-converter.
+To view exotic image formats that Emacs doesn't have native support
+for, the new 'convert-images-externally' variable can be set to t.  If
+the system has GraphicsMagick, ImageMagick or ffmpeg installed, it
+will be used to convert images automatically before displaying with
+'create-image'.
+
 *** 'image-mode' now uses this library to automatically rotate images
 according to the orientation in the Exif data, if any.
 
diff --git a/lisp/image.el b/lisp/image.el
index b36a513..00b4c48 100644
--- a/lisp/image.el
+++ b/lisp/image.el
@@ -141,6 +141,17 @@ based on the font pixel size."
                  (const :tag "Automatically compute" auto))
   :version "26.1")
 
+(defcustom convert-images-externally nil
+  "If non-nil, `create-image' will use external converters for exotic formats.
+Emacs handles most of the common image formats (JPEG, PNG, GIF
+and so on) internally, but images that doesn't have native
+support in Emacs can still be displayed by Emacs if external
+conversion programs (like ImageMagick \"convert\", GraphicsMagick
+\"gm\" or \"ffmpeg\") are installed."
+  :group 'image
+  :type 'bool
+  :version "27.1")
+
 ;; Map put into text properties on images.
 (defvar image-map
   (let ((map (make-sparse-keymap)))
@@ -357,6 +368,9 @@ be determined."
            ;; If nothing seems to be supported, return first type that matched.
            (or first (setq first type))))))))
 
+(declare-function image-convert-p "image-converter.el" (file))
+(declare-function image-convert "image-converter.el" (image))
+
 ;;;###autoload
 (defun image-type (source &optional type data-p)
   "Determine and return image type.
@@ -372,10 +386,16 @@ Optional DATA-P non-nil means SOURCE is a string 
containing image data."
     (setq type (if data-p
                   (image-type-from-data source)
                 (or (image-type-from-file-header source)
-                    (image-type-from-file-name source))))
-    (or type (error "Cannot determine image type")))
-  (or (memq type (and (boundp 'image-types) image-types))
-      (error "Invalid image type `%s'" type))
+                    (image-type-from-file-name source)
+                     (and convert-images-externally
+                          (progn
+                            (require 'image-converter)
+                            (image-convert-p source))))))
+    (unless type
+      (error "Cannot determine image type")))
+  (when (and (not (eq type 'image-convert))
+             (not (memq type (and (boundp 'image-types) image-types))))
+    (error "Invalid image type `%s'" type))
   type)
 
 
@@ -438,6 +458,12 @@ Image file names that are not absolute are searched for in 
the
 `x-bitmap-file-path' (in that order)."
   ;; It is x_find_image_file in image.c that sets the search path.
   (setq type (image-type file-or-data type data-p))
+  ;; If we have external image conversion switched on (for exotic,
+  ;; non-native image formats), then we convert the file.
+  (when (eq type 'image-convert)
+    (setq file-or-data (image-convert file-or-data)
+          type 'png
+          data-p t))
   (when (image-type-available-p type)
     (append (list 'image :type type (if data-p :data :file) file-or-data)
             (and (not (plist-get props :scale))
diff --git a/lisp/image/image-converter.el b/lisp/image/image-converter.el
new file mode 100644
index 0000000..f485c34
--- /dev/null
+++ b/lisp/image/image-converter.el
@@ -0,0 +1,191 @@
+;;; image-converter.el --- Converting images from exotic formats -*- 
lexical-binding: t -*-
+
+;; Copyright (C) 2019 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <address@hidden>
+;; Keywords: images
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs 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 GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; The main interface function here is `image-convert'.
+
+;;; Code:
+
+(require 'cl-generic)
+(eval-when-compile (require 'cl-lib))
+
+(defcustom convert-external-images nil
+  "If non-nil, `create-image' will use external converters for exotic formats.
+Emacs handles most of the common image formats (JPEG, PNG, GIF
+and so on) internally, but images that doesn't have native
+support in Emacs can still be displayed by Emacs if external
+conversion programs (like ImageMagick \"convert\", GraphicsMagick
+\"gm\" or \"ffmpeg\") are installed."
+  :group 'image
+  :type 'bool
+  :version "27.1")
+
+(defcustom image-converter nil
+  "What external converter to use.
+`imagemagick', `graphicsmagick' and `ffmpeg' are supported."
+  :group 'image
+  :type 'symbol
+  :version "27.1")
+
+(defvar image-converter-regexp nil
+  "A regexp that matches the file name suffixes that can be converted.")
+
+(defvar image-converter--converters
+  '((graphicsmagick :command "gm convert" :probe "-list format")
+    (imagemagick :command "convert" :probe "-list format")
+    (ffmpeg :command "ffmpeg" :probe "-decoders"))
+  "List of supported image converters to try.")
+
+(defun image-convert-p (file)
+  "Return `image-convert' if FILE can be converted."
+  ;; Find an installed image converter.
+  (unless image-converter
+    (image-converter--find-converter))
+  (and image-converter
+       (string-match image-converter-regexp file)
+       'image-convert))
+
+(defun image-convert (image)
+  "Convert IMAGE to a format Emacs can display.
+IMAGE can either be a file name, which will make the return value
+a string with the image data.  It can also be an image object as
+returned by `create-image'.  If so, it has to be an image object
+where created with DATA-P nil (i.e., it has to refer to a file)."
+  ;; Find an installed image converter.
+  (unless image-converter
+    (image-converter--find-converter))
+  (unless image-converter
+    (error "No external image converters installed"))
+  (when (and (listp image)
+             (not (plist-get (cdr image) :file)))
+    (error "Only images that refer to files can be converted"))
+  (with-temp-buffer
+    (set-buffer-multibyte nil)
+    (when-let ((err (image-converter--convert
+                     image-converter
+                     (if (listp image)
+                         (plist-get (cdr image) :file)
+                       image))))
+      (error "%s" err))
+    (if (listp image)
+        ;; Return an image object that's the same as we were passed,
+        ;; but ignore the :type and :file values.
+        (apply #'create-image (buffer-string) 'png t
+               (cl-loop for (key val) on (cdr image) by #'cddr
+                        unless (memq key '(:type :file))
+                        append (list key val)))
+      (buffer-string))))
+
+(defun image-converter--value (type elem)
+  "Return the value of ELEM of image converter TYPE."
+  (plist-get (cdr (assq type image-converter--converters)) elem))
+
+(cl-defmethod image-converter--probe ((type (eql graphicsmagick)))
+  "Check whether the system has GraphicsMagick installed."
+  (with-temp-buffer
+    (let ((command (split-string (image-converter--value type :command) " "))
+          formats)
+      (when (zerop (apply #'call-process (car command) nil '(t nil) nil
+                          (append (cdr command)
+                                  (split-string
+                                   (image-converter--value type :probe) " "))))
+        (goto-char (point-min))
+        (when (re-search-forward "^-" nil t)
+          (forward-line 1)
+          ;; Lines look like
+          ;; "   8BIM P  rw-  Photoshop resource format".
+          (while (re-search-forward "^ *\\([A-Z0-9]+\\) +. +r" nil t)
+            (push (downcase (match-string 1)) formats)))
+        (nreverse formats)))))
+
+(cl-defmethod image-converter--probe ((type (eql imagemagick)))
+  "Check whether the system has ImageMagick installed."
+  (with-temp-buffer
+    (let ((command (split-string (image-converter--value type :command) " "))
+          formats)
+      ;; Can't check return value; ImageMagick convert usually returns
+      ;; a non-zero result on "-list format".
+      (apply #'call-process (car command) nil '(t nil) nil
+             (append (cdr command)
+                     (split-string (image-converter--value type :probe) " ")))
+      (goto-char (point-min))
+      (when (re-search-forward "^-" nil t)
+        (forward-line 1)
+        ;; Lines look like
+        ;; "      WPG* r--   Word Perfect Graphics".
+        (while (re-search-forward "^ *\\([A-Z0-9]+\\)\\*? +r" nil t)
+          (push (downcase (match-string 1)) formats)))
+      (nreverse formats))))
+
+(cl-defmethod image-converter--probe ((type (eql ffmpeg)))
+  "Check whether the system has ffmpeg installed."
+  (with-temp-buffer
+    (let ((command (image-converter--value type :command))
+          formats)
+      (when (zerop (call-process command nil '(t nil) nil
+                                 (image-converter--value type :probe)))
+        (goto-char (point-min))
+        (when (re-search-forward "^ *-" nil t)
+          (forward-line 1)
+          ;; Lines look like
+          ;; " V....D alias_pix            Alias/Wavefront PIX image"
+          (while (re-search-forward "^ *V[^ ]+ +\\([a-z0-9_]+\\)" nil t)
+            (push (match-string 1) formats)))
+        (nreverse formats)))))
+
+(defun image-converter--find-converter ()
+  "Find an installed image converter."
+  (dolist (elem image-converter--converters)
+    (when-let ((formats (image-converter--probe (car elem))))
+      (setq image-converter (car elem)
+            image-converter-regexp (concat "\\." (regexp-opt formats) 
"\\'")))))
+
+(cl-defmethod image-converter--convert ((type (eql graphicsmagick)) file)
+  "Convert using GraphicsMagick."
+  (image-converter--convert-magick type file))
+
+(cl-defmethod image-converter--convert ((type (eql imagemagick)) file)
+  "Convert using ImageMagick."
+  (image-converter--convert-magick type file))
+
+(defun image-converter--convert-magick (type file)
+  (let ((command (split-string (image-converter--value type :command) " ")))
+    (unless (zerop (apply #'call-process (car command)
+                          nil t nil
+                          (append (cdr command)
+                                  (list (expand-file-name file) "png:-"))))
+      ;; If the command failed, hopefully the buffer contains the
+      ;; error message.
+      (buffer-string))))
+
+(cl-defmethod image-converter--convert ((type (eql ffmpeg)) file)
+  "Convert using ffmpeg."
+  (unless (zerop (call-process (image-converter--value type :command)
+                               nil '(t nil) nil
+                               "-i" (expand-file-name file)
+                               "-c:v" "png" "-f" "image2pipe" "-"))
+    "ffmpeg error when converting"))
+
+(provide 'image-converter)
+
+;;; image-converter.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]