There are two ways to do anything:There are actually a lot more than two ways to code anything. But first here's the story: my elder daughter was talking about writing poetry "as the ox turns" (more here) and wondered how hard it would be to visually flip every other line left-to-right. Just reversing the letters is easy; something like this would do it:
• the wrong way, and
• your mother’s way.(redacted)
#!/usr/bin/python -utt # vim:et:sw=4 '''Reverse every other line. Like a filter.''' import sys flipIt = False for aline in sys.stdin: aline = aline.strip() if flipIt: alist = list(aline) alist.reverse() print ''.join(alist) else: print aline flipIt = not flipItIt works like this:
INPUT:
the leaves they were crisped and sere -
The leaves they were withering and sere;
It was night in the lonesome October
Of my most immemorial year:
OUTPUT:
- eres dna depsirc erew yeht sevael eht
The leaves they were withering and sere;
rebotcO emosenol eht ni thgin saw tI
Of my most immemorial year:
The Wrong Way to Code This
Naturally I went looking on the web for "truetype file format" and proceeded to pick a font file apart, using information from Apple and Microsoft. I began like this:def main(filename): global font_data, flip_data font_data = [ord(X) for X in list(file(filename, 'rb').read())] # offset subtable scaler_type = u32(0) assert(scaler_type == 0x74727565 or scaler_type == 0x10000) numTables = u16(4) print 'numTables', numTables startTabDir = 12 print 'table list' glyfStart = None tables = dict() toffset2name = dict() for tabEntryOffset in range(0,numTables*16,16): mystart = startTabDir+tabEntryOffset tname = l2s(font_data[mystart:mystart+4]) tstart, tlen = u32(mystart+8), u32(mystart+0xc) print '\t%s offset=%#08x len=%#x' % (tname, tstart, tlen) assert(tname not in tables) # duplicate => evil toffset2name[tstart] = tname # to sort by offset if tname == 'glyf': # flip glyphs left-to-rightBasically, I read the entire file in as a bytestream, then created an array (a "list" in Python-ese) of the bytes. That u32(0) call means "give me the 32-bit integer formed by reading 4 bytes starting at offset 0" (that's way later in the file). This program knows where tables start, and looks for certain tables by name (e.g, "glyf"), and...
So what's wrong with coding like this? The problem is that it's re-inventing the wheel. What I should have done, had I known of it at the time, was consult stackoverflow.com; I would have found questions and answers like this one, which gave me the clue that maybe there was a module out there that already handles truetype (though that question was about PERL) or (aha!) this one which led me to TTX, a fabulous package that turns ".ttf" files into XML and back.
So I was doing a bunch of work (and a lot of it was empirical, based just on what I found in this one file) rather than following the possible versions (etc) that the specs allow. Before I stopped working on the wrong way to code this, I had 476 lines in "flip.py" -- of which 398 were nonblank, noncomment lines. Besides, the fonts it produced weren't quite right.
Not so Wrong
So here's the new plan. Rather than writing all that code to parse the (mostly binary) TTF file, ttx would turn (e.g.) times.ttf⇒times.ttx; I'd modify the XML inside times.ttx, creating, say, semit.ttx ("times" backwards) and ttx would turn that into "semit.ttf".Besides flipping each glyph left-to-right, I'd also flip the kerning table. Why? Consider the character pair ‘P.’ -- we want the ‘.’ closer to the ‘P’ than it would be without kerning, right? Now imagine if the ‘P’ is flipped left-to-right so it "sticks out to the left" like ‘¶’ -- in this case we want the characters moved closer when the ‘.’ comes before the (flipped) ‘P’. Thus the pair we want to look for is not {‘P’,‘.’} but rather {‘.’,‘P’}.
It's now about 121 lines (nonblank noncomment lines), after trying it out on a few more fonts (one of which didn't have a kerning table). The fonts look good, too. Here's the result of "pydoc -w flipttx":
flipttx | index /mnt/home/collin/fonts/flipttx.py |
Flip a true type font (ttx) horizontally.
Usage: flipttx.py [-d] {-o oldname} {-n newname} [infile [outfile]]
-d (OPTIONAL): add debugging output
oldname (REQUIRED) is original font name, e.g., "Times New Roman"
newname (REQUIRED) is new (flipped) font name
infile (OPTIONAL) is name of old font's ttx file
newfile (OPTIONAL) is name of new font's ttx file
OPERATION
Given a truetype font, named "Foo", in a file "fontFile.ttf",
create a flipped font (call it "Oof") in "oof-flip.ttf" as follows:
1. ttx fontFile.ttf
=> will create fontFile.ttx
2. flipttx.py -o Foo -n Oof fontFile.ttx oof-flip.ttx
=> will create oof-flip.ttx
* and "Foo" in fontFile.ttx's NAME table becomes "Oof" in oof-flip.ttx
3. ttx oof-flip.ttx
=> will create oof-flip.ttf
Use spadmin (for OpenOffice.org) or FontBook (on Mac OS X), etc.
to get oof-flip into your system. In your application (OpenOffice.org,
NeoOffice, Micro$oft Office, etc.) specify font name "Oof"
Why are oldname/newname required? Because you need a way to specify
the flipped font name (e.g., "Oof").
GUIDES FOR THE PERPLEXED
* "ttx" -- see http://www.letterror.com/code/ttx/
* what's in a truetype font file?
http://developer.apple.com/fonts/ttrefman/rm06/Chap6.html (2002)
http://www.microsoft.com/typography/otspec/otff.htm (2008)
HOW TO AVOID UGLY ON-SCREEN DISPLAY
1. On a system like Mac OS X 10.6 you can just do what comes
naturally: add fonts using the "Font Book" application.
Then NeoOffice, Aqua (maybe even X11) will be able to
display the new font just fine.
2. On a system like OpenSUSE 11.3 where OpenOffice.org gets
fonts from "spadmin" but you need to "xset fp+" or similar
for display fonts, be sure to run mkfontdir(1) then add
the directory to your X server's font path, lest your
screen look truly ugly. But at least on my system, "print"
and "export to PDF" from OpenOffice.org both produced nice
enough output.
BUGS
1. Doesn't handle a too-short hmtx table, such as might be
the case with some monospaced font. (But Courier New
was OK -- maybe ttx creates a full hmtx table?)
2. Doesn't do anything with composite glyphs. Maybe they'll
"Just Work" -- but probably not.
3. Doesn't do anything with GSUB so ligatures, if your font has
any, will probably look goofy.
VERSION
$Id: flipttx.py,v 0.5 2011/03/26 20:05:07 collin Exp $
Modules | ||||||
|
Functions | ||
|
Data | ||
DEBUG = False INFILE = <open file '<stdin>', mode 'r'> NEWNAME = None OLDNAME = None OUTFILE = <open file '<stdout>', mode 'w'> progname = 'flipttx.py' |
And maybe an even less wrong way...
A search on "poetry oxturn" (no quotes) led me here which in turn led to this program which does the whole thing for you -- flips every other line and creates a postscript or PDF of the result. Apparently you can just try it online without having to download it and run under Tcl/Tk.But... currently it gives you a typewriter-like font, not so pretty. This may change soon, as I'll send my program to the site's webmaster.
2 comments:
I know this is an old post, but I'd love to see the code!
here ya go... http://cpwriter.net/tmp/flipttx.py
Post a Comment