1#  ToHTML (c) 2002, 2003, 2005, 2006, 2007, 2008
2#    David Turner <david@freetype.org>
3
4from sources import *
5from content import *
6from formatter import *
7
8import time
9
10
11# The following defines the HTML header used by all generated pages.
12html_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
21html_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
52html_header_3 = """
53<table align=center><tr><td><font size=-1>[<a href="\
54"""
55
56html_header_3i = """
57<table align=center><tr><td width="100%"></td>
58<td><font size=-1>[<a href="\
59"""
60
61html_header_4 = """\
62">Index</a>]</font></td>
63<td width="100%"></td>
64<td><font size=-1>[<a href="\
65"""
66
67html_header_5 = """\
68">TOC</a>]</font></td></tr></table>
69<center><h1>\
70"""
71
72html_header_5t = """\
73">Index</a>]</font></td>
74<td width="100%"></td></tr></table>
75<center><h1>\
76"""
77
78html_header_6 = """\
79 API Reference</h1></center>
80"""
81
82
83# The HTML footer used by all generated pages.
84html_footer = """\
85</body>
86</html>\
87"""
88
89# The header and footer used for each section.
90section_title_header = "<center><h1>"
91section_title_footer = "</h1></center>"
92
93# The header and footer used for code segments.
94code_header = '<pre class="colored">'
95code_footer = '</pre>'
96
97# Paragraph header and footer.
98para_header = "<p>"
99para_footer = "</p>"
100
101# Block header and footer.
102block_header        = '<table align=center width="75%"><tr><td>'
103block_footer_start  = """\
104</td></tr></table>
105<hr width="75%">
106<table align=center width="75%"><tr><td><font size=-2>[<a href="\
107"""
108block_footer_middle = """\
109">Index</a>]</font></td>
110<td width="100%"></td>
111<td><font size=-2>[<a href="\
112"""
113block_footer_end    = """\
114">TOC</a>]</font></td></tr></table>
115"""
116
117# Description header/footer.
118description_header = '<table align=center width="87%"><tr><td>'
119description_footer = "</td></tr></table><br>"
120
121# Marker header/inter/footer combination.
122marker_header = '<table align=center width="87%" cellpadding=5><tr bgcolor="#EEEEFF"><td><em><b>'
123marker_inter  = "</b></em></td></tr><tr><td>"
124marker_footer = "</td></tr></table>"
125
126# Header location header/footer.
127header_location_header = '<table align=center width="87%"><tr><td>'
128header_location_footer = "</td></tr></table><br>"
129
130# Source code extracts header/footer.
131source_header = '<table align=center width="87%"><tr bgcolor="#D6E8FF"><td><pre>\n'
132source_footer = "\n</pre></table><br>"
133
134# Chapter header/inter/footer.
135chapter_header = '<br><table align=center width="75%"><tr><td><h2>'
136chapter_inter  = '</h2><ul class="empty"><li>'
137chapter_footer = '</li></ul></td></tr></table>'
138
139# Index footer.
140index_footer_start = """\
141<hr>
142<table><tr><td width="100%"></td>
143<td><font size=-2>[<a href="\
144"""
145index_footer_end = """\
146">TOC</a>]</font></td></tr></table>
147"""
148
149# TOC footer.
150toc_footer_start = """\
151<hr>
152<table><tr><td><font size=-2>[<a href="\
153"""
154toc_footer_end = """\
155">Index</a>]</font></td>
156<td width="100%"></td>
157</tr></table>
158"""
159
160
161# source language keyword coloration/styling
162keyword_prefix = '<span class="keyword">'
163keyword_suffix = '</span>'
164
165section_synopsis_header = '<h2>Synopsis</h2>'
166section_synopsis_footer = ''
167
168
169# Translate a single line of source to HTML.  This will convert
170# a "<" into "&lt.", ">" into "&gt.", etc.
171def  html_quote( line ):
172    result = string.replace( line, "&", "&amp;" )
173    result = string.replace( result, "<", "&lt;" )
174    result = string.replace( result, ">", "&gt;" )
175    return result
176
177
178# same as 'html_quote', but ignores left and right brackets
179def  html_quote0( line ):
180    return string.replace( line, "&", "&amp;" )
181
182
183def  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
198class  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&lsquo;\2&rsquo;\3', \
297                           line )
298            # convert tilde into non-breakable space
299            line = string.replace( line, "~", "&nbsp;" )
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
594