gallery.py: Shift urlencode into the _urllib_parse namespace for Python 2
[blog.git] / posts / Upside-down_text / 180.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # Original maps mostly drawn from
5 #  http://www.fileformat.info/convert/text/upside-down-map.htm
6 #  http://www.upsidedowntext.com/unicode
7
8 """Rotate ASCII text using unicode homographs.
9
10 Most glaringly obvious in my terminal:
11   25@BDKRUZ`jl
12
13 Some of these choices were a result of my font setup.  For example,
14   ᴚ LATIN LETTER SMALL CAPITAL TURNED R (U+1D1A)
15 is clearly superior to 
16   ʁ LATIN LETTER SMALL CAPITAL INVERTED R (U+0281)
17 but it't not in my default font.
18 """
19
20 __version__ = '0.1'
21
22 import sys
23
24
25 ROTATE_TABLE = {  # Ordered by key's code point.
26     # ASCII keys
27
28     # Code points U+0000 through U+0020 are whitespace.  Fall through.
29     u'!': u'¡', # EXCLAMATION MARK (U+0021), INVERTED EXCLAMATION MARK (U+00A1)
30     u'"': u'„', # QUOTATION MARK (U+0022), DOUBLE LOW-9 QUOTATION MARK (U+201E)
31     # # NUMBER SIGN (U+0023).  Fall through.
32     # $ DOLLAR SIGN (U+0024).  Fall through.
33     # % PERCENT SIGN (U+2005).  Fall through.
34     u'&': u'⅋', # AMPERSAND (U+0026), TURNED AMPERSAND (U+214B)
35     u"'": u'‚', # APOSTROPHE (U+0027), SINGLE LOW-9 QUOTATION MARK (U+201A)
36                 # Alternatives: , COMMA (U+002C)
37     u'(': u')', # LEFT PARENTHESIS (U+0028), RIGHT PARENTHESIS (U+0029)
38     # ) RIGHT PARENTHESIS (U+0029).  Inverse of '(' -> ')'.
39     u'*': u'⁎', # ASTERIX (U+002A), LOW ASTERIX (U+204E)
40     # + PLUS SIGN (U+002B).  Fall through.
41     u',': u'‘', # COMMA (U+002C), LEFT SINGLE QUOTATION MARK (U+2018)
42     # - HYPHEN-MINUS (U+2002D).  Fall through.
43     u'.': u'˙', # FULL STOP (U+002E), DOT ABOVE (U+02D9)
44     # / SOLIDUS (U+002F).  Fall through.
45     # 0 DIGIT ZERO (U+0030). Fall through.
46     u'1': u'⇂', # 1 DIGIT ONE (U+0031), DOWNWARDS HARPOON WITH BARB RIGHTWARDS (U+21C2)
47                 # Alternatives: Ɩ LATIN CAPITAL LETTER IOTA (U+0196)  HACK
48     u'2': u'Z', # DIGIT TWO (U+0032), Z LATIN CAPITAL LETTER Z (U+005A)  HACK
49                 # Alternatives: ჷ GEORGIAN LETTER YN (U+10F7)
50     u'3': u'Ɛ', # DIGIT THREE (U+0033), LATIN CAPITAL LETTER OPEN E (U+0190)
51     u'4': u'Ϟ', # DIGIT FOUR (U+0034), GREEK LETTER KOPPA (U+03DE)  HACK
52                 # Alternatives: ᔭ CANADIAN SYLLABICS YA (U+152D)
53                 #               ㄣ BOPOMOFO LETTER EN (U+3123)
54                 #               ߈ NKO DIGIT EIGHT (U+07C8)
55     u'5': u'ʢ', # 2 DIGIT TWO (U+2035), LATIN LETTER REVERSED GLOTTAL STOP WITH STROKE (U+02A2)  HACK
56                 # Alternatives: ϛ GREEK SMALL LETTER STIGMA (U+03DB)
57     u'6': u'9', # DIGIT SIX (U+0036), DIGIT NINE (U+0039)
58     u'7': u'Ł', # DIGIT SEVEN (U+0037), LATIN CAPITAL LETTER L WITH STROKE (U+0141)
59                 # Alternatives: L LATIN CAPITAL LETTER L (U+004C)
60                 #               Ɫ LATIN CAPITAL LETTER L WITH MIDDLE TILDE (U+2C62)
61                 #               Ƚ LATIN CAPITAL LETTER L WITH BAR (U+023D)
62                 #               ㄥ BOPOMOFO LETTER ENG (U+3125)
63     # 8 DIGIT EIGHT (U+0038).  Fall through.
64     # 9 DIGIT NINE (U+0039).  Inverse of '6' -> '9'.
65     # : COLON (U+003A).  Fall through.
66     u';': u'⁏', # SEMICOLON (U+003B), REVERSED SEMICOLON (U+204F)  HACK
67                 # Alternatives: ؛ ARABIC SEMICOLON (U+061B)
68     u'<': u'>', # LESS-THAN SIGN (U+003C), GREATER-THAN SIGN (U+003E)
69     # = EQUALS SIGN (U+003D).  Fall through.
70     # > GREATER-THAN SIGN (U+003E).  Inverse of '<' -> '>'.
71     u'?': u'¿', # QUESTION MARK (U+003F), INVERTED QUESTION MARK (U+00BF)
72     # @ COMMERCIAL AT (U+0040).  Fall through.
73                 # Alternatives: ᠗ MONGOLIAN DIGIT SEVEN (U+1817)
74     u'A': u'∀', # LATIN CAPITAL LETTER A (U+0041), FOR ALL (U+2200)
75                 # Alternatives: Ɐ LATIN CAPITAL LETTER TURNED A (U+2C6F)
76     u'B': u'θ', # LATIN CAPITAL LETTER B (U+0042), GREEK SMALL LETTER THETA (U+03B8)
77                 # Alternatives: 𐐒 DESERET CAPITAL LETTER BEE (U+10412)
78                 #               ৪ BENGALI DIGIT FOUR (U+09EA)
79     u'C': u'Ɔ', # LATIN CAPITAL LETTER C (U+0043), LATIN CAPITAL LETTER OPEN O (U+0186)
80                 # Alternatives: Ↄ ROMAN NUMERAL REVERSED ONE HUNDRED (U+2183)
81     u'D': u'◖', # LATIN CAPITAL LETTER D (U+0044), LEFT HALF BLACK CIRCLE (U+25D6)
82                 # Alternatives: ⫏ CLOSED SUBSET (U+2ACF)
83                 #               Ɑ LATIN CAPITAL LETTER ALPHA (U+2C6D)
84     u'E': u'Ǝ', # LATIN CAPITAL LETTER E (U+0045), LATIN CAPITAL LETTER REVERSED E (U+018E)
85     u'F': u'Ⅎ', # LATIN CAPITAL LETTER F (U+0046), TURNED CAPITAL F (U+2132)
86     u'G': u'⅁', # LATIN CAPITAL LETTER G (U+0047), TURNED SANS-SERIF CAPITAL G (U+2141)
87     # H LATIN CAPITAL LETTER H (U+0048).  Fall through.
88     # I LATIN CAPITAL LETTER I (U+0049).  Fall through.
89     u'J': u'ſ', # LATIN CAPITAL LETTER J (U+004A), LATIN SMALL LETTER LONG S (U+017F)
90     u'K': u'⋊', # LATIN CAPITAL LETTER K (U+004B), RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT (U+22CA)  HACK
91                 # Alternatives: ⺦ CJK RADICAL SIMPLIFIED HALF TREE TRUNK (U+2EA6)
92     u'L': u'⅂', # LATIN CAPITAL LETTER L (U+004C), TURNED SANS-SERIF CAPITAL L (U+2142)
93                 # Alternatives: Ꞁ LATIN CAPITAL LETTER TURNED L (U+A780)
94     u'M': u'W', # LATIN CAPITAL LETTER M (U+004D), LATIN CAPITAL LETTER W (U+0057)
95                 # Alternatives: ꟽ LATIN EPIGRAPHIC LETTER INVERTED M (U+A7FD)
96                 #               Ɯ LATIN CAPITAL LETTER TURNED M (U+019C)
97     u'N': u'N', # LATIN CAPITAL LETTER N (U+004E), LATIN CAPITAL LETTER N (U+004E)
98                 # Alternatives: ᴎ LATIN LETTER SMALL CAPITAL REVERSED N (U+1D0E)
99     u'O': u'O', # LATIN CAPITAL LETTER O (U+004F).  Fall through.
100     u'P': u'Ԁ', # LATIN CAPITAL LETTER P (U+0050), CYRILLIC CAPITAL LETTER KOMI DE (U+0500)
101     u'Q': u'Ό', # LATIN CAPITAL LETTER Q (U+0051), GREEK CAPITAL LETTER OMICRON WITH TONOS (U+038C)  HACK
102     u'R': u'ʁ', # LATIN CAPITAL LETTER R (U+0052), LATIN LETTER SMALL CAPITAL INVERTED R (U+0281)  HACK:not rotated
103                 # Alternatives: ᴚ LATIN LETTER SMALL CAPITAL TURNED R (U+1D1A)
104     u'S': u'S', # LATIN CAPITAL LETTER S (U+0053).  Fall through.
105     u'T': u'⊥', # LATIN CAPITAL LETTER T (U+0054), UP TACK (U+22A5)
106                 # Alternatives: ⟂ PERPENDICULAR (U+27C2)
107     u'U': u'∩', # LATIN CAPITAL LETTER U (U+0055), INTERSECTION (U+2229)  HACK
108     u'V': u'Λ', # LATIN CAPITAL LETTER V (U+0056), GREEK CAPITAL LETTER LAMBDA (U+039B)
109                 # Alternatives: Ʌ LATIN CAPITAL LETTER TURNED V (U+0245)
110     # W LATIN CAPITAL LETTER W (U+0057).  Inverse of 'M' -> 'W'.
111     # X LATIN CAPITAL LETTER X (U+0058).  Fall through.
112     u'Y': u'⅄', # LATIN CAPITAL LETTER Y (U+0059), TURNED SANS-SERIF CAPITAL Y (U+2144)
113     # Z LATIN CAPITAL LETTER Z (U+005A).  Fall through.
114     u'[': u']', # LEFT SQUARE BRACKET (U+005B), RIGHT SQUARE BRACKET (U+005D)
115     # \ REVERSE SOLIDUS (U+005C).  Fall through.
116     # ] RIGHT SQUARE BRACKET (U+005D).  Inverse of '[' -> ']'
117     u'^': u'⌵', # CIRCUMFLEX ACCENT (U+005E), COUNTERSINK (U+2335)
118     u'_': u'‾', # LOW LINE (U+005F), OVERLINE (U+203E)
119     u'`': u'․', # GRAVE ACCENT (U+0060), ONE DOT LEADER (U+2024)  HACK
120                 # Alternatives: , COMMA (U+002C)
121                 #               ، ARABIC COMMA (U+060C)
122     u'a': u'ɐ', # LATIN SMALL LETTER A (U+0061), LATIN SMALL LETTER TURNED A (U+0250)
123     u'b': u'q', # LATIN SMALL LETTER B (U+0062), LATIN SMALL LETTER Q (U+0071)
124     u'c': u'ɔ', # LATIN SMALL LETTER C (U+0063), LATIN SMALL LETTER OPEN O (U+0254)
125     u'd': u'p', # LATIN SMALL LETTER D (U+0064), LATIN SMALL LETTER P (U+0070)
126     u'e': u'ǝ', # LATIN SMALL LETTER E (U+0065), LATIN SMALL LETTER TURNED E (U+01DD)
127     u'f': u'ɟ', # LATIN SMALL LETTER F (U+0066), LATIN SMALL LETTER DOTLESS J WITH STROKE (U+025F)
128     u'g': u'ƃ', # LATIN SMALL LETTER G (U+0067), LATIN SMALL LETTER B WITH TOPBAR (U+0183)
129                 # Alternatives: ᵷ LATIN SMALL LETTER TURNED G (U+1D77)
130     u'h': u'ɥ', # LATIN SMALL LETTER H (U+0068), LATIN SMALL LETTER TURNED H (U+0265)
131     u'i': u'ı', # LATIN SMALL LETTER I (U+0069), LATIN SMALL LETTER DOTLESS I (U+0131)
132     u'j': u'ɾ', # LATIN SMALL LETTER J (U+006A), LATIN SMALL LETTER R WITH FISHHOOK (U+027E)
133     u'k': u'ʞ', # LATIN SMALL LETTER K (U+006B), LATIN SMALL LETTER TURNED K (U+029E)
134     # l LATIN SMALL LETTER L (U+006C).  Fall through.
135                 # Alternatives: ʃ LATIN SMALL LETTER ESH (U+0283)
136                 #               ן HEBREW LETTER FINAL NUN (U+05DF, right to left)
137     u'm': u'ɯ', # LATIN SMALL LETTER M (U+006D), LATIN SMALL LETTER TURNED M (U+026F)
138     u'n': u'u', # LATIN SMALL LETTER N (U+006E), LATIN SMALL LETTER U (U+0075)
139                 # Alternatives: и CYRILLIC SMALL LETTER I (U+0438)
140     # o LATIN SMALL LETTER Z (U+006F).  Fall through.
141     # p LATIN SMALL LETTER P (U+0070).  Inverse of 'd' -> 'p'.
142     # q LATIN SMALL LETTER Q (U+0071).  Inverse of 'b' -> 'q'.
143     u'r': u'ɹ', # LATIN SMALL LETTER R (U+0072), LATIN SMALL LETTER TURNED R (U+0279)
144     # s LATIN SMALL LETTER S (U+0073).  Fall through.
145     u't': u'ʇ', # LATIN SMALL LETTER T (U+0074), LATIN SMALL LETTER TURNED T (U+0287)
146     # u LATIN SMALL LETTER U (U+0075).  Inverse of 'n' -> 'u'.
147     u'v': u'ʌ', # LATIN SMALL LETTER V (U+0076), LATIN SMALL LETTER TURNED V (U+028C)
148     u'w': u'ʍ', # LATIN SMALL LETTER W (U+0077), LATIN SMALL LETTER TURNED W (U+028D)
149     # x LATIN SMALL LETTER X (U+0078).  Fall through.
150     u'y': u'ʎ', # LATIN SMALL LETTER Y (U+0079), LATIN SMALL LETTER TURNED Y (U+028E)
151     # z LATIN SMALL LETTER Z (U+007A).  Fall through.
152     u'{': u'}', # LEFT CURLY BRACKET (U+007B), RIGHT CURLY BRACKET (U+007D)
153     # | VERTICAL LINE (U+007C).  Fall through.
154     # } RIGHT CURLY BRACKET (U+007B).  Inverse of '{' -> '}'.
155     # ~ TILDE (U+007E).  Fall through.
156     # Code point U+007F (NULL) is whitespace.  Fall through.
157     }
158
159 INVERSE_ROTATE_TABLE = dict([[v,k] for k,v in ROTATE_TABLE.items()])
160
161 def rotate(c):
162     return ROTATE_TABLE.get(c, INVERSE_ROTATE_TABLE.get(c, c))
163
164 def write_in_rows(values, stream, encoding):
165     M = max([len(v) for v in values])
166     m = min([len(v) for v in values])
167     assert M == m, 'string lengths range from %d to %d' % (m, M)
168     columns = 80 / (2+m)  # integer division rounds down
169     row = 0
170     for i,v in enumerate(values):
171         if row == columns:
172             stream.write('\n')
173             row = 0
174         elif i != 0:
175             stream.write('  ')
176         row += 1
177         stream.write(v.encode(encoding))
178     stream.write('\n')
179
180 def test(stream=sys.stdout, encoding='utf-8'):
181     format = u'%2x %s %s'
182     stream.write('Printable rotations:\n')
183     write_in_rows(
184         values=[format % (i, chr(i), rotate(chr(i))) for i in range(33, 127)],
185         stream=stream, encoding=encoding)
186     collisions = [format % (i, chr(i), rotate(chr(i))) for i in range(0, 128)
187                   if chr(i) != rotate(rotate(chr(i)))]
188     if len(collisions) > 0:
189         stream.write('Collisions:\n')
190         write_in_rows(values=collisions, stream=stream, encoding=encoding)
191
192
193 if __name__ == '__main__':
194     if len(sys.argv) > 1:  # print all the ascii chars and their inverse
195         encoding = sys.argv[1]
196         try:
197             u''.encode(encoding)
198         except LookupError:
199             encoding = 'utf-8'  # default to utf-8
200         test(encoding=encoding)
201         sys.exit(0)
202     print ''.join([rotate(c) for c in reversed(sys.stdin.read().strip())])