From 067a42f8dd2ce19de3203605ee8c1c08aa192580 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Tue, 19 Nov 2019 11:47:19 +0100 Subject: [PATCH] Allow eww to display exotic images like webp * lisp/image.el (image-type): Allow passing in the image type. (create-image): Make conversion work with data in addition to files. * lisp/image/image-converter.el (image-convert-p): Allow taking working on data in addition to files (bug#38036). (image-convert): Ditto. (image-converter--convert): Extend signature to say whether we're getting a file or data. (image-converter--convert-magick): Convert data. (image-converter--convert): Ditto. --- lisp/image.el | 38 ++++++++---- lisp/image/image-converter.el | 105 ++++++++++++++++++++++++---------- 2 files changed, 102 insertions(+), 41 deletions(-) diff --git a/lisp/image.el b/lisp/image.el index ad2ee6c6071..5f24475ce5b 100644 --- a/lisp/image.el +++ b/lisp/image.el @@ -369,8 +369,10 @@ 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)) +(declare-function image-convert-p "image-converter.el" + (source &optional image-format)) +(declare-function image-convert "image-converter.el" + (image &optional image-format)) ;;;###autoload (defun image-type (source &optional type data-p) @@ -380,12 +382,20 @@ Optional TYPE is a symbol describing the image type. If TYPE is omitted or nil, try to determine the image type from its first few bytes of image data. If that doesn't work, and SOURCE is a file name, use its file extension as image type. -Optional DATA-P non-nil means SOURCE is a string containing image data." + +Optional DATA-P non-nil means SOURCE is a string containing image +data. If DATA-P is a symbol with a name on the format +`image/jpeg', that may be used as a hint to determine the image +type if we can't otherwise guess it." (when (and (not data-p) (not (stringp source))) (error "Invalid image file name `%s'" source)) (unless type (setq type (if data-p - (image-type-from-data source) + (or (image-type-from-data source) + (and image-use-external-converter + (progn + (require 'image-converter) + (image-convert-p source data-p)))) (or (image-type-from-file-header source) (image-type-from-file-name source) (and image-use-external-converter @@ -457,14 +467,18 @@ Images should not be larger than specified by `max-image-size'. Image file names that are not absolute are searched for in the \"images\" sub-directory of `data-directory' and `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)) + (let ((data-format + ;; Pass the image format, if any, if this is data. + (and data-p (or (plist-get props :format) t)))) + ;; It is x_find_image_file in image.c that sets the search path. + (setq type (ignore-error unknown-image-type + (image-type file-or-data type data-format))) + ;; 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 data-format) + 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 index 2e09976c011..dedccadcf46 100644 --- a/lisp/image/image-converter.el +++ b/lisp/image/image-converter.el @@ -48,43 +48,58 @@ installed on the system." (imagemagick :command "convert" :probe ("-list" "format"))) "List of supported image converters to try.") -(defun image-convert-p (file) - "Return `image-convert' if FILE is an image file that can be converted." +(defun image-convert-p (source &optional data-p) + "Return `image-convert' if SOURCE is an image that can be converted. +SOURCE can either be a file name or a string containing image +data. In the latter case, DATA-P should be non-nil. If DATA-P +is a string, it should be a MIME format string like +\"image/gif\"." ;; Find an installed image converter. (unless image-converter (image-converter--find-converter)) (and image-converter - (string-match image-converter-regexp file) + (or (and (not data-p) + (string-match image-converter-regexp source)) + (and data-p + (symbolp data-p) + (string-match "/" (symbol-name data-p)) + (string-match + image-converter-regexp + (concat "foo." (image-converter--mime-type data-p))))) 'image-convert)) -(defun image-convert (image) +(defun image-convert (image &optional image-format) "Convert IMAGE file to the PNG format. 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)." +a string with the image data. + +If IMAGE-FORMAT is non-nil, IMAGE is a string containing the +image data, and IMAGE-FORMAT is a symbol with a MIME format name +like \"image/webp\". + +IMAGE can also be an image object as returned by `create-image'." ;; Find an installed image converter. (unless image-converter (image-converter--find-converter)) (unless image-converter (error "No external image converters available")) - (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)))) + image) + (if (listp image) + (plist-get (cdr image) :data-p) + image-format)))) (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. + ;; but ignore the :type value. (apply #'create-image (buffer-string) 'png t (cl-loop for (key val) on (cdr image) by #'cddr - unless (memq key '(:type :file)) + unless (eq key :type) append (list key val))) (buffer-string)))) @@ -159,33 +174,65 @@ where created with DATA-P nil (i.e., it has to refer to a file)." image-converter-regexp (concat "\\." (regexp-opt formats) "\\'")) (throw 'done image-converter))))) -(cl-defmethod image-converter--convert ((type (eql graphicsmagick)) file) +(cl-defmethod image-converter--convert ((type (eql graphicsmagick)) source + image-format) "Convert using GraphicsMagick." - (image-converter--convert-magick type file)) + (image-converter--convert-magick type source image-format)) -(cl-defmethod image-converter--convert ((type (eql imagemagick)) file) +(cl-defmethod image-converter--convert ((type (eql imagemagick)) source + image-format) "Convert using ImageMagick." - (image-converter--convert-magick type file)) + (image-converter--convert-magick type source image-format)) + +(defun image-converter--mime-type (image-format) + (and (symbolp image-format) + (cadr (split-string (symbol-name image-format) "/")))) -(defun image-converter--convert-magick (type file) +(defun image-converter--convert-magick (type source image-format) (let ((command (image-converter--value type :command))) - (unless (zerop (apply #'call-process (car command) - nil t nil - (append (cdr command) - (list (expand-file-name file) "png:-")))) + (unless (zerop (if image-format + ;; We have the image data in SOURCE. + (progn + (insert source) + (apply #'call-process-region (point-min) (point-max) + (car command) t t nil + (append + (cdr command) + (list (format "%s:-" + (image-converter--mime-type + image-format)) + "png:-")))) + ;; SOURCE is a file name. + (apply #'call-process (car command) + nil t nil + (append (cdr command) + (list (expand-file-name source) "png:-"))))) ;; If the command failed, hopefully the buffer contains the ;; error message. (buffer-string)))) -(cl-defmethod image-converter--convert ((type (eql ffmpeg)) file) +(cl-defmethod image-converter--convert ((type (eql ffmpeg)) source + image-format) "Convert using ffmpeg." (let ((command (image-converter--value type :command))) - (unless (zerop (apply #'call-process - (car command) - nil '(t nil) nil - (append (cdr command) - (list "-i" (expand-file-name file) - "-c:v" "png" "-f" "image2pipe" "-")))) + (unless (zerop (if image-format + (progn + (insert source) + (apply #'call-process-region + (point-min) (point-max) (car command) + t '(t nil) nil + (append + (cdr command) + (list "-i" "-" + "-c:v" "png" + "-f" "image2pipe" "-")))) + (apply #'call-process + (car command) + nil '(t nil) nil + (append (cdr command) + (list "-i" (expand-file-name source) + "-c:v" "png" "-f" "image2pipe" + "-"))))) "ffmpeg error when converting"))) (provide 'image-converter) -- 2.39.2