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.

nic ferrier
  • 173
  • 5
  • Are you simply missing the DATA-P argument to create-image? – phils Nov 02 '14 at 12:44
  • @phils I tried that, but it's still a blank square. – Malabarba Nov 02 '14 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 02 '14 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)) – nic ferrier Nov 02 '14 at 13:12
  • btw, this doesn't work for me either: (insert-image (create-image (with-current-buffer (find-file-noselect "known.jpg") (buffer-string)))) – nic ferrier Nov 02 '14 at 13:18
  • 1
    GOT IT! :) posting an answer – Malabarba Nov 02 '14 at 13:33

2 Answers2

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.

Malabarba
  • 23,148
  • 6
  • 79
  • 164
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).

nic ferrier
  • 173
  • 5