Improved Mac screenshot/upload utility

I’ve made a couple of improvements to my screenshot/upload utility, snapftp. Snapftp is what I use in place of the Mac’s built-in ⌘⇧4 (Command-Shift-4) screenshot facility. The advantages of snapftp are:

  1. It allows me to name the screenshot as I take it.
  2. It can resize the screenshot on the fly.
  3. It can create both a thumbnail and a full-sized version of the screenshot.
  4. It can automatically upload the image file(s) to my blog.
  5. It puts the URL(s) of the uploaded file(s) on my clipboard for later pasting in a blog post.
  6. It optimizes the images for reduced file size.

Using snapftp

Snapftp has a simple but efficient GUI for setting the file name and size of the screenshot.

I invoke snapftp through FastScripts using the ⌃⌥⌘4 (Control-Option-Command-4) keyboard shortcut, but it could also be invoked though Quicksilver or LaunchBar. After I set the file name and options and click the Snap button, the snapftp window disappears and the cursor turns into a camera. Putting the camera over any window on the screen and clicking the mouse button takes the screenshot and does whatever resizing and uploading I requested. If I want a screenshot of some arbitrary rectangular area of the screen rather than a window, I push the spacebar to turn the camera cursor into a set of crosshairs and drag out the desired rectangle. (Snapftp’s use of the camera and crosshairs cursor is basically the same as that of ⌘⇧4, only inverted. I made the camera the default because I’m more likely to take screenshots of windows than of arbitrary rectangles.)

The screenshot files appear on my Desktop and, unless I set the “Local files only” option, in the images subdirectory here on my blog. The images are in PNG format; that’s the default for OS X since Tiger (10.4).

Building snapftp

Snapftp is written in Python and uses three command-line utilities that come standard with OS X:

  1. screencapture for doing the screenshot itself;
  2. sips for resizing the image; and
  3. pbcopy for putting the URLs on the clipboard.

It also uses two programs that aren’t standard on the Mac:

  1. Carsten Blüm’s Pashua to provide the GUI; and
  2. Cosmin Truţa’s OptiPNG to optimize the PNGs.

These have to be downloaded and installed before running snapftp.

Pashua comes in two parts: an application that you put in /Applications, and a Python module,, that you put in /Library/Python/2.5/site-packages. As I said in my earlier snapftp post, I think you should comment out Line 199 of

196:      # Parse result
197:      ResultDict = {}
198:      for Line in Result:
199:          # print Line
200:          Parm, Value = Line.split('=')
201:          ResultDict[Parm] = Value.rstrip()

because a library function that’s intended to read lines shouldn’t be printing anything.

OptiPNG needs to be compiled. Assuming you have the Developer Tools installed, download and extract the tarball and execute

sudo make install

from within the optipng-0.6.3 directory. The optipng binary and man page will be put in /usr/local.

