4 # A sub-class container of the `Formatter' class to produce HTML.
6 # Copyright 2002, 2003, 2005-2008, 2013, 2014 by
9 # This file is part of the FreeType project, and may only be used,
10 # modified, and distributed under the terms of the FreeType project
11 # license, LICENSE.TXT. By continuing to use, modify, or distribute
12 # this file you indicate that you have read the license and
13 # understand and accept it fully.
15 # The parent class is contained in file `formatter.py'.
20 from formatter import *
25 # The following strings define the HTML header used by all generated pages.
27 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
28 "http://www.w3.org/TR/html4/loose.dtd">
31 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
37 <style type="text/css">
38 a:link { color: #0000EF; }
39 a:visited { color: #51188E; }
40 a:hover { color: #FF0000; }
42 body { font-family: Verdana, Geneva, Arial, Helvetica, serif;
48 div.section { width: 75%;
50 div.section hr { margin: 4ex 0 1ex 0; }
51 div.section h4 { background-color: #EEEEFF;
55 margin: 3ex 0 1.5ex 9%;
56 padding: 0.3ex 0 0.3ex 1%; }
57 div.section p { margin: 1.5ex 0 1.5ex 10%; }
58 div.section pre { margin: 3ex 0 3ex 9%;
59 background-color: #D6E8FF;
60 padding: 2ex 0 2ex 1%; }
61 div.section table.fields { width: 90%;
62 margin: 1.5ex 0 1.5ex 10%; }
63 div.section table.toc { width: 95%;
64 margin: 1.5ex 0 1.5ex 5%; }
65 div.timestamp { text-align: center;
67 margin: 1.5ex 0 1.5ex 0; }
69 h1 { text-align: center; }
70 h3 { font-size: medium;
71 margin: 4ex 0 1.5ex 0; }
73 p { text-align: justify; }
75 pre.colored { color: blue; }
77 span.keyword { font-family: monospace;
82 table.fields td.val { font-weight: bold;
85 vertical-align: baseline;
86 padding: 1ex 1em 1ex 0; }
87 table.fields td.desc { vertical-align: baseline;
88 padding: 1ex 0 1ex 1em; }
89 table.fields td.desc p:first-child { margin: 0; }
90 table.fields td.desc p { margin: 1.5ex 0 0 0; }
91 table.index { margin: 6ex auto 6ex auto;
93 border-collapse: separate;
94 border-spacing: 1em 0.3ex; }
95 table.index tr { padding: 0; }
96 table.index td { padding: 0; }
97 table.index-toc-link { width: 100%;
100 margin: 1ex 0 1ex 0; }
101 table.index-toc-link td.left { padding: 0 0.5em 0 0.5em;
104 table.index-toc-link td.middle { padding: 0 0.5em 0 0.5em;
106 text-align: center; }
107 table.index-toc-link td.right { padding: 0 0.5em 0 0.5em;
110 table.synopsis { margin: 6ex auto 6ex auto;
112 border-collapse: separate;
113 border-spacing: 2em 0.6ex; }
114 table.synopsis tr { padding: 0; }
115 table.synopsis td { padding: 0; }
116 table.toc td.link { width: 30%;
118 vertical-align: baseline;
119 padding: 1ex 1em 1ex 0; }
120 table.toc td.desc { vertical-align: baseline;
121 padding: 1ex 0 1ex 1em;
123 table.toc td.desc p:first-child { margin: 0;
125 table.toc td.desc p { margin: 1.5ex 0 0 0;
134 <table class="index-toc-link"><tr><td class="left">[<a href="\
138 <table class="index-toc-link"><tr><td class="right">[<a href="\
142 ">Index</a>]</td><td class="right">[<a href="\
145 html_header_5t = """\
146 ">TOC</a>]</td></tr></table>
150 html_header_5i = """\
151 ">Index</a>]</td></tr></table>
160 # The HTML footer used by all generated pages.
166 # The header and footer used for each section.
167 section_title_header = "<h1>"
168 section_title_footer = "</h1>"
170 # The header and footer used for code segments.
171 code_header = '<pre class="colored">'
172 code_footer = '</pre>'
174 # Paragraph header and footer.
178 # Block header and footer.
179 block_header = '<div class="section">'
180 block_footer_start = """\
182 <table class="index-toc-link"><tr><td class="left">[<a href="\
184 block_footer_middle = """\
186 <td class="middle">[<a href="#">Top</a>]</td>\
187 <td class="right">[<a href="\
189 block_footer_end = """\
190 ">TOC</a>]</td></tr></table></div>
193 # Description header/footer.
194 description_header = ""
195 description_footer = ""
197 # Marker header/inter/footer combination.
198 marker_header = "<h4>"
199 marker_inter = "</h4>"
202 # Header location header/footer.
203 header_location_header = "<p>"
204 header_location_footer = "</p>"
206 # Source code extracts header/footer.
207 source_header = "<pre>"
208 source_footer = "</pre>"
210 # Chapter header/inter/footer.
211 chapter_header = """\
212 <div class="section">
215 chapter_inter = '</h2>'
216 chapter_footer = '</div>'
219 index_footer_start = """\
221 <table class="index-toc-link"><tr><td class="right">[<a href="\
223 index_footer_end = """\
224 ">TOC</a>]</td></tr></table>
228 toc_footer_start = """\
230 <table class="index-toc-link"><tr><td class="left">[<a href="\
232 toc_footer_end = """\
233 ">Index</a>]</td></tr></table>
237 # Source language keyword coloration and styling.
238 keyword_prefix = '<span class="keyword">'
239 keyword_suffix = '</span>'
241 section_synopsis_header = '<h2>Synopsis</h2>'
242 section_synopsis_footer = ''
245 # Translate a single line of source to HTML. This converts `<', `>', and
246 # `&' into `<',`>', and `&'.
248 def html_quote( line ):
249 result = string.replace( line, "&", "&" )
250 result = string.replace( result, "<", "<" )
251 result = string.replace( result, ">", ">" )
255 ################################################################
257 ## HTML FORMATTER CLASS
259 class HtmlFormatter( Formatter ):
261 def __init__( self, processor, project_title, file_prefix ):
262 Formatter.__init__( self, processor )
266 global html_header_3l, html_header_3r
268 global html_header_5t, html_header_5i
273 file_prefix = file_prefix + "-"
277 self.headers = processor.headers
278 self.project_title = project_title
279 self.file_prefix = file_prefix
281 html_header_1 + project_title
283 + html_header_3l + file_prefix + "index.html"
284 + html_header_4 + file_prefix + "toc.html"
285 + html_header_5t + project_title
287 self.html_index_header = (
288 html_header_1 + project_title
290 + html_header_3r + file_prefix + "toc.html"
291 + html_header_5t + project_title
293 self.html_toc_header = (
294 html_header_1 + project_title
296 + html_header_3l + file_prefix + "index.html"
297 + html_header_5i + project_title
300 '<div class="timestamp">generated on '
301 + time.asctime( time.localtime( time.time() ) )
302 + "</div>" + html_footer )
306 def make_section_url( self, section ):
307 return self.file_prefix + section.name + ".html"
309 def make_block_url( self, block, name = None ):
312 return self.make_section_url( block.section ) + "#" + name
314 def make_html_word( self, word ):
315 """Analyze a simple word to detect cross-references and markup."""
316 # handle cross-references
317 m = re_crossref.match( word )
322 block = self.identifiers[name]
323 url = self.make_block_url( block )
324 return '<a href="' + url + '">' + name + '</a>' + rest
326 # we detected a cross-reference to an unknown item
327 sys.stderr.write( "WARNING: undefined cross reference"
328 + " '" + name + "'.\n" )
329 return '?' + name + '?' + rest
331 # handle markup for italic and bold
332 m = re_italic.match( word )
336 return '<i>' + name + '</i>' + rest
338 m = re_bold.match( word )
342 return '<b>' + name + '</b>' + rest
344 return html_quote( word )
346 def make_html_para( self, words ):
347 """Convert words of a paragraph into tagged HTML text. Also handle
351 line = self.make_html_word( words[0] )
352 for word in words[1:]:
353 line = line + " " + self.make_html_word( word )
355 line = re_url.sub( r'<a href="\1">\1</a>', line )
356 # convert `...' quotations into real left and right single quotes
357 line = re.sub( r"(^|\W)`(.*?)'(\W|$)",
358 r'\1‘\2’\3',
360 # convert tilde into non-breakable space
361 line = string.replace( line, "~", " " )
363 return para_header + line + para_footer
365 def make_html_code( self, lines ):
366 """Convert a code sequence to HTML."""
367 line = code_header + '\n'
369 line = line + html_quote( l ) + '\n'
371 return line + code_footer
373 def make_html_items( self, items ):
374 """Convert a field's content into HTML."""
378 lines.append( self.make_html_code( item.lines ) )
380 lines.append( self.make_html_para( item.words ) )
382 return string.join( lines, '\n' )
384 def print_html_items( self, items ):
385 print self.make_html_items( items )
387 def print_html_field( self, field ):
389 print( '<table><tr valign="top"><td><b>'
393 print self.make_html_items( field.items )
396 print "</td></tr></table>"
398 def html_source_quote( self, line, block_name = None ):
401 m = re_source_crossref.match( line )
404 prefix = html_quote( m.group( 1 ) )
405 length = len( m.group( 0 ) )
407 if name == block_name:
408 # this is the current block name, if any
409 result = result + prefix + '<b>' + name + '</b>'
410 elif re_source_keywords.match( name ):
411 # this is a C keyword
412 result = ( result + prefix
413 + keyword_prefix + name + keyword_suffix )
414 elif name in self.identifiers:
415 # this is a known identifier
416 block = self.identifiers[name]
419 # link to a field ID if possible
420 for markup in block.markups:
421 if markup.tag == 'values':
422 for field in markup.fields:
426 result = ( result + prefix
428 + self.make_block_url( block, id )
429 + '">' + name + '</a>' )
431 result = result + html_quote( line[:length] )
435 result = result + html_quote( line )
440 def print_html_field_list( self, fields ):
441 print '<table class="fields">'
443 print ( '<tr><td class="val" id="' + field.name + '">'
445 + '</td><td class="desc">' )
446 self.print_html_items( field.items )
450 def print_html_markup( self, markup ):
452 for field in markup.fields:
454 # We begin a new series of field or value definitions. We
455 # record them in the `table_fields' list before outputting
456 # all of them as a single table.
457 table_fields.append( field )
460 self.print_html_field_list( table_fields )
463 self.print_html_items( field.items )
466 self.print_html_field_list( table_fields )
469 # formatting the index
471 def index_enter( self ):
472 print self.html_index_header
473 self.index_items = {}
475 def index_name_enter( self, name ):
476 block = self.identifiers[name]
477 url = self.make_block_url( block )
478 self.index_items[name] = url
480 def index_exit( self ):
481 # `block_index' already contains the sorted list of index names
482 count = len( self.block_index )
483 rows = ( count + self.columns - 1 ) // self.columns
485 print '<table class="index">'
486 for r in range( rows ):
488 for c in range( self.columns ):
491 bname = self.block_index[r + c * rows]
492 url = self.index_items[bname]
493 line = ( line + '<td><a href="' + url + '">'
494 + bname + '</a></td>' )
496 line = line + '<td></td>'
497 line = line + "</tr>"
502 print( index_footer_start
503 + self.file_prefix + "toc.html"
506 print self.html_footer
508 self.index_items = {}
510 def index_dump( self, index_filename = None ):
511 if index_filename == None:
512 index_filename = self.file_prefix + "index.html"
514 Formatter.index_dump( self, index_filename )
517 # formatting the table of contents
519 def toc_enter( self ):
520 print self.html_toc_header
521 print "<h1>Table of Contents</h1>"
523 def toc_chapter_enter( self, chapter ):
524 print chapter_header + string.join( chapter.title ) + chapter_inter
525 print '<table class="toc">'
527 def toc_section_enter( self, section ):
528 print ( '<tr><td class="link">'
529 + '<a href="' + self.make_section_url( section ) + '">'
530 + section.title + '</a></td><td class="desc">' )
531 print self.make_html_para( section.abstract )
533 def toc_section_exit( self, section ):
536 def toc_chapter_exit( self, chapter ):
540 def toc_index( self, index_filename ):
541 print( chapter_header
542 + '<a href="' + index_filename + '">Global Index</a>'
543 + chapter_inter + chapter_footer )
545 def toc_exit( self ):
546 print( toc_footer_start
547 + self.file_prefix + "index.html"
550 print self.html_footer
552 def toc_dump( self, toc_filename = None, index_filename = None ):
553 if toc_filename == None:
554 toc_filename = self.file_prefix + "toc.html"
556 if index_filename == None:
557 index_filename = self.file_prefix + "index.html"
559 Formatter.toc_dump( self, toc_filename, index_filename )
562 # formatting sections
564 def section_enter( self, section ):
565 print self.html_header
567 print section_title_header + section.title + section_title_footer
570 for b in section.blocks.values():
571 if len( b.name ) > maxwidth:
572 maxwidth = len( b.name )
574 width = 70 # XXX magic number
576 # print section synopsis
577 print section_synopsis_header
578 print '<table class="synopsis">'
580 columns = width // maxwidth
584 count = len( section.block_names )
585 # don't handle last entry if it is empty
586 if section.block_names[-1] == "/empty/":
588 rows = ( count + columns - 1 ) // columns
590 for r in range( rows ):
592 for c in range( columns ):
596 name = section.block_names[i]
597 if name == "/empty/":
598 # it can happen that a complete row is empty, and
599 # without a proper `filler' the browser might
600 # collapse the row to a much smaller height (or
601 # even omit it completely)
602 line = line + " "
604 line = ( line + '<a href="#' + name + '">'
607 line = line + '</td>'
608 line = line + "</tr>"
612 print section_synopsis_footer
614 print description_header
615 print self.make_html_items( section.description )
616 print description_footer
618 def block_enter( self, block ):
621 # place html anchor if needed
623 print( '<h3 id="' + block.name + '">' + block.name + '</h3>' )
625 # dump the block C source lines now
628 for f in self.headers.keys():
629 if block.source.filename.find( f ) >= 0:
630 header = self.headers[f] + ' (' + f + ')'
635 # "WARNING: No header macro for"
636 # + " '" + block.source.filename + "'.\n" )
639 print ( header_location_header
640 + 'Defined in ' + header + '.'
641 + header_location_footer )
645 print self.html_source_quote( l, block.name )
648 def markup_enter( self, markup, block ):
649 if markup.tag == "description":
650 print description_header
652 print marker_header + markup.tag + marker_inter
654 self.print_html_markup( markup )
656 def markup_exit( self, markup, block ):
657 if markup.tag == "description":
658 print description_footer
662 def block_exit( self, block ):
663 print( block_footer_start + self.file_prefix + "index.html"
664 + block_footer_middle + self.file_prefix + "toc.html"
667 def section_exit( self, section ):
670 def section_dump_all( self ):
671 for section in self.sections:
672 self.section_dump( section,
673 self.file_prefix + section.name + '.html' )