1 # ToHTML (c) 2002, 2003, 2005, 2006, 2007, 2008 |
|
2 # David Turner <david@freetype.org> |
|
3 |
|
4 from sources import * |
|
5 from content import * |
|
6 from formatter import * |
|
7 |
|
8 import time |
|
9 |
|
10 |
|
11 # The following defines the HTML header used by all generated pages. |
|
12 html_header_1 = """\ |
|
13 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
|
14 "http://www.w3.org/TR/html4/loose.dtd"> |
|
15 <html> |
|
16 <head> |
|
17 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> |
|
18 <title>\ |
|
19 """ |
|
20 |
|
21 html_header_2 = """\ |
|
22 API Reference</title> |
|
23 <style type="text/css"> |
|
24 body { font-family: Verdana, Geneva, Arial, Helvetica, serif; |
|
25 color: #000000; |
|
26 background: #FFFFFF; } |
|
27 |
|
28 p { text-align: justify; } |
|
29 h1 { text-align: center; } |
|
30 li { text-align: justify; } |
|
31 td { padding: 0 0.5em 0 0.5em; } |
|
32 td.left { padding: 0 0.5em 0 0.5em; |
|
33 text-align: left; } |
|
34 |
|
35 a:link { color: #0000EF; } |
|
36 a:visited { color: #51188E; } |
|
37 a:hover { color: #FF0000; } |
|
38 |
|
39 span.keyword { font-family: monospace; |
|
40 text-align: left; |
|
41 white-space: pre; |
|
42 color: darkblue; } |
|
43 |
|
44 pre.colored { color: blue; } |
|
45 |
|
46 ul.empty { list-style-type: none; } |
|
47 </style> |
|
48 </head> |
|
49 <body> |
|
50 """ |
|
51 |
|
52 html_header_3 = """ |
|
53 <table align=center><tr><td><font size=-1>[<a href="\ |
|
54 """ |
|
55 |
|
56 html_header_3i = """ |
|
57 <table align=center><tr><td width="100%"></td> |
|
58 <td><font size=-1>[<a href="\ |
|
59 """ |
|
60 |
|
61 html_header_4 = """\ |
|
62 ">Index</a>]</font></td> |
|
63 <td width="100%"></td> |
|
64 <td><font size=-1>[<a href="\ |
|
65 """ |
|
66 |
|
67 html_header_5 = """\ |
|
68 ">TOC</a>]</font></td></tr></table> |
|
69 <center><h1>\ |
|
70 """ |
|
71 |
|
72 html_header_5t = """\ |
|
73 ">Index</a>]</font></td> |
|
74 <td width="100%"></td></tr></table> |
|
75 <center><h1>\ |
|
76 """ |
|
77 |
|
78 html_header_6 = """\ |
|
79 API Reference</h1></center> |
|
80 """ |
|
81 |
|
82 |
|
83 # The HTML footer used by all generated pages. |
|
84 html_footer = """\ |
|
85 </body> |
|
86 </html>\ |
|
87 """ |
|
88 |
|
89 # The header and footer used for each section. |
|
90 section_title_header = "<center><h1>" |
|
91 section_title_footer = "</h1></center>" |
|
92 |
|
93 # The header and footer used for code segments. |
|
94 code_header = '<pre class="colored">' |
|
95 code_footer = '</pre>' |
|
96 |
|
97 # Paragraph header and footer. |
|
98 para_header = "<p>" |
|
99 para_footer = "</p>" |
|
100 |
|
101 # Block header and footer. |
|
102 block_header = '<table align=center width="75%"><tr><td>' |
|
103 block_footer_start = """\ |
|
104 </td></tr></table> |
|
105 <hr width="75%"> |
|
106 <table align=center width="75%"><tr><td><font size=-2>[<a href="\ |
|
107 """ |
|
108 block_footer_middle = """\ |
|
109 ">Index</a>]</font></td> |
|
110 <td width="100%"></td> |
|
111 <td><font size=-2>[<a href="\ |
|
112 """ |
|
113 block_footer_end = """\ |
|
114 ">TOC</a>]</font></td></tr></table> |
|
115 """ |
|
116 |
|
117 # Description header/footer. |
|
118 description_header = '<table align=center width="87%"><tr><td>' |
|
119 description_footer = "</td></tr></table><br>" |
|
120 |
|
121 # Marker header/inter/footer combination. |
|
122 marker_header = '<table align=center width="87%" cellpadding=5><tr bgcolor="#EEEEFF"><td><em><b>' |
|
123 marker_inter = "</b></em></td></tr><tr><td>" |
|
124 marker_footer = "</td></tr></table>" |
|
125 |
|
126 # Header location header/footer. |
|
127 header_location_header = '<table align=center width="87%"><tr><td>' |
|
128 header_location_footer = "</td></tr></table><br>" |
|
129 |
|
130 # Source code extracts header/footer. |
|
131 source_header = '<table align=center width="87%"><tr bgcolor="#D6E8FF"><td><pre>\n' |
|
132 source_footer = "\n</pre></table><br>" |
|
133 |
|
134 # Chapter header/inter/footer. |
|
135 chapter_header = '<br><table align=center width="75%"><tr><td><h2>' |
|
136 chapter_inter = '</h2><ul class="empty"><li>' |
|
137 chapter_footer = '</li></ul></td></tr></table>' |
|
138 |
|
139 # Index footer. |
|
140 index_footer_start = """\ |
|
141 <hr> |
|
142 <table><tr><td width="100%"></td> |
|
143 <td><font size=-2>[<a href="\ |
|
144 """ |
|
145 index_footer_end = """\ |
|
146 ">TOC</a>]</font></td></tr></table> |
|
147 """ |
|
148 |
|
149 # TOC footer. |
|
150 toc_footer_start = """\ |
|
151 <hr> |
|
152 <table><tr><td><font size=-2>[<a href="\ |
|
153 """ |
|
154 toc_footer_end = """\ |
|
155 ">Index</a>]</font></td> |
|
156 <td width="100%"></td> |
|
157 </tr></table> |
|
158 """ |
|
159 |
|
160 |
|
161 # source language keyword coloration/styling |
|
162 keyword_prefix = '<span class="keyword">' |
|
163 keyword_suffix = '</span>' |
|
164 |
|
165 section_synopsis_header = '<h2>Synopsis</h2>' |
|
166 section_synopsis_footer = '' |
|
167 |
|
168 |
|
169 # Translate a single line of source to HTML. This will convert |
|
170 # a "<" into "<.", ">" into ">.", etc. |
|
171 def html_quote( line ): |
|
172 result = string.replace( line, "&", "&" ) |
|
173 result = string.replace( result, "<", "<" ) |
|
174 result = string.replace( result, ">", ">" ) |
|
175 return result |
|
176 |
|
177 |
|
178 # same as 'html_quote', but ignores left and right brackets |
|
179 def html_quote0( line ): |
|
180 return string.replace( line, "&", "&" ) |
|
181 |
|
182 |
|
183 def dump_html_code( lines, prefix = "" ): |
|
184 # clean the last empty lines |
|
185 l = len( self.lines ) |
|
186 while l > 0 and string.strip( self.lines[l - 1] ) == "": |
|
187 l = l - 1 |
|
188 |
|
189 # The code footer should be directly appended to the last code |
|
190 # line to avoid an additional blank line. |
|
191 print prefix + code_header, |
|
192 for line in self.lines[0 : l + 1]: |
|
193 print '\n' + prefix + html_quote( line ), |
|
194 print prefix + code_footer, |
|
195 |
|
196 |
|
197 |
|
198 class HtmlFormatter( Formatter ): |
|
199 |
|
200 def __init__( self, processor, project_title, file_prefix ): |
|
201 Formatter.__init__( self, processor ) |
|
202 |
|
203 global html_header_1, html_header_2, html_header_3 |
|
204 global html_header_4, html_header_5, html_footer |
|
205 |
|
206 if file_prefix: |
|
207 file_prefix = file_prefix + "-" |
|
208 else: |
|
209 file_prefix = "" |
|
210 |
|
211 self.headers = processor.headers |
|
212 self.project_title = project_title |
|
213 self.file_prefix = file_prefix |
|
214 self.html_header = html_header_1 + project_title + \ |
|
215 html_header_2 + \ |
|
216 html_header_3 + file_prefix + "index.html" + \ |
|
217 html_header_4 + file_prefix + "toc.html" + \ |
|
218 html_header_5 + project_title + \ |
|
219 html_header_6 |
|
220 |
|
221 self.html_index_header = html_header_1 + project_title + \ |
|
222 html_header_2 + \ |
|
223 html_header_3i + file_prefix + "toc.html" + \ |
|
224 html_header_5 + project_title + \ |
|
225 html_header_6 |
|
226 |
|
227 self.html_toc_header = html_header_1 + project_title + \ |
|
228 html_header_2 + \ |
|
229 html_header_3 + file_prefix + "index.html" + \ |
|
230 html_header_5t + project_title + \ |
|
231 html_header_6 |
|
232 |
|
233 self.html_footer = "<center><font size=""-2"">generated on " + \ |
|
234 time.asctime( time.localtime( time.time() ) ) + \ |
|
235 "</font></center>" + html_footer |
|
236 |
|
237 self.columns = 3 |
|
238 |
|
239 def make_section_url( self, section ): |
|
240 return self.file_prefix + section.name + ".html" |
|
241 |
|
242 def make_block_url( self, block ): |
|
243 return self.make_section_url( block.section ) + "#" + block.name |
|
244 |
|
245 def make_html_words( self, words ): |
|
246 """ convert a series of simple words into some HTML text """ |
|
247 line = "" |
|
248 if words: |
|
249 line = html_quote( words[0] ) |
|
250 for w in words[1:]: |
|
251 line = line + " " + html_quote( w ) |
|
252 |
|
253 return line |
|
254 |
|
255 def make_html_word( self, word ): |
|
256 """analyze a simple word to detect cross-references and styling""" |
|
257 # look for cross-references |
|
258 m = re_crossref.match( word ) |
|
259 if m: |
|
260 try: |
|
261 name = m.group( 1 ) |
|
262 rest = m.group( 2 ) |
|
263 block = self.identifiers[name] |
|
264 url = self.make_block_url( block ) |
|
265 return '<a href="' + url + '">' + name + '</a>' + rest |
|
266 except: |
|
267 # we detected a cross-reference to an unknown item |
|
268 sys.stderr.write( \ |
|
269 "WARNING: undefined cross reference '" + name + "'.\n" ) |
|
270 return '?' + name + '?' + rest |
|
271 |
|
272 # look for italics and bolds |
|
273 m = re_italic.match( word ) |
|
274 if m: |
|
275 name = m.group( 1 ) |
|
276 rest = m.group( 3 ) |
|
277 return '<i>' + name + '</i>' + rest |
|
278 |
|
279 m = re_bold.match( word ) |
|
280 if m: |
|
281 name = m.group( 1 ) |
|
282 rest = m.group( 3 ) |
|
283 return '<b>' + name + '</b>' + rest |
|
284 |
|
285 return html_quote( word ) |
|
286 |
|
287 def make_html_para( self, words ): |
|
288 """ convert words of a paragraph into tagged HTML text, handle xrefs """ |
|
289 line = "" |
|
290 if words: |
|
291 line = self.make_html_word( words[0] ) |
|
292 for word in words[1:]: |
|
293 line = line + " " + self.make_html_word( word ) |
|
294 # convert `...' quotations into real left and right single quotes |
|
295 line = re.sub( r"(^|\W)`(.*?)'(\W|$)", \ |
|
296 r'\1‘\2’\3', \ |
|
297 line ) |
|
298 # convert tilde into non-breakable space |
|
299 line = string.replace( line, "~", " " ) |
|
300 |
|
301 return para_header + line + para_footer |
|
302 |
|
303 def make_html_code( self, lines ): |
|
304 """ convert a code sequence to HTML """ |
|
305 line = code_header + '\n' |
|
306 for l in lines: |
|
307 line = line + html_quote( l ) + '\n' |
|
308 |
|
309 return line + code_footer |
|
310 |
|
311 def make_html_items( self, items ): |
|
312 """ convert a field's content into some valid HTML """ |
|
313 lines = [] |
|
314 for item in items: |
|
315 if item.lines: |
|
316 lines.append( self.make_html_code( item.lines ) ) |
|
317 else: |
|
318 lines.append( self.make_html_para( item.words ) ) |
|
319 |
|
320 return string.join( lines, '\n' ) |
|
321 |
|
322 def print_html_items( self, items ): |
|
323 print self.make_html_items( items ) |
|
324 |
|
325 def print_html_field( self, field ): |
|
326 if field.name: |
|
327 print "<table><tr valign=top><td><b>" + field.name + "</b></td><td>" |
|
328 |
|
329 print self.make_html_items( field.items ) |
|
330 |
|
331 if field.name: |
|
332 print "</td></tr></table>" |
|
333 |
|
334 def html_source_quote( self, line, block_name = None ): |
|
335 result = "" |
|
336 while line: |
|
337 m = re_source_crossref.match( line ) |
|
338 if m: |
|
339 name = m.group( 2 ) |
|
340 prefix = html_quote( m.group( 1 ) ) |
|
341 length = len( m.group( 0 ) ) |
|
342 |
|
343 if name == block_name: |
|
344 # this is the current block name, if any |
|
345 result = result + prefix + '<b>' + name + '</b>' |
|
346 elif re_source_keywords.match( name ): |
|
347 # this is a C keyword |
|
348 result = result + prefix + keyword_prefix + name + keyword_suffix |
|
349 elif self.identifiers.has_key( name ): |
|
350 # this is a known identifier |
|
351 block = self.identifiers[name] |
|
352 result = result + prefix + '<a href="' + \ |
|
353 self.make_block_url( block ) + '">' + name + '</a>' |
|
354 else: |
|
355 result = result + html_quote( line[:length] ) |
|
356 |
|
357 line = line[length:] |
|
358 else: |
|
359 result = result + html_quote( line ) |
|
360 line = [] |
|
361 |
|
362 return result |
|
363 |
|
364 def print_html_field_list( self, fields ): |
|
365 print "<p></p>" |
|
366 print "<table cellpadding=3 border=0>" |
|
367 for field in fields: |
|
368 if len( field.name ) > 22: |
|
369 print "<tr valign=top><td colspan=0><b>" + field.name + "</b></td></tr>" |
|
370 print "<tr valign=top><td></td><td>" |
|
371 else: |
|
372 print "<tr valign=top><td><b>" + field.name + "</b></td><td>" |
|
373 |
|
374 self.print_html_items( field.items ) |
|
375 print "</td></tr>" |
|
376 print "</table>" |
|
377 |
|
378 def print_html_markup( self, markup ): |
|
379 table_fields = [] |
|
380 for field in markup.fields: |
|
381 if field.name: |
|
382 # we begin a new series of field or value definitions, we |
|
383 # will record them in the 'table_fields' list before outputting |
|
384 # all of them as a single table |
|
385 # |
|
386 table_fields.append( field ) |
|
387 else: |
|
388 if table_fields: |
|
389 self.print_html_field_list( table_fields ) |
|
390 table_fields = [] |
|
391 |
|
392 self.print_html_items( field.items ) |
|
393 |
|
394 if table_fields: |
|
395 self.print_html_field_list( table_fields ) |
|
396 |
|
397 # |
|
398 # Formatting the index |
|
399 # |
|
400 def index_enter( self ): |
|
401 print self.html_index_header |
|
402 self.index_items = {} |
|
403 |
|
404 def index_name_enter( self, name ): |
|
405 block = self.identifiers[name] |
|
406 url = self.make_block_url( block ) |
|
407 self.index_items[name] = url |
|
408 |
|
409 def index_exit( self ): |
|
410 # block_index already contains the sorted list of index names |
|
411 count = len( self.block_index ) |
|
412 rows = ( count + self.columns - 1 ) / self.columns |
|
413 |
|
414 print "<table align=center border=0 cellpadding=0 cellspacing=0>" |
|
415 for r in range( rows ): |
|
416 line = "<tr>" |
|
417 for c in range( self.columns ): |
|
418 i = r + c * rows |
|
419 if i < count: |
|
420 bname = self.block_index[r + c * rows] |
|
421 url = self.index_items[bname] |
|
422 line = line + '<td><a href="' + url + '">' + bname + '</a></td>' |
|
423 else: |
|
424 line = line + '<td></td>' |
|
425 line = line + "</tr>" |
|
426 print line |
|
427 |
|
428 print "</table>" |
|
429 |
|
430 print index_footer_start + \ |
|
431 self.file_prefix + "toc.html" + \ |
|
432 index_footer_end |
|
433 |
|
434 print self.html_footer |
|
435 |
|
436 self.index_items = {} |
|
437 |
|
438 def index_dump( self, index_filename = None ): |
|
439 if index_filename == None: |
|
440 index_filename = self.file_prefix + "index.html" |
|
441 |
|
442 Formatter.index_dump( self, index_filename ) |
|
443 |
|
444 # |
|
445 # Formatting the table of content |
|
446 # |
|
447 def toc_enter( self ): |
|
448 print self.html_toc_header |
|
449 print "<center><h1>Table of Contents</h1></center>" |
|
450 |
|
451 def toc_chapter_enter( self, chapter ): |
|
452 print chapter_header + string.join( chapter.title ) + chapter_inter |
|
453 print "<table cellpadding=5>" |
|
454 |
|
455 def toc_section_enter( self, section ): |
|
456 print '<tr valign=top><td class="left">' |
|
457 print '<a href="' + self.make_section_url( section ) + '">' + \ |
|
458 section.title + '</a></td><td>' |
|
459 |
|
460 print self.make_html_para( section.abstract ) |
|
461 |
|
462 def toc_section_exit( self, section ): |
|
463 print "</td></tr>" |
|
464 |
|
465 def toc_chapter_exit( self, chapter ): |
|
466 print "</table>" |
|
467 print chapter_footer |
|
468 |
|
469 def toc_index( self, index_filename ): |
|
470 print chapter_header + \ |
|
471 '<a href="' + index_filename + '">Global Index</a>' + \ |
|
472 chapter_inter + chapter_footer |
|
473 |
|
474 def toc_exit( self ): |
|
475 print toc_footer_start + \ |
|
476 self.file_prefix + "index.html" + \ |
|
477 toc_footer_end |
|
478 |
|
479 print self.html_footer |
|
480 |
|
481 def toc_dump( self, toc_filename = None, index_filename = None ): |
|
482 if toc_filename == None: |
|
483 toc_filename = self.file_prefix + "toc.html" |
|
484 |
|
485 if index_filename == None: |
|
486 index_filename = self.file_prefix + "index.html" |
|
487 |
|
488 Formatter.toc_dump( self, toc_filename, index_filename ) |
|
489 |
|
490 # |
|
491 # Formatting sections |
|
492 # |
|
493 def section_enter( self, section ): |
|
494 print self.html_header |
|
495 |
|
496 print section_title_header |
|
497 print section.title |
|
498 print section_title_footer |
|
499 |
|
500 maxwidth = 0 |
|
501 for b in section.blocks.values(): |
|
502 if len( b.name ) > maxwidth: |
|
503 maxwidth = len( b.name ) |
|
504 |
|
505 width = 70 # XXX magic number |
|
506 if maxwidth <> 0: |
|
507 # print section synopsis |
|
508 print section_synopsis_header |
|
509 print "<table align=center cellspacing=5 cellpadding=0 border=0>" |
|
510 |
|
511 columns = width / maxwidth |
|
512 if columns < 1: |
|
513 columns = 1 |
|
514 |
|
515 count = len( section.block_names ) |
|
516 rows = ( count + columns - 1 ) / columns |
|
517 |
|
518 for r in range( rows ): |
|
519 line = "<tr>" |
|
520 for c in range( columns ): |
|
521 i = r + c * rows |
|
522 line = line + '<td></td><td>' |
|
523 if i < count: |
|
524 name = section.block_names[i] |
|
525 line = line + '<a href="#' + name + '">' + name + '</a>' |
|
526 |
|
527 line = line + '</td>' |
|
528 line = line + "</tr>" |
|
529 print line |
|
530 |
|
531 print "</table><br><br>" |
|
532 print section_synopsis_footer |
|
533 |
|
534 print description_header |
|
535 print self.make_html_items( section.description ) |
|
536 print description_footer |
|
537 |
|
538 def block_enter( self, block ): |
|
539 print block_header |
|
540 |
|
541 # place html anchor if needed |
|
542 if block.name: |
|
543 print '<h4><a name="' + block.name + '">' + block.name + '</a></h4>' |
|
544 |
|
545 # dump the block C source lines now |
|
546 if block.code: |
|
547 header = '' |
|
548 for f in self.headers.keys(): |
|
549 if block.source.filename.find( f ) >= 0: |
|
550 header = self.headers[f] + ' (' + f + ')' |
|
551 break; |
|
552 |
|
553 # if not header: |
|
554 # sys.stderr.write( \ |
|
555 # 'WARNING: No header macro for ' + block.source.filename + '.\n' ) |
|
556 |
|
557 if header: |
|
558 print header_location_header |
|
559 print 'Defined in ' + header + '.' |
|
560 print header_location_footer |
|
561 |
|
562 print source_header |
|
563 for l in block.code: |
|
564 print self.html_source_quote( l, block.name ) |
|
565 print source_footer |
|
566 |
|
567 def markup_enter( self, markup, block ): |
|
568 if markup.tag == "description": |
|
569 print description_header |
|
570 else: |
|
571 print marker_header + markup.tag + marker_inter |
|
572 |
|
573 self.print_html_markup( markup ) |
|
574 |
|
575 def markup_exit( self, markup, block ): |
|
576 if markup.tag == "description": |
|
577 print description_footer |
|
578 else: |
|
579 print marker_footer |
|
580 |
|
581 def block_exit( self, block ): |
|
582 print block_footer_start + self.file_prefix + "index.html" + \ |
|
583 block_footer_middle + self.file_prefix + "toc.html" + \ |
|
584 block_footer_end |
|
585 |
|
586 def section_exit( self, section ): |
|
587 print html_footer |
|
588 |
|
589 def section_dump_all( self ): |
|
590 for section in self.sections: |
|
591 self.section_dump( section, self.file_prefix + section.name + '.html' ) |
|
592 |
|
593 # eof |
|