Once Pashua and OptiPNG are installed, you’re ready for snapftp. It consists of this single relatively short Python file:

  1:  #!/usr/bin/python
  3:  import Pashua
  4:  import sys, os, shutil
  5:  from subprocess import *
  6:  from ftplib import *
  8:  # FTP and local parameters
  9:  host = ""
 10:  baseurl = ""
 11:  extension = ".png"
 12:  user = "drdrang"
 13:  passwd = "blahblah"   # no, not my real password
 14:  ftpdir = "public_html/all-this/images"
 15:  localdir = os.environ['HOME'] + "/Desktop"
 17:  # Dialog box configuration
 18:  conf = '''
 19:  # Window properties
 20:  *.title = Snapshot FTP
 22:  # File name text field properties
 23:  fn.type = textfield
 24:  fn.default = snap
 25:  fn.width = 264
 26:  fn.x = 94
 27:  fn.y = 130
 28:  fnl.type = text
 29:  fnl.default = File name:
 30:  fnl.x = 20
 31:  fnl.y = 132
 33:  # Radio button group properties
 34:  rb.type = radiobutton
 35:  rb.option = Original
 36:  rb.option = Resized
 37:  rb.option = Both
 38:  rb.default = Original
 39:  rb.x = 94
 40:  rb.y = 52
 41:  rbl.type = text
 42:  rbl.default = Capture:
 43:  rbl.x = 30
 44:  rbl.y = 92
 46:  # Resized width text field properties
 47:  rw.type = textfield
 48:  rw.default = 450
 49:  rw.height = 22
 50:  rw.width = 60
 51:  rw.x = 263
 52:  rw.y = 71
 53:  rwl.type = text
 54:  rwl.default = width:
 55:  rwl.width = 50
 56:  rwl.x = 215
 57:  rwl.y = 73
 59:  # Local files checkbox properties
 60:  lf.type = checkbox
 61:  lf.label = Local files only
 62:  lf.x = 32
 63:  lf.y = 5
 65:  # Default button
 66:  db.type = defaultbutton
 67:  db.label = Snap
 69:  # Cancel button
 70:  cb.type = cancelbutton
 71:  '''
 73:  # Open the dialog box and get the input.
 74:  dialog =
 75:  if dialog['cb'] == '1':
 76:    sys.exit()
 78:  # Go to the localdir.
 79:  os.chdir(localdir)
 81:  # Set the filenames and url.
 82:  fn =  '%s.png' % dialog['fn']
 83:  fnt = '%s-t.png' % dialog['fn']
 84:  url = '%s/%s' % (baseurl, fn)
 86:  # Capture a portion of the screen and save it to the file.
 87:  Popen(["screencapture", "-iW", fn], stdout=PIPE).communicate()
 89:  # Resize the file or create a separate thumbnail if requested.
 90:  if dialog['rb'] == 'Resized':
 91:    Popen(['sips', '--resampleWidth', dialog['rw'], fn],
 92:                      stdout=PIPE).communicate()
 93:  elif dialog['rb'] == 'Both':
 94:    shutil.copy(fn, fnt)
 95:    Popen(['sips', '--resampleWidth', dialog['rw'], fnt],
 96:                      stdout=PIPE).communicate()
 97:    # Optimize the thumbnail.
 98:    Popen(['optipng', '-quiet', fnt], stdout=PIPE).communicate()
100:  # Optimize the main file.
101:  Popen(['optipng', '-quiet', fn], stdout=PIPE).communicate()
103:  # Upload the file(s) and put the URL(s) on the clipboard unless told not to.
104:  if dialog['lf'] != '1':
105:    Popen('pbcopy', stdin=PIPE).communicate('%s/%s' % (baseurl, fn))
106:    ftp = FTP(host, user, passwd)
107:    ftp.cwd(ftpdir)
108:    ftp.storbinary("STOR %s" % fn, open(fn, "rb"))
109:    if dialog['rb'] == 'Both':
110:      ftp.storbinary("STOR %s" % fnt, open(fnt, "rb"))
111:      Popen('pbcopy', stdin=PIPE).communicate('%s/%s' % (baseurl, fnt))
112:    ftp.quit()

Obviously, you’ll need to change the things in Lines 8–15 to fit your situation. You may also want to change the resize default width in Line 48. I use a 450-pixel width because that’s a good match to the content area of this blog.

I won’t repeat the description of the code given in my original post, but I will describe the changes between then and now. The most important change is the addition of image optimization. The images produced by screencapture (Line 87) are a bit bloated. Lines 98 and 101 run the image files through optipng before uploading. These lines change the files in place; the original is overwritten by the optimized version.

The only switch passed to optipng is -quiet to keep it from spitting out progress messages. If you look at the optipng manual, you’ll see that it has many other options, mainly for controlling the optimization. At the moment, I don’t know much about these controls, so I’m just going with the defaults—that may change as I learn more. In my tests so far, the default optimization is reducing file sizes by 20–30%.

The other change from the original snapftp is in its use of the clipboard. Line 105 puts the URL of the uploaded image file onto the clipboard. That was all the original snapftp did. Now, if the user asks for a thumbnail as well (by clicking the “Both” radio button), Line 111 puts the URL for the thumbnail image file onto the clipboard. On most Macs, that would overwrite the first URL, but because I use Jumpcut, I get both URLs in my Clipboard history and can easily paste both of them into my blog posts. I’m pretty sure this will work for anyone using a Clipboard history utility—LaunchBar, iClip, Butler, etc.—but I’ve only tested it with Jumpcut.

Productivity and customization

If you like to edit and annotate your screenshots with comments and arrows and circles and things, you’ll probably be more productive with something like ImageWell. But if you take and upload lots of raw screenshots, snapftp will save you lots of time, especially if you dig into the source code and customize it to your specific needs. For example:

These are just the simple customizations that come to mind. If you make your own version of snapftp, I’d like to hear about it.