0

I am trying to make jabber.el support images sent from e.g. Conversations.

I download the image using url-retrieve-synchonously and call create-image to get an image which I can then use with insert-image. This works as expected.

I would like, however, to put a :max-width and :max-height on the image. I can add them in the create-image call, but because create-image automatically inserts a :width and a :height image property, the max-values are ignored, as the manual explains:

The :max-width and :max-height keywords are used for scaling if the size of the image of the image exceeds these values. If :width is set it will have precedence over max-width, and if :height is set it will have precedence over max-height

The documentation says that I can remove image properties with setf, by setting them to nil:

Function: image-property image property

Return the value of property in image. Properties can be set by using setf. Setting a property to nil will remove the property from the image.

This, however, I can't make work - if I setf a property to nil, the key is deleted, but the value isn't, and the image object seems to be messed up.

Example (simplified):

(defun my-fetch-image (url)
  "Retun filename of image downloaded from url"
  (url-copy-file url "/tmp/test.jpg")
  (create-image "/tmp/test.jpg"))

(setq my-image (my-fetch-image "https://koldfront.dk/photo/pics/2018/09/snapshot-22-142810-s.jpg"))

Now, my-image looks like this: (image :type imagemagick :file "/tmp/test.jpg" :scale 1.2019047619047618 :width 288 :height 400)

And when I try to remove width and height:

(setf (image-property my-image :width) nil)
(setf (image-property my-image :height) nil)

I am left with my-image looking like this: (image :type imagemagick :file "/tmp/test.jpg" :scale 1.2019047619047618 288 :height 400)

As you can see :width got removed, but the value 288 is still there, and :height 400 is untouched.

I'm sure I haven't understood how to use setf, but I can't find any examples to go by, and my guesses don't yield the result I'm after, either.

asjo
  • 1,037
  • 8
  • 12

3 Answers3

1

Update: The following statement is incorrect, I can reproduce the issue, and I believe it's a bug of image--set-property.

I can't reproduce with Emacs 26.1 and 26.2 from emacs -Q, the following produces the expected result, unlike yours:

;; So `setf' can know how to macroexpand `image-property'
(require 'image)

(let ((my-image '(image :type imagemagick
                        :file "/tmp/test.jpg"
                        :scale 1.2019047619047618
                        :width 288
                        :height 400)))
  (setf (image-property my-image :width) nil)
  my-image)

;; =>

(image :type imagemagick
       :file "/tmp/test.jpg"
       :scale 1.2019047619047618 288
       :height 400)

setf is a macro, so you can macroexpand it to understand what's it doing, for example, (setf (point) 1) expands to (goto-char 1). (Personally, I use M-x macrostep-expand from a third-party package, however, nowadays Emacs provides the builtin emacs-lisp-macroexpand command.).

xuchunyang
  • 14,302
  • 1
  • 18
  • 39
  • Uhm, the result you show is the same result as I get. Notice the "288" efter the floating point value in your output? That's the value of the :width your call to setf removed. – asjo Apr 14 '19 at 11:25
  • If you add another setf call to remove the :height, I bet it won't be gone for you either. – asjo Apr 14 '19 at 11:26
  • 1
    @asjo Oops, you're right, I missed the number 288. There is a bug in `image--set-property`, `(setcdr image (cddr image))` should be `(setcdr image (cdddr image))`, i.e., change `cddr` into `cdddr`. – xuchunyang Apr 14 '19 at 12:28
  • Thanks for looking into it - have you reported the bug or should I? – asjo Apr 14 '19 at 21:29
  • 1
    @asjo No, I've not. I think you should. – xuchunyang Apr 15 '19 at 04:23
  • 1
    Thanks for the help analyzing this, I have reported the bug now: https://debbugs.gnu.org/35285 – asjo Apr 15 '19 at 10:40
0

So far, the workaround I have found is:

            (let ((width (image-property image :width))
                  (height (image-property image :height)))
              (setf (image-property image :width) nil)
              (setf (image-property image width) nil)
              (setf (image-property image :height) nil)
              (setf (image-property image height) nil))

This can't be the right way‽

asjo
  • 1,037
  • 8
  • 12
0

For what's worth, since it's bug of Emacs, before it gets fixed, you can roll your own, something like the following (I wrote it mainly for fun and some practice):

(defun my-image-delete-properties (image &rest props)
  (cl-loop for (k v) on (cdr image) by #'cddr
           unless (memq k props)
           nconc (list k v) into properties
           finally return (cons (car image) properties)))

(let ((my-image '(image :type imagemagick
                        :file "/tmp/test.jpg"
                        :scale 1.2019047619047618
                        :width 288
                        :height 400)))
  (my-image-delete-properties my-image :width :height))
;; => (image :type imagemagick
;;           :file "/tmp/test.jpg"
;;           :scale 1.2019047619047618)

Unlike (setf (image-property image :width) nil), the above doesn't change image in-place (it is usually a good idea), if you want that, you can try the following instead:

(defun my-image-delete-properties (image &rest props)
  (cl-loop for (k v) on (cdr image) by #'cddr
           unless (memq k props)
           nconc (list k v) into properties
           finally return (setcdr image properties))
  image)

(let ((my-image '(image :type imagemagick
                        :file "/tmp/test.jpg"
                        :scale 1.2019047619047618
                        :width 288
                        :height 400)))
  (my-image-delete-properties my-image :width :height)
  my-image)
;; => (image :type imagemagick
;;           :file "/tmp/test.jpg"
;;           :scale 1.2019047619047618)

By the way, there is org-plist-delete for deleting a property from a plist, it doesn't change the argument in-place as well, like the first my-image-delete-properties.

xuchunyang
  • 14,302
  • 1
  • 18
  • 39