7

I am having trouble creating images with just data:

(web-http-get
 (lambda (con hdr data)
   (setq nic-profile-img-data data))
 :url "http://pbs.twimg.com/profile_images/494606908385288192/Vq16xxxu_normal.jpeg")

downloads an image from Twitter (yes, I'm writing a Twitter tool).

When I do this:

 (insert-image (create-image nic-profile-img-data 'jpeg))

I just get a blank square.

When I do this however:

(insert-image
 (progn
   (with-temp-file "/tmp/nic.jpg"
     (insert nic-profile-img-data))
   (create-image "/tmp/nic.jpg" 'jpeg)))

I get a nice picture of a really good looking Englishman.

I don't understand the difference here. I thought it might be encoding of the image data somehow, so I tried all sorts of different decoding and encoding (who understands that stuff in Emacs anyway?), but none of it made any difference.

The create-image with data stuff seems fairly new, and most code I've found uses files. I can use files, but it's an extra layer I didn't want.

How might this work and what, if anything I'm doing wrong?

Platform: Emacs 24-4 on GNU/Linux.

6
  • Are you simply missing the DATA-P argument to create-image?
    – phils
    Nov 2, 2014 at 12:44
  • @phils I tried that, but it's still a blank square.
    – Malabarba
    Nov 2, 2014 at 12:46
  • FWIW, if I load a jpeg into a buffer and then store its (buffer-string) to a variable and use (insert-image (create-image nic-profile-img-data 'jpeg t)), it works as expected. Maybe try that and then, if successful, try to ascertain the difference between the working and non-working data.
    – phils
    Nov 2, 2014 at 12:55
  • this doesn't work either: (insert-image (create-image (with-temp-buffer (insert nic-profile-img-data) (buffer-string)) nic-profile-img-data 'jpeg t)) Nov 2, 2014 at 13:12
  • 1
    GOT IT! :) posting an answer
    – Malabarba
    Nov 2, 2014 at 13:33

2 Answers 2

11

First of all: Use the third argument.

Both data and file are represented by a string, you need to tell create-image which one you're using. If you don't tell it, it defaults to file, which is why your data version wasn't working.

 (insert-image (create-image nic-profile-img-data 'jpeg t))

The third argument, when non-nil, specifies that the first argument is a data string instead of a filename.


Secondly: Use Unibyte

Even when doing the above, I still get a blank square. The reason is that the string you get from web-http-get is multibyte, and insert-image needs an unibyte string.

Here's one way to solve that:

(insert-image (create-image (string-as-unibyte nic-profile-img-data)
                            'jpeg t))

As for an example package which uses create-image from a data string, see the nethack-tiles.el file in nethack-el.

5
  • I'm still getting the blank square.
    – Dan
    Nov 2, 2014 at 12:16
  • @Dan Try it again :)
    – Malabarba
    Nov 2, 2014 at 13:36
  • Works for me! I'm gonna paste my rubbish solution to this using files. Nov 2, 2014 at 13:37
  • you would have thought that decoding to raw-text-unix would have done that. Good ole MULE. It did for Erik and it still haunts us now. Nov 2, 2014 at 13:41
  • Maybe report-emacs-bug to get a comment about unibyte data added to the documentation? On the one hand it seems obvious, but on the other it's (obviously) sufficiently easy to get wrong that a mention probably wouldn't be amiss.
    – phils
    Nov 3, 2014 at 3:07
0

Here's how to do it with files and a lot of crazy depends:

(insert-image
 (match-let* (((alist 'content-md5 md5) nic-image-meta)
              ((list* filename dir) (reverse (split-string md5 "/")))
              (directory (cons "/tmp/twaddle-avatars/" (reverse dir)))
              (dirpath (s-join "/" directory))
              (filepath (expand-file-name filename dirpath)))
   (unless (file-exists-p filepath)
     (make-directory dirpath t)
     (let ((coding-system-for-write 'raw-text))
       (with-temp-file filepath
         (insert nic-profile-img-data))))
   (create-image filepath'jpeg)))

This requires shadchen and dash at least.

I post it because it shows how to handle saving the file where you have the meta-data. The twitter meta-data for the file is:

(setq nic-image-meta
      '((x-content-type-options . "nosniff")
        (expires . "Mon, 17 Nov 2014 03:55:30 GMT")
        (x-cache-hits . "1")
        (x-cache . "HIT")
        (x-served-by . "cache-tw-lon2-cr1-5-TWLON2")
        (connection . "keep-alive")
        (age . "250298")
        (via . "1.1 varnish")
        (date . "Sun, 02 Nov 2014 03:55:30 GMT")
        (accept-ranges . "bytes")
        (content-length . "1731")
        (x-ton-expected-size . "1731")
        (server . "tfe")
        (last-modified . "Mon, 29 Sep 2014 04:37:52 GMT")
        (etag . "\"CmzKxLI/Fm2pvQV04q6cZA==\"")
        (content-type . "image/jpeg")
        (content-md5 . "CmzKxLI/Fm2pvQV04q6cZA==")
        (status-string . "OK")
        (status-code . "200")
        (status-version . "1.1")))

Dealing with the filename as a function of the md5 seems like a good idea and results in a very distributed storage across directories (which is good).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.