| # |
| # tohtml.py |
| # |
| # A sub-class container of the `Formatter' class to produce HTML. |
| # |
| # Copyright 2002-2015 by |
| # David Turner. |
| # |
| # This file is part of the FreeType project, and may only be used, |
| # modified, and distributed under the terms of the FreeType project |
| # license, LICENSE.TXT. By continuing to use, modify, or distribute |
| # this file you indicate that you have read the license and |
| # understand and accept it fully. |
| |
| # The parent class is contained in file `formatter.py'. |
| |
| |
| from sources import * |
| from content import * |
| from formatter import * |
| |
| import time |
| |
| |
| # The following strings define the HTML header used by all generated pages. |
| html_header_1 = """\ |
| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
| "http://www.w3.org/TR/html4/loose.dtd"> |
| <html> |
| <head> |
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> |
| <title>\ |
| """ |
| |
| html_header_2 = """\ |
| API Reference</title> |
| <style type="text/css"> |
| a:link { color: #0000EF; } |
| a:visited { color: #51188E; } |
| a:hover { color: #FF0000; } |
| |
| body { font-family: Verdana, Geneva, Arial, Helvetica, serif; |
| color: #000000; |
| background: #FFFFFF; |
| width: 87%; |
| margin: auto; } |
| |
| div.section { width: 75%; |
| margin: auto; } |
| div.section hr { margin: 4ex 0 1ex 0; } |
| div.section h4 { background-color: #EEEEFF; |
| font-size: medium; |
| font-style: oblique; |
| font-weight: bold; |
| margin: 3ex 0 1.5ex 9%; |
| padding: 0.3ex 0 0.3ex 1%; } |
| div.section p { margin: 1.5ex 0 1.5ex 10%; } |
| div.section pre { margin: 3ex 0 3ex 9%; |
| background-color: #D6E8FF; |
| padding: 2ex 0 2ex 1%; } |
| div.section table.fields { width: 90%; |
| margin: 1.5ex 0 1.5ex 10%; } |
| div.section table.toc { width: 95%; |
| margin: 1.5ex 0 1.5ex 5%; } |
| div.timestamp { text-align: center; |
| font-size: 69%; |
| margin: 1.5ex 0 1.5ex 0; } |
| |
| h1 { text-align: center; } |
| h3 { font-size: medium; |
| margin: 4ex 0 1.5ex 0; } |
| |
| p { text-align: justify; } |
| |
| pre.colored { color: blue; } |
| |
| span.keyword { font-family: monospace; |
| text-align: left; |
| white-space: pre; |
| color: darkblue; } |
| |
| table.fields td.val { font-weight: bold; |
| text-align: right; |
| width: 30%; |
| vertical-align: baseline; |
| padding: 1ex 1em 1ex 0; } |
| table.fields td.desc { vertical-align: baseline; |
| padding: 1ex 0 1ex 1em; } |
| table.fields td.desc p:first-child { margin: 0; } |
| table.fields td.desc p { margin: 1.5ex 0 0 0; } |
| table.index { margin: 6ex auto 6ex auto; |
| border: 0; |
| border-collapse: separate; |
| border-spacing: 1em 0.3ex; } |
| table.index tr { padding: 0; } |
| table.index td { padding: 0; } |
| table.index-toc-link { width: 100%; |
| border: 0; |
| border-spacing: 0; |
| margin: 1ex 0 1ex 0; } |
| table.index-toc-link td.left { padding: 0 0.5em 0 0.5em; |
| font-size: 83%; |
| text-align: left; } |
| table.index-toc-link td.middle { padding: 0 0.5em 0 0.5em; |
| font-size: 83%; |
| text-align: center; } |
| table.index-toc-link td.right { padding: 0 0.5em 0 0.5em; |
| font-size: 83%; |
| text-align: right; } |
| table.synopsis { margin: 6ex auto 6ex auto; |
| border: 0; |
| border-collapse: separate; |
| border-spacing: 2em 0.6ex; } |
| table.synopsis tr { padding: 0; } |
| table.synopsis td { padding: 0; } |
| table.toc td.link { width: 30%; |
| text-align: right; |
| vertical-align: baseline; |
| padding: 1ex 1em 1ex 0; } |
| table.toc td.desc { vertical-align: baseline; |
| padding: 1ex 0 1ex 1em; |
| text-align: left; } |
| table.toc td.desc p:first-child { margin: 0; |
| text-align: left; } |
| table.toc td.desc p { margin: 1.5ex 0 0 0; |
| text-align: left; } |
| |
| </style> |
| </head> |
| <body> |
| """ |
| |
| html_header_3l = """ |
| <table class="index-toc-link"><tr><td class="left">[<a href="\ |
| """ |
| |
| html_header_3r = """ |
| <table class="index-toc-link"><tr><td class="right">[<a href="\ |
| """ |
| |
| html_header_4 = """\ |
| ">Index</a>]</td><td class="right">[<a href="\ |
| """ |
| |
| html_header_5t = """\ |
| ">TOC</a>]</td></tr></table> |
| <h1>\ |
| """ |
| |
| html_header_5i = """\ |
| ">Index</a>]</td></tr></table> |
| <h1>\ |
| """ |
| |
| html_header_6 = """\ |
| API Reference</h1> |
| """ |
| |
| |
| # The HTML footer used by all generated pages. |
| html_footer = """\ |
| </body> |
| </html>\ |
| """ |
| |
| # The header and footer used for each section. |
| section_title_header = "<h1>" |
| section_title_footer = "</h1>" |
| |
| # The header and footer used for code segments. |
| code_header = '<pre class="colored">' |
| code_footer = '</pre>' |
| |
| # Paragraph header and footer. |
| para_header = "<p>" |
| para_footer = "</p>" |
| |
| # Block header and footer. |
| block_header = '<div class="section">' |
| block_footer_start = """\ |
| <hr> |
| <table class="index-toc-link"><tr><td class="left">[<a href="\ |
| """ |
| block_footer_middle = """\ |
| ">Index</a>]</td>\ |
| <td class="middle">[<a href="#">Top</a>]</td>\ |
| <td class="right">[<a href="\ |
| """ |
| block_footer_end = """\ |
| ">TOC</a>]</td></tr></table></div> |
| """ |
| |
| # Description header/footer. |
| description_header = "" |
| description_footer = "" |
| |
| # Marker header/inter/footer combination. |
| marker_header = "<h4>" |
| marker_inter = "</h4>" |
| marker_footer = "" |
| |
| # Header location header/footer. |
| header_location_header = "<p>" |
| header_location_footer = "</p>" |
| |
| # Source code extracts header/footer. |
| source_header = "<pre>" |
| source_footer = "</pre>" |
| |
| # Chapter header/inter/footer. |
| chapter_header = """\ |
| <div class="section"> |
| <h2>\ |
| """ |
| chapter_inter = '</h2>' |
| chapter_footer = '</div>' |
| |
| # Index footer. |
| index_footer_start = """\ |
| <hr> |
| <table class="index-toc-link"><tr><td class="right">[<a href="\ |
| """ |
| index_footer_end = """\ |
| ">TOC</a>]</td></tr></table> |
| """ |
| |
| # TOC footer. |
| toc_footer_start = """\ |
| <hr> |
| <table class="index-toc-link"><tr><td class="left">[<a href="\ |
| """ |
| toc_footer_end = """\ |
| ">Index</a>]</td></tr></table> |
| """ |
| |
| |
| # Source language keyword coloration and styling. |
| keyword_prefix = '<span class="keyword">' |
| keyword_suffix = '</span>' |
| |
| section_synopsis_header = '<h2>Synopsis</h2>' |
| section_synopsis_footer = '' |
| |
| |
| # Translate a single line of source to HTML. This converts `<', `>', and |
| # `&' into `<',`>', and `&'. |
| # |
| def html_quote( line ): |
| result = string.replace( line, "&", "&" ) |
| result = string.replace( result, "<", "<" ) |
| result = string.replace( result, ">", ">" ) |
| return result |
| |
| |
| ################################################################ |
| ## |
| ## HTML FORMATTER CLASS |
| ## |
| class HtmlFormatter( Formatter ): |
| |
| def __init__( self, processor, project_title, file_prefix ): |
| Formatter.__init__( self, processor ) |
| |
| global html_header_1 |
| global html_header_2 |
| global html_header_3l, html_header_3r |
| global html_header_4 |
| global html_header_5t, html_header_5i |
| global html_header_6 |
| global html_footer |
| |
| if file_prefix: |
| file_prefix = file_prefix + "-" |
| else: |
| file_prefix = "" |
| |
| self.headers = processor.headers |
| self.project_title = project_title |
| self.file_prefix = file_prefix |
| self.html_header = ( |
| html_header_1 + project_title |
| + html_header_2 |
| + html_header_3l + file_prefix + "index.html" |
| + html_header_4 + file_prefix + "toc.html" |
| + html_header_5t + project_title |
| + html_header_6 ) |
| self.html_index_header = ( |
| html_header_1 + project_title |
| + html_header_2 |
| + html_header_3r + file_prefix + "toc.html" |
| + html_header_5t + project_title |
| + html_header_6 ) |
| self.html_toc_header = ( |
| html_header_1 + project_title |
| + html_header_2 |
| + html_header_3l + file_prefix + "index.html" |
| + html_header_5i + project_title |
| + html_header_6 ) |
| self.html_footer = ( |
| '<div class="timestamp">generated on ' |
| + time.asctime( time.localtime( time.time() ) ) |
| + "</div>" + html_footer ) |
| |
| self.columns = 3 |
| |
| def make_section_url( self, section ): |
| return self.file_prefix + section.name + ".html" |
| |
| def make_block_url( self, block, name = None ): |
| if name == None: |
| name = block.name |
| return self.make_section_url( block.section ) + "#" + name |
| |
| def make_html_word( self, word ): |
| """Analyze a simple word to detect cross-references and markup.""" |
| # handle cross-references |
| m = re_crossref.match( word ) |
| if m: |
| try: |
| name = m.group( 1 ) |
| rest = m.group( 2 ) |
| block = self.identifiers[name] |
| url = self.make_block_url( block ) |
| return '<a href="' + url + '">' + name + '</a>' + rest |
| except: |
| # we detected a cross-reference to an unknown item |
| sys.stderr.write( "WARNING: undefined cross reference" |
| + " '" + name + "'.\n" ) |
| return '?' + name + '?' + rest |
| |
| # handle markup for italic and bold |
| m = re_italic.match( word ) |
| if m: |
| name = m.group( 1 ) |
| rest = m.group( 2 ) |
| return '<i>' + name + '</i>' + rest |
| |
| m = re_bold.match( word ) |
| if m: |
| name = m.group( 1 ) |
| rest = m.group( 2 ) |
| return '<b>' + name + '</b>' + rest |
| |
| return html_quote( word ) |
| |
| def make_html_para( self, words ): |
| """Convert words of a paragraph into tagged HTML text. Also handle |
| cross references.""" |
| line = "" |
| if words: |
| line = self.make_html_word( words[0] ) |
| for word in words[1:]: |
| line = line + " " + self.make_html_word( word ) |
| # handle hyperlinks |
| line = re_url.sub( r'<a href="\1">\1</a>', line ) |
| # convert `...' quotations into real left and right single quotes |
| line = re.sub( r"(^|\W)`(.*?)'(\W|$)", |
| r'\1‘\2’\3', |
| line ) |
| # convert tilde into non-breakable space |
| line = string.replace( line, "~", " " ) |
| |
| return para_header + line + para_footer |
| |
| def make_html_code( self, lines ): |
| """Convert a code sequence to HTML.""" |
| line = code_header + '\n' |
| for l in lines: |
| line = line + html_quote( l ) + '\n' |
| |
| return line + code_footer |
| |
| def make_html_items( self, items ): |
| """Convert a field's content into HTML.""" |
| lines = [] |
| for item in items: |
| if item.lines: |
| lines.append( self.make_html_code( item.lines ) ) |
| else: |
| lines.append( self.make_html_para( item.words ) ) |
| |
| return string.join( lines, '\n' ) |
| |
| def print_html_items( self, items ): |
| print self.make_html_items( items ) |
| |
| def print_html_field( self, field ): |
| if field.name: |
| print( '<table><tr valign="top"><td><b>' |
| + field.name |
| + "</b></td><td>" ) |
| |
| print self.make_html_items( field.items ) |
| |
| if field.name: |
| print "</td></tr></table>" |
| |
| def html_source_quote( self, line, block_name = None ): |
| result = "" |
| while line: |
| m = re_source_crossref.match( line ) |
| if m: |
| name = m.group( 2 ) |
| prefix = html_quote( m.group( 1 ) ) |
| length = len( m.group( 0 ) ) |
| |
| if name == block_name: |
| # this is the current block name, if any |
| result = result + prefix + '<b>' + name + '</b>' |
| elif re_source_keywords.match( name ): |
| # this is a C keyword |
| result = ( result + prefix |
| + keyword_prefix + name + keyword_suffix ) |
| elif name in self.identifiers: |
| # this is a known identifier |
| block = self.identifiers[name] |
| id = block.name |
| |
| # link to a field ID if possible |
| for markup in block.markups: |
| if markup.tag == 'values': |
| for field in markup.fields: |
| if field.name: |
| id = name |
| |
| result = ( result + prefix |
| + '<a href="' |
| + self.make_block_url( block, id ) |
| + '">' + name + '</a>' ) |
| else: |
| result = result + html_quote( line[:length] ) |
| |
| line = line[length:] |
| else: |
| result = result + html_quote( line ) |
| line = [] |
| |
| return result |
| |
| def print_html_field_list( self, fields ): |
| print '<table class="fields">' |
| for field in fields: |
| print ( '<tr><td class="val" id="' + field.name + '">' |
| + field.name |
| + '</td><td class="desc">' ) |
| self.print_html_items( field.items ) |
| print "</td></tr>" |
| print "</table>" |
| |
| def print_html_markup( self, markup ): |
| table_fields = [] |
| for field in markup.fields: |
| if field.name: |
| # We begin a new series of field or value definitions. We |
| # record them in the `table_fields' list before outputting |
| # all of them as a single table. |
| table_fields.append( field ) |
| else: |
| if table_fields: |
| self.print_html_field_list( table_fields ) |
| table_fields = [] |
| |
| self.print_html_items( field.items ) |
| |
| if table_fields: |
| self.print_html_field_list( table_fields ) |
| |
| # |
| # formatting the index |
| # |
| def index_enter( self ): |
| print self.html_index_header |
| self.index_items = {} |
| |
| def index_name_enter( self, name ): |
| block = self.identifiers[name] |
| url = self.make_block_url( block ) |
| self.index_items[name] = url |
| |
| def index_exit( self ): |
| # `block_index' already contains the sorted list of index names |
| count = len( self.block_index ) |
| rows = ( count + self.columns - 1 ) // self.columns |
| |
| print '<table class="index">' |
| for r in range( rows ): |
| line = "<tr>" |
| for c in range( self.columns ): |
| i = r + c * rows |
| if i < count: |
| bname = self.block_index[r + c * rows] |
| url = self.index_items[bname] |
| line = ( line + '<td><a href="' + url + '">' |
| + bname + '</a></td>' ) |
| else: |
| line = line + '<td></td>' |
| line = line + "</tr>" |
| print line |
| |
| print "</table>" |
| |
| print( index_footer_start |
| + self.file_prefix + "toc.html" |
| + index_footer_end ) |
| |
| print self.html_footer |
| |
| self.index_items = {} |
| |
| def index_dump( self, index_filename = None ): |
| if index_filename == None: |
| index_filename = self.file_prefix + "index.html" |
| |
| Formatter.index_dump( self, index_filename ) |
| |
| # |
| # formatting the table of contents |
| # |
| def toc_enter( self ): |
| print self.html_toc_header |
| print "<h1>Table of Contents</h1>" |
| |
| def toc_chapter_enter( self, chapter ): |
| print chapter_header + string.join( chapter.title ) + chapter_inter |
| print '<table class="toc">' |
| |
| def toc_section_enter( self, section ): |
| print ( '<tr><td class="link">' |
| + '<a href="' + self.make_section_url( section ) + '">' |
| + section.title + '</a></td><td class="desc">' ) |
| print self.make_html_para( section.abstract ) |
| |
| def toc_section_exit( self, section ): |
| print "</td></tr>" |
| |
| def toc_chapter_exit( self, chapter ): |
| print "</table>" |
| print chapter_footer |
| |
| def toc_index( self, index_filename ): |
| print( chapter_header |
| + '<a href="' + index_filename + '">Global Index</a>' |
| + chapter_inter + chapter_footer ) |
| |
| def toc_exit( self ): |
| print( toc_footer_start |
| + self.file_prefix + "index.html" |
| + toc_footer_end ) |
| |
| print self.html_footer |
| |
| def toc_dump( self, toc_filename = None, index_filename = None ): |
| if toc_filename == None: |
| toc_filename = self.file_prefix + "toc.html" |
| |
| if index_filename == None: |
| index_filename = self.file_prefix + "index.html" |
| |
| Formatter.toc_dump( self, toc_filename, index_filename ) |
| |
| # |
| # formatting sections |
| # |
| def section_enter( self, section ): |
| print self.html_header |
| |
| print section_title_header + section.title + section_title_footer |
| |
| maxwidth = 0 |
| for b in section.blocks.values(): |
| if len( b.name ) > maxwidth: |
| maxwidth = len( b.name ) |
| |
| width = 70 # XXX magic number |
| if maxwidth > 0: |
| # print section synopsis |
| print section_synopsis_header |
| print '<table class="synopsis">' |
| |
| columns = width // maxwidth |
| if columns < 1: |
| columns = 1 |
| |
| count = len( section.block_names ) |
| # don't handle last entry if it is empty |
| if section.block_names[-1] == "/empty/": |
| count -= 1 |
| rows = ( count + columns - 1 ) // columns |
| |
| for r in range( rows ): |
| line = "<tr>" |
| for c in range( columns ): |
| i = r + c * rows |
| line = line + '<td>' |
| if i < count: |
| name = section.block_names[i] |
| if name == "/empty/": |
| # it can happen that a complete row is empty, and |
| # without a proper `filler' the browser might |
| # collapse the row to a much smaller height (or |
| # even omit it completely) |
| line = line + " " |
| else: |
| line = ( line + '<a href="#' + name + '">' |
| + name + '</a>' ) |
| |
| line = line + '</td>' |
| line = line + "</tr>" |
| print line |
| |
| print "</table>" |
| print section_synopsis_footer |
| |
| print description_header |
| print self.make_html_items( section.description ) |
| print description_footer |
| |
| def block_enter( self, block ): |
| print block_header |
| |
| # place html anchor if needed |
| if block.name: |
| print( '<h3 id="' + block.name + '">' + block.name + '</h3>' ) |
| |
| # dump the block C source lines now |
| if block.code: |
| header = '' |
| for f in self.headers.keys(): |
| if block.source.filename.find( f ) >= 0: |
| header = self.headers[f] + ' (' + f + ')' |
| break |
| |
| # if not header: |
| # sys.stderr.write( |
| # "WARNING: No header macro for" |
| # + " '" + block.source.filename + "'.\n" ) |
| |
| if header: |
| print ( header_location_header |
| + 'Defined in ' + header + '.' |
| + header_location_footer ) |
| |
| print source_header |
| for l in block.code: |
| print self.html_source_quote( l, block.name ) |
| print source_footer |
| |
| def markup_enter( self, markup, block ): |
| if markup.tag == "description": |
| print description_header |
| else: |
| print marker_header + markup.tag + marker_inter |
| |
| self.print_html_markup( markup ) |
| |
| def markup_exit( self, markup, block ): |
| if markup.tag == "description": |
| print description_footer |
| else: |
| print marker_footer |
| |
| def block_exit( self, block ): |
| print( block_footer_start + self.file_prefix + "index.html" |
| + block_footer_middle + self.file_prefix + "toc.html" |
| + block_footer_end ) |
| |
| def section_exit( self, section ): |
| print html_footer |
| |
| def section_dump_all( self ): |
| for section in self.sections: |
| self.section_dump( section, |
| self.file_prefix + section.name + '.html' ) |
| |
| # eof |