-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathi7tohtml.py
171 lines (144 loc) · 4.42 KB
/
i7tohtml.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!/usr/bin/env python3
"""
i7tohtml.py: Simple syntax-coloring wrapper for Inform 7
Written by Andrew Plotkin. This script is in the public domain.
This script converts Inform 7 source code to HTML, adding syntax coloring
in a way which I like. Strings, comments, and substitutions are colored
to resemble the I7 IDE. I6 source inclusions are shown in fixed-width,
with strings and comments colored but no other I6 elements called out.
(I don't try to mark I7 inclusions in the I6. Doesn't seem worth it.)
The real work is done by the Pygments syntax-coloring library. This requires
Pygments 2.0, which is unreleased (as I write this). You'll have to get it
from the source repository: http://bitbucket.org/birkenfeld/pygments-main/
(This script is Python 3. I haven't tested it in Python 2. It would probably
get Unicode wrong.)
"""
import sys
import re
import codecs
import optparse
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import HtmlFormatter
from pygments.token import Token, Comment, String
from pygments.filter import Filter
popt = optparse.OptionParser(usage='i7tohtml.py [options] story.ni > out.html')
popt.add_option('-T', '--title',
action='store', dest='title', default='Game-Title',
help='Title of game (default: "Game-Title")')
popt.add_option('--css',
action='store', dest='cssfile', metavar='FILE',
help='read CSS file (instead of using built-in CSS)')
(opts, args) = popt.parse_args()
if not args:
code = sys.stdin.read()
else:
fl = open(args[0])
code = fl.read()
fl.close()
css = None
if opts.cssfile:
fl = open(opts.cssfile)
css = fl.read()
fl.close()
class CleanI6Filter(Filter):
"""Clean up the fruit-salad of I6 syntax coloring that Pygments normally
applies. All I6 code winds up in the Comment.Single, String.Other, or
Token.Other class.
"""
def __init__(self, **options):
Filter.__init__(self, **options)
def filter(self, lexer, stream):
i6mode = False
for ttype, value in stream:
if ttype is Token.Punctuation:
if value == '(-':
i6mode = True
yield ttype, value
continue
if value == '-)':
i6mode = False
yield ttype, value
continue
if i6mode:
if ttype is Comment.Single:
yield ttype, value
elif ttype is String.Double or ttype is String.Single or ttype is String.Char:
yield String.Other, value
else:
yield Token.Other, value
else:
yield ttype, value
lexer = get_lexer_by_name('inform7', encoding='utf-8')
lexer.add_filter(CleanI6Filter())
formatter = HtmlFormatter(nowrap=True, classprefix='i7', lineseparator='\n')
result = highlight(code, lexer, formatter)
header_block = '''<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>$TITLE$: Source Code</title>
<style type="text/css">
$CSS$
</style>
</head>
<body>
'''
css_block = '''
.i7cm {
font-style: italic;
color: #207D20;
}
.i7c1 {
font-family: monospace;
font-style: italic;
color: #207D20;
}
.i7gh {
font-size: 1.2em;
font-weight: bold;
}
.i7si {
font-style: italic;
color: #4E54FB;
}
.i7s2 {
font-weight: bold;
color: #095097;
}
.i7sx {
font-family: monospace;
font-weight: bold;
color: #095097;
}
.i7x {
font-family: monospace;
}
'''
footer_block = '''
</body>
</html>
'''
encode_ascii = codecs.getencoder('ascii')
spaces_re = re.compile(' +')
def spaces_replace(match):
num = match.end() - match.start()
if num == 0:
return ''
if num == 1:
return ' '
return (' ' * (num-1)) + ' '
if css is None:
css = css_block
print(header_block.replace('$CSS$', css).replace('$TITLE$', opts.title))
for ln in result.split('\n'):
# Detab (assuming four spaces per tab).
ln = ln.replace('\t', ' ')
ln = ln.rstrip()
# Convert any sequence of two or more spaces to a string of NBSP
# followed by a normal space. This gives us correct indentation.
ln = spaces_re.sub(spaces_replace, ln)
# Convert non-ASCII characters to HTML encoded chars (&#NNN;)
ln = encode_ascii(ln, 'xmlcharrefreplace')[0].decode()
print(ln)
print('<br>')
print(footer_block)