Some simple Services
September 17, 2013 at 10:41 PM by Dr. Drang
All the cool kids are writing workflows for Editorial, but since I don’t have Editorial or an iPad to run it on, I’m writing goofy Services to amuse myself.
I started with one that strikes through the selected characters. There are web pages with fields that’ll do this for you (like these two), but I wanted to
- Avoid the copying and pasting that’s necessary when you use these pages.
- Learn something about Unicode combining characters.
Strikethroughs are easy in HTML. You just surround the text with <s>
and </s>
so that <s>this text</s>
displays as this text. But you can’t do HTML in places like Twitter; there, you have to rely on Unicode.
The key Unicode character is U+0336, the COMBINING LONG STROKE OVERLAY. Put this after every regular character you want struck through and voila! S̶t̶r̶i̶k̶e̶t̶h̶r̶o̶u̶g̶h̶ ̶m̶a̶g̶i̶c̶.
(You may find the magic a little less magical than you’d like. Unlike the HTML, which makes a nice clean stroke through all the characters, Unicode U+0336 adjusts its position according to the height of the character it’s striking through, leading to a sort of wavy line. Also, not every font supports U+0336, so you may find yourself forced to use a font you don’t like. We must suffer for our art.)
Here’s an Automator screenshot of the Service I made to strike through the selected text:
It receives text in any application and replaces it with the output. The transformation is done by this Python script:
python:
1: import sys
2:
3: unstruck = sys.stdin.read()
4: struck = u'\u0336'.join(unstruck)
5: sys.stdout.write(struck.encode('utf-8')[:-1])
When debugging this script, I noticed something weird about Automator workflows. The standard input that the first step feeds to this script isn’t the selected text—it’s the selected text with a linefeed added to the end. That’s why Line 4 doesn’t put U+0336 after the last character of unstruck
and why Line 5 doesn’t write the last character of struck
: in each case, that last character is a linefeed that wasn’t in the original selection.1 I have no idea why this happens, but it seems like a bug to me.
Update 9/18/13
The original version of the script imported the subprocess
library, which you can still see in the screenshot. This was a a holdover from an earlier version the script that used pbcopy
to get the clipboard. Thanks to James Cash for pointing this out.
To use this Service, I select the text I want struck through and choose “Strikethrough selection” from the Services submenu. Or, more quickly, I use the ⌃⌥⇧⌘- keyboard shortcut I’ve mapped to that menu item.
That would be a lot of modifier keys to hold down at once, but I’ve mapped my keyboard to get that modifier bundle when I press the Caps Lock key, so it’s actually easier to press than a lot of keyboard combinations.
If you want, you could modify the script to use another strikethrough character. Unicode U+0338, COMBINING LONG SOLIDUS OVERLAY, would give you s̸o̸m̸e̸t̸h̸i̸n̸g̸ ̸l̸i̸k̸e̸ ̸t̸h̸i̸s̸, which I find a little too busy but you might like.
After making the strikethrough Service, I decided to add a couple of others. A few years ago, I wrote a TextExpander snippet that used Unicode phonetic symbols to make text that looked uʍop ǝpısdn. Turning it into a Service was pretty simple. The structure of this Service is the same as that above but with this Python script:
python:
1: # -*- coding: UTF-8 -*-
2:
3: from sys import stdin, stdout
4:
5: pchars = u"abcdefghijklmnopqrstuvwxyz,.?!'()[]{}"
6: fchars = u"ɐqɔpǝɟƃɥıɾʞlɯuodbɹsʇnʌʍxʎz'˙¿¡,)(][}{"
7: flipper = dict(zip(pchars, fchars))
8:
9: def flip(s):
10: charList = [ flipper.get(x, x) for x in s.lower() ]
11: charList.reverse()
12: return "".join(charList)
13:
14: stdout.write(flip(stdin.read()[:-1]).encode('utf8'))
The [:-1]
in Line 14 works around the standard input bug mentioned above.
I also changed a ROT13 snippet into a Service. The structure is the same as before but with guvf Clguba fpevcg:
python:
1: from string import maketrans
2: from sys import stdin, stdout
3:
4: alpha = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
5: rot13 = 'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
6: r13table = maketrans(alpha, rot13)
7:
8: orig = stdin.read()[:-1]
9: stdout.write(orig.translate(r13table))
The main advantage of using a Service instead of a snippet is speed. With the snippets, I had to copy the text to be transformed onto the clipboard and then type the snippet abbreviation. With a Service, I just select the text and hit the appropriate keyboard combination. A secondary advantage is that using the Service doesn’t clutter up my clipboard history.
I have a feeling these would be easy to convert into Editorial workflows, but workflow probably wouldn’t be the right word for them.
-
As it happens, Automator seems to strip the last character from the output if it’s a linefeed, so I get the same result whether the
[:-1]
is in Line 5 or not. I decided to keep it there to remind me of this weird behavior. ↩