Old code

I updated my curriculum vitae the other day and realized that the script I ran to generate the PDF file is twenty years old. Time flies.

In late ’96/early ’97, I transitioned away from the Mac and started using Linux as my full-time desktop operating system. Since 1985, my CV had been saved in many forms: MacWrite, MS Word, WriteNow, PageMaker, MS Word again, and Claris Works. Six different file formats over twelve years. I was sick of word processors, which was good because Linux didn’t have any to speak of. It would be years before OpenOffice arrived. I had to decide on a plain text solution to generate a printed CV.1

The two obvious solutions were troff and LaTeX. I hated LaTeX, mainly because I hated the Computer Modern font and didn’t know to change it. That left me with troff and its macro packages, ms, mm, and me. I wasn’t too thrilled with them, either. But there were other ways to use troff.

Like most people learning HTML in the 90s, I’d read that it was derived from SGML, a much more flexible markup system that could be specialized for, seemingly, any type of document. I was already figuring out ways to use SGML to produce reports, correspondence, and other documents, so it was no big deal to whip out a system for CVs.

Here’s the document type declaration (DTD) for my CV:

 1:  <!ELEMENT cv            - -     (name, pos, intro?, s+)>
 2:  <!ELEMENT name          - O     (#PCDATA)>
 3:  <!ELEMENT pos           - O     (#PCDATA)>
 4:  <!ELEMENT intro         - O     (#PCDATA)>
 5:  <!ELEMENT s             - O     (h,(item|ditem)*)>
 6:  <!ELEMENT h             O O     (#PCDATA)>
 7:  <!ELEMENT item          - O     (#PCDATA | cite | br)*>
 8:  <!ELEMENT ditem         - O     (#PCDATA | cite | br)*>
 9:  <!ATTLIST ditem         date    CDATA   #REQUIRED>
10:  <!ELEMENT br            - O     EMPTY>
11:  <!ELEMENT cite          - -     (#PCDATA)>

By this definition, a CV (Line 1) consists of a name, a position (or job title), an optional introductory paragraph, and a series of sections. The name, position, and intro (Lines 2–4) are just text. A section (Line 5) consists of a header followed by a set of items or dated items. A header (Line 6) is just text. Items and dated items (Lines 7–8) are a mixture of text, citations, and line breaks. Dated items have a single attribute (Line 9): text that defines the date. Line breaks (Line 10) contain no data, and citations (Line 11) contain text.

One of the great things about SGML—as opposed to its duller child, XML—is that you could define your document in such a way as to make certain element tags optional. The middle column of the DTD, the one that’s mostly hyphens and zeros, shows that the closing tag is optional for most of the elements. The opening of the next item implies the closing of the current item. In fact, headers need neither opening nor closing tags; because they’re the first item in a section, there’s no ambiguity in the header.

Here are the education and professional membership sections of my CV:

<ditem date=1985>
Ph.D. in Civil Engineering; University of Illinois at Urbana-Champaign
<ditem date=1982>
M.S. in Civil Engineering; University of Illinois at Urbana-Champaign
<ditem date=1981>
B.S. in Civil Engineering; University of Illinois at Urbana-Champaign

Professional societies
American Society of Civil Engineers
American Society of Mechanical Engineers
American Institute of Steel Construction
American Concrete Institute
American Welding Society
ASM International
American Statistical Association

You can see how the optional tags keep the clutter to a minimum compared to XML.

How do you parse an SGML document? With James Clark’s NSGMLS parser, part of his SP package, which has been around since 1994. There’s a parallel system called OpenSP, the purpose of which I’ve never understood, as Clark’s work was all open source. The easiest way to get this software on your Mac is by installing Homebrew’s open-sp package.

What NSGMLS does is convert your SGML document into a line-based format called ESIS that’s very easy to parse. What I did what write a simple Perl script, called cv2roff, to read the ESIS and output the troff codes necessary to format the CV. The pipeline to generate a PDF is

onsgmls cv.sgml | cv2roff | groff | ps2pdf - cv.pdf

This seems ridiculous, I know, but you have to consider this:

  1. Writing the DTD took almost no time after I’d already worked out the DTDs for more complex documents.
  2. Similarly, writing cv2roff consisted mainly of stealing code from the more complicated script that generated troff for reports.
  3. I don’t actually type out the pipeline. It’s invoked through a shell script called drangCV.

The upfront complexity in building this system led to great simplicity in use. Once or twice a year I add a couple of lines to cv.sgml and run drangCV. Changes in text editors and even operating systems have not affected this. The only change I’ve made in 20 years has been to add ps2pdf to the pipeline. The groff output used to be piped to lpr, which sent the PostScript directly to a printer.

James Thomson tweeted this yesterday:

Never assume that code will be short-lived. I’m still maintaining the first app I ever wrote when I was at university…

…25 years ago.

James Thomson (@jamesthomson) Sep 16 2017 7:52 AM

I don’t think I’m still running any code of mine that’s 25 years old, but I’m close. And although the Perl in cv2roff is pretty ugly—no, I’m not posting it—it’s been quite reliable.

  1. Paper was still the norm, at least among the people I worked with. It would be quite a while before I could email a PDF to clients and be certain they’d know how to open it—and that’s if they had an email address, which most didn’t. ↩︎

iOS cropping

I’m pretty sure I’ve always been frustrated by the way cropping works in the iOS Photos app. It’s usually presented as being so easy—just drag the crop handles where you want—but that isn’t really how it works. Quite often, a handle you aren’t dragging moves too, screwing up your careful editing.

Here’s an example. I took this screenshot on my iPhone from the Sherlock Holmes Wikipedia page.

Original screenshot

After opening it in Photos and tapping to get to the crop and rotate screen, we see this: a shrunken version of the image with the crop handles out at its corners.

Preparing for crop

Let’s say we want a final image with just the paragraph of text. We’ll start by dragging one of the top crop handles down to a spot just above the top of the paragraph.

After cropping top

If you look carefully, you’ll see that the image has expanded slightly to better fill the editing area. This has the advantage of providing us a bit more precision in positioning the crop handle in our next edit.

But if you look even more closely, you’ll see that something unintended has happened. The bottom handles have moved up very slightly, cropping out more of the last line of text captured in the screenshot. Not a big deal; we were going to crop those last couple of lines out anyway. Let’s go ahead and do so by moving one of the bottom crop handles up.

After cropping bottom

The image has again resized itself to fill the editing area, but this time the adjustment has completely screwed up our intended edit. The upper crop marks have moved down and sliced into the top line of the paragraph we were intending to capture. This happened even though we never touched either of the upper crop handles during the second edit.

This behavior, which seems to be driven by how the aspect ratio of the image changes as you drag one of the crop handles, is thoroughly unintuitive. The handle at the opposite corner of the one you’re dragging should never move.

There is a way around this, but it’s also unintuitive, and I often forget about it until my crop is ruined and I have to start over again.

Instead of dragging the crop handles at the corners of the image, touch and drag from the middle of an edge. For God knows what reason, cropping this way doesn’t change the position of the other crop handles. I say this is unintuitive because the handles at the corners look like the things you should be grabbing and moving. There are no visual clues that dragging from the middle of an edge is allowed.1

Here’s what happens to the Wikipedia screenshot when we drag the centers of the edges instead of the handles at the corners. First, dragging down from the top:

After cropping top by dragging center

Note that there’s been no change at the bottom. Also, I put the cropped top edge just above the text to make it easier to see that cropping out the bottom by dragging up the bottom edge leaves the top edge where we want it.

After cropping bottom by dragging center

I can’t think of any time in which the corner-dragging behavior of Photos is what I’d want, and I don’t understand why Apple made it the way it is. No other image editing app I’m aware of, including Apple’s own Preview on the Mac, changes the upper left handle of a selection when I’m dragging the lower right handle.

Update Sep 4, 2017 8:32 PM
The best explanation of the corner-dragging behavior came in this series of tweets from detailgeschrei (can’t anyone on the internet use their real name?). As we try to drag a corner handle straight up or down, we tend to slide our finger to the outside to maintain the full width of the image. When we do so, Photos stupidly interprets this as an instruction to enlarge the image as we drag. The diagonally opposite crop corner stays in place relative to the screen, but moves inward relative to the growing image. The upshot is a miscropped image.

When we drag the edge instead of the corner, the corners move perfectly vertically and the other side of the crop rectangle stays put, giving us what we want.

This explanation doesn’t let Apple off the hook. The image shouldn’t resize on screen no matter where we drag the crop handle until the dragging is done, and the diagonally opposite handle should never move relative to the image.

  1. Acorn on the Mac, an app I love, is also skimpy on edge-dragging clues. There are no handles at the centers of edges while using Acorn’s crop tool, but the cursor does change to show what you can do when it’s over an edge. ↩︎

Auto-switching to and from the iTunes MiniPlayer

A couple of days ago, Dan Sturm had a great idea:

Ugh, all I want is for iTunes to automatically switch to the mini player when it’s not the active window, is that so hard? Jeez
  — Dan Sturm (@dansturm) Wed Aug 30 2017 2:44 PM

The solution I found wasn’t that hard, but it did take some messing around. And even though it’s not foolproof—as Dan discovered in his testing—it’s good enough for me.

First, let’s define what we want. Under normal circumstances, iTunes displays its regular window.

iTunes regular window

To save space while still displaying the current track, especially when iTunes is in the background, there’s also a MiniPlayer window,

iTunes MiniPlayer

which can be stretched vertically to show the album cover art, but I typically don’t do that. I like having the MiniPlayer down in the lower right corner of my screen, where I seldom have other windows to cover it.

You can switch between the regular window and the MiniPlayer using the Switch to/from MiniPlayer command in iTunes’s Window menu, or use the ⇧⌘M keyboard shorcut. But this is a manual operation that you have to do when iTunes is active. What Dan wants—and what I now realize I want—is for iTunes to make this switch automatically: going to the regular view when iTunes is put in the foreground and to the MiniPlayer view when it’s put in the background.

Keyboard Maestro has a way to trigger actions upon an application being activated and deactivated, and it’s pretty easy to come up with a macro that switches to the regular window upon activation.

From MiniPlayer macro

The trickier macro is the one going the other direction. You’d think the thing to do would be to just change the menu item to Switch to MiniPlayer, but all that does is set up an infinite loop between the two macros, probably because Keyboard Maestro activates iTunes to execute the menu command.

What I found worked was good old-fashioned UI scripting in AppleScript, the kind of thing I used to have to do before buying Keyboard Maestro:

To MiniPlayer macro

The script is

1:  tell application "System Events"
2:    tell process "iTunes"
3:      click menu item "Switch to MiniPlayer" of menu "Window" of menu bar 1
4:    end tell
5:  end tell

I think this works because iTunes isn’t being activated during the execution of the script.

I have both of these macros saved in a Keyboard Maestro Group that’s available in all applications, and they work fine. Mostly.

Dan found a problem almost immediately. If you switch quickly into and out of iTunes (or vice versa), you can generate an error:

Keyboard Maestro error message

iTunes apparently doesn’t get its menus set up fast enough and isn’t ready to execute the command when it’s called. Although I’d like to fix this, I don’t feel like putting in a lot of time on something that won’t be a benefit under normal use. Other than stress testing, I don’t see why anyone would want to switch into and out of iTunes that quickly. As I told Dan,

@dansturm This is my version of “you’re holding it wrong.”
  — Dr. Drang (@drdrang) Wed Aug 30 2017 11:45 PM

Markdown nostalgia

Before I leave the subject of the current episode of The Talk Show, I want to mention one other topic John Gruber and Jason Snell talked about: Markdown.

John was apparently in a nostalgic mood when the episode was recorded—a common state among the middle-aged—and he shifted from the early days of Daring Fireball to the invention and initial development of Markdown. The latter didn’t directly follow from the former, but nearly so. And John indulged in a little “they all thought I was crazy” triumphalism, which I can’t blame him for. When I started using Markdown thirteen years ago, it was just one of many plain-text markup formats, and it was far down on the list when it came to popularity and recommendations. Now it’s everywhere and is the only plain-text format most people have heard of.

And if you’ll permit me my own bit of triumphalism, one of the last features to make it into the official, Gruber-certified version of Markdown, was due to my complaint near the end of 2004.

Markdown version

The release notes for Markdown 1.01 include the following:

Changed the syntax rules for code blocks and spans. Previously, backslash escapes for special Markdown characters were processed everywhere other than within inline HTML tags. Now, the contents of code blocks and spans are no longer processed for backslash escapes. This means that code blocks and spans are now treated literally, with no special rules to worry about regarding backslashes.

NOTE: This changes the syntax from all previous versions of Markdown. Code blocks and spans involving backslash characters will now generate different output than before.

Those are John’s words, of course, but it was my bitching that made him write them.

Then as now, I included a lot of source code in my Markdown, and certain backslashed character sequences had to be double backslashed in code blocks to get the resulting HTML to display correctly. For example, if you had a regex in your code that looked like this,

/\* [A-Z]/

you couldn’t simply paste it into Markdown and indent it. You had to go in after pasting and escape that backslash,

/\\* [A-Z]/

which was a pain in the ass and far too easy to overlook. Worse, it was unMarkdown-like, which is why John made the effort to rewrite the code block section of Markdown.pl.

So if you put a lot of source code in your Markdown documents and you never think twice about having to edit the code after pasting, you have me to thank. And John, I suppose.