summaryrefslogtreecommitdiff
path: root/lisp/image
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/image')
-rw-r--r--lisp/image/exif.el36
-rw-r--r--lisp/image/gravatar.el6
-rw-r--r--lisp/image/image-converter.el92
3 files changed, 98 insertions, 36 deletions
diff --git a/lisp/image/exif.el b/lisp/image/exif.el
index 23f11bd87cc..b25968af536 100644
--- a/lisp/image/exif.el
+++ b/lisp/image/exif.el
@@ -58,6 +58,9 @@
;; (:tag 306 :tag-name date-time :format 2 :format-type ascii
;; :value "2019:09:21 16:22:13")
;; ...)
+;;
+;; (exif-field 'date-time (exif-parse-file "test.jpg")) =>
+;; "2022:09:14 18:46:19"
;;; Code:
@@ -65,6 +68,7 @@
(defvar exif-tag-alist
'((11 processing-software)
+ (270 description)
(271 make)
(272 model)
(274 orientation)
@@ -73,7 +77,8 @@
(296 resolution-unit)
(305 software)
(306 date-time)
- (315 artist))
+ (315 artist)
+ (33432 copyright))
"Alist of tag values and their names.")
(defconst exif--orientation
@@ -95,7 +100,10 @@ mirrored or not.")
"Parse FILE (a JPEG file) and return the Exif data, if any.
The return value is a list of Exif items.
-If the data is invalid, an `exif-error' is signaled."
+If the data is invalid, an `exif-error' is signaled.
+
+Also see the `exif-field' convenience function to extract data
+from the return value of this function."
(with-temp-buffer
(set-buffer-multibyte nil)
(insert-file-contents-literally file)
@@ -105,7 +113,10 @@ If the data is invalid, an `exif-error' is signaled."
"Parse BUFFER (which should be a JPEG file) and return the Exif data, if any.
The return value is a list of Exif items.
-If the data is invalid, an `exif-error' is signaled."
+If the data is invalid, an `exif-error' is signaled.
+
+Also see the `exif-field' convenience function to extract data
+from the return value of this function."
(setq buffer (or buffer (current-buffer)))
(with-current-buffer buffer
(if enable-multibyte-characters
@@ -122,13 +133,20 @@ If the data is invalid, an `exif-error' is signaled."
(when-let ((app1 (cdr (assq #xffe1 (exif--parse-jpeg)))))
(exif--parse-exif-chunk app1))))))
+(defun exif-field (field data)
+ "Return raw FIELD from EXIF.
+If FIELD is not present in the data, return nil.
+FIELD is a symbol in the cdr of `exif-tag-alist'.
+DATA is the result of calling `exif-parse-file'."
+ (plist-get (seq-find (lambda (e)
+ (eq field (plist-get e :tag-name)))
+ data)
+ :value))
+
(defun exif-orientation (exif)
"Return the orientation (in degrees) in EXIF.
If the orientation isn't present in the data, return nil."
- (let ((code (plist-get (cl-find 'orientation exif
- :key (lambda (e)
- (plist-get e :tag-name)))
- :value)))
+ (let ((code (exif-field 'orientation exif)))
(cadr (assq code exif--orientation))))
(defun exif--parse-jpeg ()
@@ -243,9 +261,9 @@ VALUE is an integer representing BYTES characters."
(set-buffer-multibyte nil)
(if le
(dotimes (i bytes)
- (insert (logand (lsh value (* i -8)) 255)))
+ (insert (logand (ash value (* i -8)) 255)))
(dotimes (i bytes)
- (insert (logand (lsh value (* (- (1- bytes) i) -8)) 255))))
+ (insert (logand (ash value (* (- (1- bytes) i) -8)) 255))))
(insert 0)
(buffer-string)))
diff --git a/lisp/image/gravatar.el b/lisp/image/gravatar.el
index 8ef8bd8eeed..8c49c1edf28 100644
--- a/lisp/image/gravatar.el
+++ b/lisp/image/gravatar.el
@@ -45,7 +45,7 @@
"Time to live in seconds for gravatar cache entries.
If a requested gravatar has been cached for longer than this, it
is retrieved anew. The default value is 30 days."
- :type 'integer
+ :type 'natnum
;; Restricted :type to number of seconds.
:version "27.1"
:group 'gravatar)
@@ -277,7 +277,7 @@ where GRAVATAR is either an image descriptor, or the symbol
;; Store the image in the cache.
(when image
(setf (gethash mail-address gravatar--cache)
- (cons (time-convert (current-time) 'integer)
+ (cons (time-convert nil 'integer)
image)))
(prog1
(apply callback (if data image 'error) cbargs)
@@ -286,7 +286,7 @@ where GRAVATAR is either an image descriptor, or the symbol
(defun gravatar--prune-cache ()
(let ((expired nil)
- (time (- (time-convert (current-time) 'integer)
+ (time (- (time-convert nil 'integer)
;; Twelve hours.
(* 12 60 60))))
(maphash (lambda (key val)
diff --git a/lisp/image/image-converter.el b/lisp/image/image-converter.el
index d3d560f0219..9c2f24819a3 100644
--- a/lisp/image/image-converter.el
+++ b/lisp/image/image-converter.el
@@ -46,6 +46,16 @@ formats that are to be supported: Only the suffixes that map to
:type 'symbol
:version "27.1")
+(defcustom image-convert-to-format "png"
+ "The image format to convert to.
+This should be a string like \"png\" or \"ppm\" or some
+other (preferably lossless) format that Emacs understands
+natively. The converter chosen has to support the format, and if
+not, conversion will fail."
+ :group 'image
+ :version "29.1"
+ :type 'string)
+
(defvar image-converter-regexp nil
"A regexp that matches the file name suffixes that can be converted.")
@@ -58,15 +68,21 @@ formats that are to be supported: Only the suffixes that map to
(imagemagick :command "convert" :probe ("-list" "format")))
"List of supported image converters to try.")
+(defvar image-converter--extra-converters (make-hash-table :test #'equal))
+
+(defun image-converter-initialize ()
+ "Determine the external image converter to be used.
+This also determines which external formats we can parse."
+ (unless image-converter
+ (image-converter--find-converter)))
+
(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))
+ (image-converter-initialize)
;; When image-converter was customized
(when (and image-converter (not image-converter-regexp))
(when-let ((formats (image-converter--probe image-converter)))
@@ -85,22 +101,23 @@ is a string, it should be a MIME format string like
'image-convert))
(defun image-convert (image &optional image-format)
- "Convert IMAGE file to the PNG format.
+ "Convert IMAGE file to an image format Emacs understands.
+This will usually be \"png\", but this is controlled by the
+`image-convert-to-format' user option.
+
IMAGE can either be a file name or image data.
To pass in image data, IMAGE should a string containing the image
data, and IMAGE-FORMAT should be a symbol with a MIME format name
like \"image/webp\". For instance:
- (image-convert data-string 'image/bmp)
+ (image-convert data-string \\='image/bmp)
IMAGE can also be an image object as returned by `create-image'.
-This function converts the image to PNG, and the converted image
-data is returned as a string."
- ;; Find an installed image converter.
- (unless image-converter
- (image-converter--find-converter))
+This function converts the image the preferred format, and the
+converted image data is returned as a string."
+ (image-converter-initialize)
(unless image-converter
(error "No external image converters available"))
(when (and image-format
@@ -108,19 +125,27 @@ data is returned as a string."
(error "IMAGE-FORMAT should be a symbol like `image/png'"))
(with-temp-buffer
(set-buffer-multibyte nil)
- (when-let ((err (image-converter--convert
- image-converter
- (if (listp image)
- (plist-get (cdr image) :file)
- image)
- (if (listp image)
- (plist-get (cdr image) :data-p)
- image-format))))
- (error "%s" err))
+ (let* ((source (if (listp image)
+ (plist-get (cdr image) :file)
+ image))
+ (format (if (listp image)
+ (plist-get (cdr image) :data-p)
+ image-format))
+ (type (if format
+ (image-converter--mime-type format)
+ (file-name-extension source)))
+ (extra-converter (gethash type image-converter--extra-converters)))
+ (if extra-converter
+ (funcall extra-converter source format)
+ (when-let ((err (image-converter--convert
+ image-converter source format)))
+ (error "%s" err))))
(if (listp image)
;; Return an image object that's the same as we were passed,
;; but ignore the :type value.
- (apply #'create-image (buffer-string) 'png t
+ (apply #'create-image (buffer-string)
+ (intern image-convert-to-format)
+ t
(cl-loop for (key val) on (cdr image) by #'cddr
unless (eq key :type)
append (list key val)))
@@ -241,12 +266,15 @@ Only suffixes that map to `image-mode' are returned."
(list (format "%s:-"
(image-converter--mime-type
image-format))
- "png:-")))))
+ (concat image-convert-to-format
+ ":-"))))))
;; SOURCE is a file name.
(apply #'call-process (car command)
nil t nil
(append (cdr command)
- (list (expand-file-name source) "png:-")))))
+ (list (expand-file-name source)
+ (concat image-convert-to-format
+ ":-"))))))
;; If the command failed, hopefully the buffer contains the
;; error message.
(buffer-string))))
@@ -266,17 +294,33 @@ Only suffixes that map to `image-mode' are returned."
(append
(cdr command)
(list "-i" "-"
- "-c:v" "png"
+ "-c:v" image-convert-to-format
"-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"
+ "-c:v" image-convert-to-format
+ "-f" "image2pipe"
"-")))))
"ffmpeg error when converting")))
+;;;###autoload
+(defun image-converter-add-handler (suffix converter)
+ "Make Emacs use CONVERTER to parse image files that end with SUFFIX.
+CONVERTER is a function with two parameters, where the first is
+the file name or a string with the image data, and the second is
+non-nil if the first parameter is image data. The converter
+should output the image in the current buffer, converted to
+`image-convert-to-format'."
+ (cl-pushnew suffix image-converter-file-name-extensions :test #'equal)
+ (setq image-converter-file-name-extensions
+ (sort image-converter-file-name-extensions #'string<))
+ (setq image-converter-regexp
+ (concat "\\." (regexp-opt image-converter-file-name-extensions) "\\'"))
+ (setf (gethash suffix image-converter--extra-converters) converter))
+
(provide 'image-converter)
;;; image-converter.el ends here