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, Pashua.py, 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 Pashua.py

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

./configure
make
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
2:
3:  import Pashua
4:  import sys, os, shutil
5:  from subprocess import *
6:  from ftplib import *
7:
8:  # FTP and local parameters
9:  host = "leancrew.com"
10:  baseurl = "http://www.leancrew.com/all-this/images"
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"
16:
17:  # Dialog box configuration
18:  conf = '''
19:  # Window properties
20:  *.title = Snapshot FTP
21:
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
32:
33:  # Radio button group properties
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
45:
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
58:
59:  # Local files checkbox properties
60:  lf.type = checkbox
61:  lf.label = Local files only
62:  lf.x = 32
63:  lf.y = 5
64:
65:  # Default button
66:  db.type = defaultbutton
67:  db.label = Snap
68:
69:  # Cancel button
70:  cb.type = cancelbutton
71:  '''
72:
73:  # Open the dialog box and get the input.
74:  dialog = Pashua.run(conf)
75:  if dialog['cb'] == '1':
76:    sys.exit()
77:
78:  # Go to the localdir.
79:  os.chdir(localdir)
80:
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)
85:
86:  # Capture a portion of the screen and save it to the file.
87:  Popen(["screencapture", "-iW", fn], stdout=PIPE).communicate()
88:
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()
99:
100:  # Optimize the main file.
101:  Popen(['optipng', '-quiet', fn], stdout=PIPE).communicate()
102:
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:

• If you want your screenshots’ default names to include a time and date code, you can use Python’s datetime module to get a timestamp and use that instead of “snap” in Line 24.
• If you prefer the default screenshot to be of a rectangular area instead of a window, you can change the "-iW" option in Line 87 to "-i".
• If you’re more concerned with your images’ height than their width, you can change the "--resampleWidth" options in Lines 91 and 95 to "--resampleHeight".
• If you want to restrict the size of the longest dimension, whether width or height, you can change Lines 91 and 95 to use "--resampleHeightWidthMax".
• If you don’t like your Desktop cluttered with screenshot files, you can change Line 15 to put them in some other folder.

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.