@code{max-image-size} is subsequently changed (@pxref{Image Cache}).
@end defvar
+Images inserted with the insertion functions above also get a local
+keymap installed in the text properties (or overlays) that span the
+displayed image. This keymap defines the following commands:
+
+@table @kbd
+@item +
+Increase the image size (@code{image-increase-size}). A prefix value
+of @samp{4} means to increase the size by 40%. The default is 20%.
+
+@item -
+Decrease the image size (@code{image-increase-size}). A prefix value
+of @samp{4} means to decrease the size by 40%. The default is 20%.
+
+@item r
+Rotate the image by 90 degrees (@code{image-rotate}).
+
+@item o
+Save the image to a file (@code{image-save}).
+@end table
+
@node Multi-Frame Images
@subsection Multi-Frame Images
@cindex multi-frame images
while in non-`visual-line-mode' it will move the point to the indented
header’s value.
-+++
-** Images are automatically scaled before displaying based on the
-`image-scaling-factor' variable (if Emacs supports scaling the images
-in question).
-
+++
** In Show Paren Mode, a parenthesis can be highlighted when point
stands inside it, and certain parens can be highlighted when point is
** If gpg2 exists on the system, it is now used as the default value
of `epg-gpg-program' (instead of gpg).
+** Images
+
++++
+*** Images are automatically scaled before displaying based on the
+`image-scaling-factor' variable (if Emacs supports scaling the images
+in question).
+
+*** Images inserted with `insert-image' and related functions get a
+keymap put into the text properties (or overlays) that span the
+image. This keymap binds keystrokes for manipulating size and
+rotation, as well as saving the image to a file.
+
** Lisp mode
---
:group 'image
:version "25.2")
+;; Map put into text properties on images.
+(defvar image-map
+ (let ((map (make-keymap)))
+ (define-key map "-" 'image-decrease-size)
+ (define-key map "+" 'image-increase-size)
+ (define-key map "r" 'image-rotate)
+ (define-key map "o" 'image-save)
+ map))
+
(defun image-load-path-for-library (library image &optional path no-error)
"Return a suitable search path for images used by LIBRARY.
(put-text-property 0 (length string) 'display prop string)
(overlay-put overlay 'put-image t)
(overlay-put overlay 'before-string string)
+ (overlay-put overlay 'map image-map)
overlay)))
(add-text-properties start (point)
`(display ,(if slice
(list (cons 'slice slice) image)
- image) rear-nonsticky (display)))))
+ image)
+ rear-nonsticky (display)
+ keymap ,image-map))))
;;;###autoload
(insert string)
(add-text-properties start (point)
`(display ,(list (list 'slice x y dx dy) image)
- rear-nonsticky (display)))
+ rear-nonsticky (display)
+ keymap ,image-map))
(setq x (+ x dx))))
(setq x 0.0
y (+ y dy))
(- 1 (/ n 10))
0.8)))
-(defun image-change-size (factor)
- (unless (fboundp 'imagemagick-types)
- (error "Can't rescale images without ImageMagick support"))
- (let ((image (get-text-property (point) 'display)))
+(defun image--get-image ()
+ (let ((image (or (get-text-property (point) 'display)
+ ;; `put-image' uses overlays, so find an image in
+ ;; the overlays.
+ (seq-find (lambda (overlay)
+ (overlay-get overlay 'display))
+ (overlays-at (point))))))
(when (or (not (consp image))
(not (eq (car image) 'image)))
(error "No image under point"))
+ image))
+
+(defun image--get-imagemagick-and-warn ()
+ (unless (fboundp 'imagemagick-types)
+ (error "Can't rescale images without ImageMagick support"))
+ (let ((image (image--get-image)))
+ (image-flush image)
(plist-put (cdr image) :type 'imagemagick)
+ image))
+
+(defun image-change-size (factor)
+ (let ((image (image--get-imagemagick-and-warn)))
(plist-put (cdr image) :scale
(* (or (plist-get (cdr image) :scale) 1) factor))))
+(defun image-rotate ()
+ "Rotate the image under point by 90 degrees clockwise."
+ (interactive)
+ (let ((image (image--get-imagemagick-and-warn)))
+ (plist-put (cdr image) :rotation
+ (float (+ (or (plist-get (cdr image) :rotation) 0) 90)))))
+
+(defun image-save ()
+ "Save the image under point."
+ (interactive)
+ (let ((image (get-text-property (point) 'display)))
+ (when (or (not (consp image))
+ (not (eq (car image) 'image)))
+ (error "No image under point"))
+ (with-temp-buffer
+ (let ((file (plist-get (cdr image) :file)))
+ (if file
+ (if (not (file-exists-p file))
+ (error "File %s no longer exists" file)
+ (insert-file-contents-literally file))
+ (insert (plist-get (cdr image) :data))))
+ (write-region (point-min) (point-max)
+ (read-file-name "Write image to file: ")))))
+
(provide 'image)
;;; image.el ends here