Home | History | Annotate | Download | only in docmaker
      1 #
      2 #  tohtml.py
      3 #
      4 #    A sub-class container of the `Formatter' class to produce HTML.
      5 #
      6 #  Copyright 2002-2018 by
      7 #  David Turner.
      8 #
      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.
     14 
     15 # The parent class is contained in file `formatter.py'.
     16 
     17 
     18 from sources import *
     19 from content import *
     20 from formatter import *
     21 
     22 import time
     23 
     24 
     25 # The following strings define the HTML header used by all generated pages.
     26 html_header_1 = """\
     27 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
     28 "https://www.w3.org/TR/html4/loose.dtd">
     29 <html>
     30 <head>
     31 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
     32 <title>\
     33 """
     34 
     35 html_header_2 = """\
     36  API Reference</title>
     37 <style type="text/css">
     38   a:link { color: #0000EF; }
     39   a:visited { color: #51188E; }
     40   a:hover { color: #FF0000; }
     41 
     42   body { font-family: Verdana, Geneva, Arial, Helvetica, serif;
     43          color: #000000;
     44          background: #FFFFFF;
     45          width: 87%;
     46          margin: auto; }
     47 
     48   div.section { width: 75%;
     49                 margin: auto; }
     50   div.section hr { margin: 4ex 0 1ex 0; }
     51   div.section h4 { background-color: #EEEEFF;
     52                    font-size: medium;
     53                    font-style: oblique;
     54                    font-weight: bold;
     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;
     66                   font-size: 69%;
     67                   margin: 1.5ex 0 1.5ex 0; }
     68 
     69   h1 { text-align: center; }
     70   h3 { font-size: medium;
     71        margin: 4ex 0 1.5ex 0; }
     72 
     73   p { text-align: justify; }
     74 
     75   pre.colored { color: blue; }
     76 
     77   span.keyword { font-family: monospace;
     78                  text-align: left;
     79                  white-space: pre;
     80                  color: darkblue; }
     81 
     82   table.fields td.val { font-weight: bold;
     83                         text-align: right;
     84                         width: 30%;
     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;
     92                 border: 0;
     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%;
     98                          border: 0;
     99                          border-spacing: 0;
    100                          margin: 1ex 0 1ex 0; }
    101   table.index-toc-link td.left { padding: 0 0.5em 0 0.5em;
    102                                  font-size: 83%;
    103                                  text-align: left; }
    104   table.index-toc-link td.middle { padding: 0 0.5em 0 0.5em;
    105                                    font-size: 83%;
    106                                    text-align: center; }
    107   table.index-toc-link td.right { padding: 0 0.5em 0 0.5em;
    108                                   font-size: 83%;
    109                                   text-align: right; }
    110   table.synopsis { margin: 6ex auto 6ex auto;
    111                    border: 0;
    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%;
    117                       text-align: right;
    118                       vertical-align: baseline;
    119                       padding: 1ex 1em 1ex 0; }
    120   table.toc td.desc { vertical-align: baseline;
    121                       padding: 1ex 0 1ex 1em;
    122                       text-align: left; }
    123   table.toc td.desc p:first-child { margin: 0;
    124                                     text-align: left; }
    125   table.toc td.desc p { margin: 1.5ex 0 0 0;
    126                         text-align: left; }
    127 
    128 </style>
    129 </head>
    130 <body>
    131 """
    132 
    133 html_header_3l = """
    134 <table class="index-toc-link"><tr><td class="left">[<a href="\
    135 """
    136 
    137 html_header_3r = """
    138 <table class="index-toc-link"><tr><td class="right">[<a href="\
    139 """
    140 
    141 html_header_4 = """\
    142 ">Index</a>]</td><td class="right">[<a href="\
    143 """
    144 
    145 html_header_5t = """\
    146 ">TOC</a>]</td></tr></table>
    147 <h1>\
    148 """
    149 
    150 html_header_5i = """\
    151 ">Index</a>]</td></tr></table>
    152 <h1>\
    153 """
    154 
    155 html_header_6 = """\
    156  API Reference</h1>
    157 """
    158 
    159 
    160 # The HTML footer used by all generated pages.
    161 html_footer = """\
    162 </body>
    163 </html>\
    164 """
    165 
    166 # The header and footer used for each section.
    167 section_title_header1 = '<h1 id="'
    168 section_title_header2 = '">'
    169 section_title_footer = "</h1>"
    170 
    171 # The header and footer used for code segments.
    172 code_header = '<pre class="colored">'
    173 code_footer = '</pre>'
    174 
    175 # Paragraph header and footer.
    176 para_header = "<p>"
    177 para_footer = "</p>"
    178 
    179 # Block header and footer.
    180 block_header        = '<div class="section">'
    181 block_footer_start  = """\
    182 <hr>
    183 <table class="index-toc-link"><tr><td class="left">[<a href="\
    184 """
    185 block_footer_middle = """\
    186 ">Index</a>]</td>\
    187 <td class="middle">[<a href="#">Top</a>]</td>\
    188 <td class="right">[<a href="\
    189 """
    190 block_footer_end    = """\
    191 ">TOC</a>]</td></tr></table></div>
    192 """
    193 
    194 # Description header/footer.
    195 description_header = ""
    196 description_footer = ""
    197 
    198 # Marker header/inter/footer combination.
    199 marker_header = "<h4>"
    200 marker_inter  = "</h4>"
    201 marker_footer = ""
    202 
    203 # Header location header/footer.
    204 header_location_header = "<p>"
    205 header_location_footer = "</p>"
    206 
    207 # Source code extracts header/footer.
    208 source_header = "<pre>"
    209 source_footer = "</pre>"
    210 
    211 # Chapter header/inter/footer.
    212 chapter_header = """\
    213 <div class="section">
    214 <h2>\
    215 """
    216 chapter_inter  = '</h2>'
    217 chapter_footer = '</div>'
    218 
    219 # Index footer.
    220 index_footer_start = """\
    221 <hr>
    222 <table class="index-toc-link"><tr><td class="right">[<a href="\
    223 """
    224 index_footer_end = """\
    225 ">TOC</a>]</td></tr></table>
    226 """
    227 
    228 # TOC footer.
    229 toc_footer_start = """\
    230 <hr>
    231 <table class="index-toc-link"><tr><td class="left">[<a href="\
    232 """
    233 toc_footer_end = """\
    234 ">Index</a>]</td></tr></table>
    235 """
    236 
    237 
    238 # Source language keyword coloration and styling.
    239 keyword_prefix = '<span class="keyword">'
    240 keyword_suffix = '</span>'
    241 
    242 section_synopsis_header = '<h2>Synopsis</h2>'
    243 section_synopsis_footer = ''
    244 
    245 
    246 # Translate a single line of source to HTML.  This converts `<', `>', and
    247 # `&' into `&lt;',`&gt;', and `&amp;'.
    248 #
    249 def  html_quote( line ):
    250     result = string.replace( line,   "&", "&amp;" )
    251     result = string.replace( result, "<", "&lt;"  )
    252     result = string.replace( result, ">", "&gt;"  )
    253     return result
    254 
    255 
    256 ################################################################
    257 ##
    258 ##  HTML FORMATTER CLASS
    259 ##
    260 class  HtmlFormatter( Formatter ):
    261 
    262     def  __init__( self, processor, project_title, file_prefix ):
    263         Formatter.__init__( self, processor )
    264 
    265         global html_header_1
    266         global html_header_2
    267         global html_header_3l, html_header_3r
    268         global html_header_4
    269         global html_header_5t, html_header_5i
    270         global html_header_6
    271         global html_footer
    272 
    273         if file_prefix:
    274             file_prefix = file_prefix + "-"
    275         else:
    276             file_prefix = ""
    277 
    278         self.headers       = processor.headers
    279         self.project_title = project_title
    280         self.file_prefix   = file_prefix
    281         self.html_header   = (
    282           html_header_1 + project_title
    283           + html_header_2
    284           + html_header_3l + file_prefix + "index.html"
    285           + html_header_4 + file_prefix + "toc.html"
    286           + html_header_5t + project_title
    287           + html_header_6 )
    288         self.html_index_header = (
    289           html_header_1 + project_title
    290           + html_header_2
    291           + html_header_3r + file_prefix + "toc.html"
    292           + html_header_5t + project_title
    293           + html_header_6 )
    294         self.html_toc_header = (
    295           html_header_1 + project_title
    296           + html_header_2
    297           + html_header_3l + file_prefix + "index.html"
    298           + html_header_5i + project_title
    299           + html_header_6 )
    300         self.html_footer = (
    301           '<div class="timestamp">generated on '
    302           + time.asctime( time.localtime( time.time() ) )
    303           + "</div>" + html_footer )
    304 
    305         self.columns = 3
    306 
    307     def  make_section_url( self, section ):
    308         return self.file_prefix + section.name + ".html"
    309 
    310     def  make_block_url( self, block, name = None ):
    311         if name == None:
    312             name = block.name
    313 
    314         try:
    315             section_url = self.make_section_url( block.section )
    316         except:
    317             # we already have a section
    318             section_url = self.make_section_url( block )
    319 
    320         return section_url + "#" + name
    321 
    322     def  make_html_word( self, word ):
    323         """Analyze a simple word to detect cross-references and markup."""
    324         # handle cross-references
    325         m = re_crossref.match( word )
    326         if m:
    327             try:
    328                 name = m.group( 'name' )
    329                 rest = m.group( 'rest' )
    330                 block = self.identifiers[name]
    331                 url   = self.make_block_url( block )
    332                 # display `foo[bar]' as `foo'
    333                 name = re.sub( r'\[.*\]', '', name )
    334                 # normalize url, following RFC 3986
    335                 url = string.replace( url, "[", "(" )
    336                 url = string.replace( url, "]", ")" )
    337 
    338                 try:
    339                     # for sections, display title
    340                     url = ( '&lsquo;<a href="' + url + '">'
    341                             + block.title + '</a>&rsquo;'
    342                             + rest )
    343                 except:
    344                     url = ( '<a href="' + url + '">'
    345                             + name + '</a>'
    346                             + rest )
    347 
    348                 return url
    349             except:
    350                 # we detected a cross-reference to an unknown item
    351                 sys.stderr.write( "WARNING: undefined cross reference"
    352                                   + " '" + name + "'.\n" )
    353                 return '?' + name + '?' + rest
    354 
    355         # handle markup for italic and bold
    356         m = re_italic.match( word )
    357         if m:
    358             name = m.group( 1 )
    359             rest = m.group( 2 )
    360             return '<i>' + name + '</i>' + rest
    361 
    362         m = re_bold.match( word )
    363         if m:
    364             name = m.group( 1 )
    365             rest = m.group( 2 )
    366             return '<b>' + name + '</b>' + rest
    367 
    368         return html_quote( word )
    369 
    370     def  make_html_para( self, words ):
    371         """Convert words of a paragraph into tagged HTML text.  Also handle
    372            cross references."""
    373         line = ""
    374         if words:
    375             line = self.make_html_word( words[0] )
    376             for word in words[1:]:
    377                 line = line + " " + self.make_html_word( word )
    378             # handle hyperlinks
    379             line = re_url.sub( r'<a href="\1">\1</a>', line )
    380             # convert `...' quotations into real left and right single quotes
    381             line = re.sub( r"(^|\W)`(.*?)'(\W|$)",
    382                            r'\1&lsquo;\2&rsquo;\3',
    383                            line )
    384             # convert tilde into non-breakable space
    385             line = string.replace( line, "~", "&nbsp;" )
    386 
    387         return para_header + line + para_footer
    388 
    389     def  make_html_code( self, lines ):
    390         """Convert a code sequence to HTML."""
    391         line = code_header + '\n'
    392         for l in lines:
    393             line = line + html_quote( l ).rstrip() + '\n'
    394 
    395         return line + code_footer
    396 
    397     def  make_html_items( self, items ):
    398         """Convert a field's content into HTML."""
    399         lines = []
    400         for item in items:
    401             if item.lines:
    402                 lines.append( self.make_html_code( item.lines ) )
    403             else:
    404                 lines.append( self.make_html_para( item.words ) )
    405 
    406         return string.join( lines, '\n' )
    407 
    408     def  print_html_items( self, items ):
    409         print self.make_html_items( items )
    410 
    411     def  print_html_field( self, field ):
    412         if field.name:
    413             print( '<table><tr valign="top"><td><b>'
    414                    + field.name
    415                    + "</b></td><td>" )
    416 
    417         print self.make_html_items( field.items )
    418 
    419         if field.name:
    420             print "</td></tr></table>"
    421 
    422     def  html_source_quote( self, line, block_name = None ):
    423         result = ""
    424         while line:
    425             m = re_source_crossref.match( line )
    426             if m:
    427                 name   = m.group( 2 )
    428                 prefix = html_quote( m.group( 1 ) )
    429                 length = len( m.group( 0 ) )
    430 
    431                 if name == block_name:
    432                     # this is the current block name, if any
    433                     result = result + prefix + '<b>' + name + '</b>'
    434                 elif re_source_keywords.match( name ):
    435                     # this is a C keyword
    436                     result = ( result + prefix
    437                                + keyword_prefix + name + keyword_suffix )
    438                 elif name in self.identifiers:
    439                     # this is a known identifier
    440                     block = self.identifiers[name]
    441                     id = block.name
    442 
    443                     # link to a field ID if possible
    444                     try:
    445                       for markup in block.markups:
    446                           if markup.tag == 'values':
    447                               for field in markup.fields:
    448                                   if field.name:
    449                                       id = name
    450 
    451                       result = ( result + prefix
    452                                  + '<a href="'
    453                                  + self.make_block_url( block, id )
    454                                  + '">' + name + '</a>' )
    455                     except:
    456                       # sections don't have `markups'; however, we don't
    457                       # want references to sections here anyway
    458                       result = result + html_quote( line[:length] )
    459 
    460                 else:
    461                     result = result + html_quote( line[:length] )
    462 
    463                 line = line[length:]
    464             else:
    465                 result = result + html_quote( line )
    466                 line   = []
    467 
    468         return result
    469 
    470     def  print_html_field_list( self, fields ):
    471         print '<table class="fields">'
    472         for field in fields:
    473             print ( '<tr><td class="val" id="' + field.name + '">'
    474                     + field.name
    475                     + '</td><td class="desc">' )
    476             self.print_html_items( field.items )
    477             print "</td></tr>"
    478         print "</table>"
    479 
    480     def  print_html_markup( self, markup ):
    481         table_fields = []
    482         for field in markup.fields:
    483             if field.name:
    484                 # We begin a new series of field or value definitions.  We
    485                 # record them in the `table_fields' list before outputting
    486                 # all of them as a single table.
    487                 table_fields.append( field )
    488             else:
    489                 if table_fields:
    490                     self.print_html_field_list( table_fields )
    491                     table_fields = []
    492 
    493                 self.print_html_items( field.items )
    494 
    495         if table_fields:
    496             self.print_html_field_list( table_fields )
    497 
    498     #
    499     # formatting the index
    500     #
    501     def  index_enter( self ):
    502         print self.html_index_header
    503         self.index_items = {}
    504 
    505     def  index_name_enter( self, name ):
    506         block = self.identifiers[name]
    507         url   = self.make_block_url( block )
    508         self.index_items[name] = url
    509 
    510     def  index_exit( self ):
    511         # `block_index' already contains the sorted list of index names
    512         count = len( self.block_index )
    513         rows  = ( count + self.columns - 1 ) // self.columns
    514 
    515         print '<table class="index">'
    516         for r in range( rows ):
    517             line = "<tr>"
    518             for c in range( self.columns ):
    519                 i = r + c * rows
    520                 if i < count:
    521                     bname = self.block_index[r + c * rows]
    522                     url   = self.index_items[bname]
    523                     # display `foo[bar]' as `foo (bar)'
    524                     bname = string.replace( bname, "[", " (" )
    525                     bname = string.replace( bname, "]", ")"  )
    526                     # normalize url, following RFC 3986
    527                     url = string.replace( url, "[", "(" )
    528                     url = string.replace( url, "]", ")" )
    529                     line  = ( line + '<td><a href="' + url + '">'
    530                               + bname + '</a></td>' )
    531                 else:
    532                     line = line + '<td></td>'
    533             line = line + "</tr>"
    534             print line
    535 
    536         print "</table>"
    537 
    538         print( index_footer_start
    539                + self.file_prefix + "toc.html"
    540                + index_footer_end )
    541 
    542         print self.html_footer
    543 
    544         self.index_items = {}
    545 
    546     def  index_dump( self, index_filename = None ):
    547         if index_filename == None:
    548             index_filename = self.file_prefix + "index.html"
    549 
    550         Formatter.index_dump( self, index_filename )
    551 
    552     #
    553     # formatting the table of contents
    554     #
    555     def  toc_enter( self ):
    556         print self.html_toc_header
    557         print "<h1>Table of Contents</h1>"
    558 
    559     def  toc_chapter_enter( self, chapter ):
    560         print chapter_header + string.join( chapter.title ) + chapter_inter
    561         print '<table class="toc">'
    562 
    563     def  toc_section_enter( self, section ):
    564         print ( '<tr><td class="link">'
    565                 + '<a href="' + self.make_section_url( section ) + '">'
    566                 + section.title + '</a></td><td class="desc">' )
    567         print self.make_html_para( section.abstract )
    568 
    569     def  toc_section_exit( self, section ):
    570         print "</td></tr>"
    571 
    572     def  toc_chapter_exit( self, chapter ):
    573         print "</table>"
    574         print chapter_footer
    575 
    576     def  toc_index( self, index_filename ):
    577         print( chapter_header
    578                + '<a href="' + index_filename + '">Global Index</a>'
    579                + chapter_inter + chapter_footer )
    580 
    581     def  toc_exit( self ):
    582         print( toc_footer_start
    583                + self.file_prefix + "index.html"
    584                + toc_footer_end )
    585 
    586         print self.html_footer
    587 
    588     def  toc_dump( self, toc_filename = None, index_filename = None ):
    589         if toc_filename == None:
    590             toc_filename = self.file_prefix + "toc.html"
    591 
    592         if index_filename == None:
    593             index_filename = self.file_prefix + "index.html"
    594 
    595         Formatter.toc_dump( self, toc_filename, index_filename )
    596 
    597     #
    598     # formatting sections
    599     #
    600     def  section_enter( self, section ):
    601         print self.html_header
    602 
    603         print ( section_title_header1 + section.name + section_title_header2
    604                 + section.title
    605                 + section_title_footer )
    606 
    607         maxwidth = 0
    608         for b in section.blocks.values():
    609             if len( b.name ) > maxwidth:
    610                 maxwidth = len( b.name )
    611 
    612         width = 70  # XXX magic number
    613         if maxwidth > 0:
    614             # print section synopsis
    615             print section_synopsis_header
    616             print '<table class="synopsis">'
    617 
    618             columns = width // maxwidth
    619             if columns < 1:
    620                 columns = 1
    621 
    622             count = len( section.block_names )
    623             # don't handle last entry if it is empty
    624             if section.block_names[-1] == "/empty/":
    625                 count -= 1
    626             rows  = ( count + columns - 1 ) // columns
    627 
    628             for r in range( rows ):
    629                 line = "<tr>"
    630                 for c in range( columns ):
    631                     i = r + c * rows
    632                     line = line + '<td>'
    633                     if i < count:
    634                         name = section.block_names[i]
    635                         if name == "/empty/":
    636                             # it can happen that a complete row is empty, and
    637                             # without a proper `filler' the browser might
    638                             # collapse the row to a much smaller height (or
    639                             # even omit it completely)
    640                             line = line + "&nbsp;"
    641                         else:
    642                             url = name
    643                             # display `foo[bar]' as `foo'
    644                             name = re.sub( r'\[.*\]', '', name )
    645                             # normalize url, following RFC 3986
    646                             url = string.replace( url, "[", "(" )
    647                             url = string.replace( url, "]", ")" )
    648                             line = ( line + '<a href="#' + url + '">'
    649                                      + name + '</a>' )
    650 
    651                     line = line + '</td>'
    652                 line = line + "</tr>"
    653                 print line
    654 
    655             print "</table>"
    656             print section_synopsis_footer
    657 
    658         print description_header
    659         print self.make_html_items( section.description )
    660         print description_footer
    661 
    662     def  block_enter( self, block ):
    663         print block_header
    664 
    665         # place html anchor if needed
    666         if block.name:
    667             url = block.name
    668             # display `foo[bar]' as `foo'
    669             name = re.sub( r'\[.*\]', '', block.name )
    670             # normalize url, following RFC 3986
    671             url = string.replace( url, "[", "(" )
    672             url = string.replace( url, "]", ")" )
    673             print( '<h3 id="' + url + '">' + name + '</h3>' )
    674 
    675         # dump the block C source lines now
    676         if block.code:
    677             header = ''
    678             for f in self.headers.keys():
    679                 if block.source.filename.find( f ) >= 0:
    680                     header = self.headers[f] + ' (' + f + ')'
    681                     break
    682 
    683 #           if not header:
    684 #               sys.stderr.write(
    685 #                 "WARNING: No header macro for"
    686 #                 + " '" + block.source.filename + "'.\n" )
    687 
    688             if header:
    689                 print ( header_location_header
    690                         + 'Defined in ' + header + '.'
    691                         + header_location_footer )
    692 
    693             print source_header
    694             for l in block.code:
    695                 print self.html_source_quote( l, block.name )
    696             print source_footer
    697 
    698     def  markup_enter( self, markup, block ):
    699         if markup.tag == "description":
    700             print description_header
    701         else:
    702             print marker_header + markup.tag + marker_inter
    703 
    704         self.print_html_markup( markup )
    705 
    706     def  markup_exit( self, markup, block ):
    707         if markup.tag == "description":
    708             print description_footer
    709         else:
    710             print marker_footer
    711 
    712     def  block_exit( self, block ):
    713         print( block_footer_start + self.file_prefix + "index.html"
    714                + block_footer_middle + self.file_prefix + "toc.html"
    715                + block_footer_end )
    716 
    717     def  section_exit( self, section ):
    718         print html_footer
    719 
    720     def  section_dump_all( self ):
    721         for section in self.sections:
    722             self.section_dump( section,
    723                                self.file_prefix + section.name + '.html' )
    724 
    725 # eof
    726