Hi everyone,

  While exploring for the policy to be used for converting source code to
markdown (though most  of the necessary modules are implemented in
http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/src/tools/docmaker
it is found that only implementing a module named tomarkdown.py similar to
tohtml.py, it can be achieved with some minor modifications in other files
as well but in addition to that reference record can be created for which
the conversion pass has to be modified in order to make entries in record,
corresponding to reference record, secondary pass has to be made to get
reference link of implementation of these functions in `*.c' files as done
in some of libraries like http://pytorch.org/docs/stable/nn.html#
<http://pytorch.org/docs/stable/nn.html> (where source code of function is
shown/rendered on website itself) & https://keras.io/layers/core/ (wherein
for source code, a reference is made to the code repository). This record
may also be used for improvement of UI of
https://www.freetype.org/freetype2/docs/reference/ft2-index.html and as
link will be made relative to this reference table, so while deploying
online, it will make reference link on website and when accessed offline it
will make reference to offline pages.

   Will this functionality will be helpful for the FreeType community? As
in the examples of libraries mentioned above are having a good UX due to
this feature.

  I think this functionality will be helpful for the community for easy
reference of code and improve the UI/UX of website. Though this
functionality is not mentioned in my proposal but it will help me to
implement custom search functionality for easy accessibility which is
mentioned in my proposal.

Docmaker currently works with python2 only, I made some minor modification
so that they are compatible with python3 as well and removed some unused
imports.

Regards,

Ankit
#
#  content.py
#
#    Parse comment blocks to build content blocks (library file).
#
#  Copyright 2002-2018 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.

#
# This file contains routines to parse documentation comment blocks,
# building more structured objects out of them.
#


from sources import *
from utils   import *

import string, re


#
# Regular expressions to detect code sequences.  `Code sequences' are simply
# code fragments embedded in '{' and '}', as demonstrated in the following
# example.
#
#   {
#     x = y + z;
#     if ( zookoo == 2 )
#     {
#       foobar();
#     }
#   }
#
# Note that the indentation of the first opening brace and the last closing
# brace must be exactly the same.  The code sequence itself should have a
# larger indentation than the surrounding braces.
#
re_code_start = re.compile( r"(\s*){\s*$" )
re_code_end   = re.compile( r"(\s*)}\s*$" )


#
# A regular expression to isolate identifiers from other text.  Two syntax
# forms are supported:
#
#   <name>
#   <name>[<id>]
#
# where both `<name>' and `<id>' consist of alphanumeric characters, `_',
# and `-'.  Use `<id>' if there are multiple, valid `<name>' entries; in the
# index, `<id>' will be appended in parentheses.
#
# For example,
#
#   stem_darkening[autofit]
#
# becomes `stem_darkening (autofit)' in the index.
#
re_identifier = re.compile( r"""
                              ((?:\w|-)+
                               (?:\[(?:\w|-)+\])?)
                            """, re.VERBOSE )


#
# We collect macro names ending in `_H' (group 1), as defined in
# `freetype/config/ftheader.h'.  While outputting the object data, we use
# this info together with the object's file location (group 2) to emit the
# appropriate header file macro and its associated file name before the
# object itself.
#
# Example:
#
#   #define FT_FREETYPE_H <freetype.h>
#
re_header_macro = re.compile( r'^#define\s{1,}(\w{1,}_H)\s{1,}<(.*)>' )


################################################################
##
##  DOC CODE CLASS
##
##  The `DocCode' class is used to store source code lines.
##
##  `self.lines' contains a set of source code lines that will be dumped as
##  HTML in a <PRE> tag.
##
##  The object is filled line by line by the parser; it strips the leading
##  `margin' space from each input line before storing it in `self.lines'.
##
class  DocCode:

    def  __init__( self, margin, lines ):
        self.lines = []
        self.words = None

        # remove margin spaces
        for l in lines:
            if string.strip( l[:margin] ) == "":
                l = l[margin:]
            self.lines.append( l )

    def  dump( self, prefix = "", width = 60 ):
        lines = self.dump_lines( 0, width )
        for l in lines:
            print(prefix + l)

    def  dump_lines( self, margin = 0, width = 60 ):
        result = []
        for l in self.lines:
            result.append( " " * margin + l )
        return(result)



################################################################
##
##  DOC PARA CLASS
##
##  `Normal' text paragraphs are stored in the `DocPara' class.
##
##  `self.words' contains the list of words that make up the paragraph.
##
class  DocPara:

    def  __init__( self, lines ):
        self.lines = None
        self.words = []
        for l in lines:
            l = string.strip( l )
            self.words.extend( string.split( l ) )

    def  dump( self, prefix = "", width = 60 ):
        lines = self.dump_lines( 0, width )
        for l in lines:
            print(prefix + l)

    def  dump_lines( self, margin = 0, width = 60 ):
        cur    = ""  # current line
        col    = 0   # current width
        result = []

        for word in self.words:
            ln = len( word )
            if col > 0:
                ln = ln + 1

            if col + ln > width:
                result.append( " " * margin + cur )
                cur = word
                col = len( word )
            else:
                if col > 0:
                    cur = cur + " "
                cur = cur + word
                col = col + ln

        if col > 0:
            result.append( " " * margin + cur )

        return result


################################################################
##
##  DOC FIELD CLASS
##
##  The `DocField' class stores a list containing either `DocPara' or
##  `DocCode' objects.  Each DocField object also has an optional `name'
##  that is used when the object corresponds to a field or value definition.
##
class  DocField:

    def  __init__( self, name, lines ):
        self.name  = name  # can be `None' for normal paragraphs/sources
        self.items = []    # list of items

        mode_none  = 0     # start parsing mode
        mode_code  = 1     # parsing code sequences
        mode_para  = 3     # parsing normal paragraph

        margin     = -1    # current code sequence indentation
        cur_lines  = []

        # analyze the markup lines to check whether they contain paragraphs,
        # code sequences, or fields definitions
        #
        start = 0
        mode  = mode_none

        for l in lines:
            # are we parsing a code sequence?
            if mode == mode_code:
                m = re_code_end.match( l )
                if m and len( m.group( 1 ) ) <= margin:
                    # that's it, we finished the code sequence
                    code = DocCode( 0, cur_lines )
                    self.items.append( code )
                    margin    = -1
                    cur_lines = []
                    mode      = mode_none
                else:
                    # otherwise continue the code sequence
                    cur_lines.append( l[margin:] )
            else:
                # start of code sequence?
                m = re_code_start.match( l )
                if m:
                    # save current lines
                    if cur_lines:
                        para = DocPara( cur_lines )
                        self.items.append( para )
                        cur_lines = []

                    # switch to code extraction mode
                    margin = len( m.group( 1 ) )
                    mode   = mode_code
                else:
                    if not string.split( l ) and cur_lines:
                        # if the line is empty, we end the current paragraph,
                        # if any
                        para = DocPara( cur_lines )
                        self.items.append( para )
                        cur_lines = []
                    else:
                        # otherwise, simply add the line to the current
                        # paragraph
                        cur_lines.append( l )

        if mode == mode_code:
            # unexpected end of code sequence
            code = DocCode( margin, cur_lines )
            self.items.append( code )
        elif cur_lines:
            para = DocPara( cur_lines )
            self.items.append( para )

    def  dump( self, prefix = "" ):
        if self.field:
            print(prefix + self.field + " ::")
            prefix = prefix + "----"

        first = 1
        for p in self.items:
            if not first:
                print ("")
            p.dump( prefix )
            first = 0

    def  dump_lines( self, margin = 0, width = 60 ):
        result = []
        nl     = None

        for p in self.items:
            if nl:
                result.append( "" )

            result.extend( p.dump_lines( margin, width ) )
            nl = 1

        return result


#
# A regular expression to detect field definitions.
#
# Examples:
#
#   foo     ::
#   foo.bar ::
#
re_field = re.compile( r"""
                         \s*
                           (
                             \w*
                           |
                             \w (\w | \.)* \w
                           )
                         \s* ::
                       """, re.VERBOSE )


################################################################
##
##  DOC MARKUP CLASS
##
class  DocMarkup:

    def  __init__( self, tag, lines ):
        self.tag    = string.lower( tag )
        self.fields = []

        cur_lines = []
        field     = None
        mode      = 0

        for l in lines:
            m = re_field.match( l )
            if m:
                # We detected the start of a new field definition.

                # first, save the current one
                if cur_lines:
                    f = DocField( field, cur_lines )
                    self.fields.append( f )
                    cur_lines = []
                    field     = None

                field     = m.group( 1 )   # record field name
                ln        = len( m.group( 0 ) )
                l         = " " * ln + l[ln:]
                cur_lines = [l]
            else:
                cur_lines.append( l )

        if field or cur_lines:
            f = DocField( field, cur_lines )
            self.fields.append( f )

    def  get_name( self ):
        try:
            return self.fields[0].items[0].words[0]
        except:
            return None

    def  dump( self, margin ):
        print(" " * margin + "<" + self.tag + ">")
        for f in self.fields:
            f.dump( "  " )
        print (" " * margin + "</" + self.tag + ">")


################################################################
##
##  DOC CHAPTER CLASS
##
class  DocChapter:

    def  __init__( self, block ):
        self.block    = block
        self.sections = []
        if block:
            self.name  = block.name
            self.title = block.get_markup_words( "title" )
            self.order = block.get_markup_words( "sections" )
        else:
            self.name  = "Other"
            self.title = string.split( "Miscellaneous" )
            self.order = []


################################################################
##
##  DOC SECTION CLASS
##
class  DocSection:

    def  __init__( self, name = "Other" ):
        self.name        = name
        self.blocks      = {}
        self.block_names = []  # ordered block names in section
        self.defs        = []
        self.abstract    = ""
        self.description = ""
        self.order       = []
        self.title       = "ERROR"
        self.chapter     = None

    def  add_def( self, block ):
        self.defs.append( block )

    def  add_block( self, block ):
        self.block_names.append( block.name )
        self.blocks[block.name] = block

    def  process( self ):
        # look up one block that contains a valid section description
        for block in self.defs:
            title = block.get_markup_text( "title" )
            if title:
                self.title       = title
                self.abstract    = block.get_markup_words( "abstract" )
                self.description = block.get_markup_items( "description" )
                self.order       = block.get_markup_words_all( "order" )
                return

    def  reorder( self ):
        self.block_names = sort_order_list( self.block_names, self.order )


################################################################
##
##  CONTENT PROCESSOR CLASS
##
class  ContentProcessor:

    def  __init__( self ):
        """Initialize a block content processor."""
        self.reset()

        self.sections = {}    # dictionary of documentation sections
        self.section  = None  # current documentation section

        self.chapters = []    # list of chapters

        self.headers  = {}    # dictionary of header macros

    def  set_section( self, section_name ):
        """Set current section during parsing."""
        if not section_name in self.sections:
            section = DocSection( section_name )
            self.sections[section_name] = section
            self.section                = section
        else:
            self.section = self.sections[section_name]

    def  add_chapter( self, block ):
        chapter = DocChapter( block )
        self.chapters.append( chapter )

    def  reset( self ):
        """Reset the content processor for a new block."""
        self.markups      = []
        self.markup       = None
        self.markup_lines = []

    def  add_markup( self ):
        """Add a new markup section."""
        if self.markup and self.markup_lines:

            # get rid of last line of markup if it's empty
            marks = self.markup_lines
            if len( marks ) > 0 and not string.strip( marks[-1] ):
                self.markup_lines = marks[:-1]

            m = DocMarkup( self.markup, self.markup_lines )

            self.markups.append( m )

            self.markup       = None
            self.markup_lines = []

    def  process_content( self, content ):
        """Process a block content and return a list of DocMarkup objects
           corresponding to it."""
        markup       = None
        markup_lines = []
        first        = 1

        margin  = -1
        in_code = 0

        for line in content:
            if in_code:
                m = re_code_end.match( line )
                if m and len( m.group( 1 ) ) <= margin:
                    in_code = 0
                    margin  = -1
            else:
                m = re_code_start.match( line )
                if m:
                    in_code = 1
                    margin  = len( m.group( 1 ) )

            found = None

            if not in_code:
                for t in re_markup_tags:
                    m = t.match( line )
                    if m:
                        found  = string.lower( m.group( 1 ) )
                        prefix = len( m.group( 0 ) )
                        # remove markup from line
                        line   = " " * prefix + line[prefix:]
                        break

            # is it the start of a new markup section ?
            if found:
                first = 0
                self.add_markup()  # add current markup content
                self.markup = found
                if len( string.strip( line ) ) > 0:
                    self.markup_lines.append( line )
            elif first == 0:
                self.markup_lines.append( line )

        self.add_markup()

        return self.markups

    def  parse_sources( self, source_processor ):
        blocks = source_processor.blocks
        count  = len( blocks )

        for n in range( count ):
            source = blocks[n]
            if source.content:
                # this is a documentation comment, we need to catch
                # all following normal blocks in the "follow" list
                #
                follow = []
                m = n + 1
                while m < count and not blocks[m].content:
                    follow.append( blocks[m] )
                    m = m + 1

                doc_block = DocBlock( source, follow, self )

    def  finish( self ):
        # process all sections to extract their abstract, description
        # and ordered list of items
        #
        for sec in self.sections.values():
            sec.process()

        # process chapters to check that all sections are correctly
        # listed there
        for chap in self.chapters:
            for sec in chap.order:
                if sec in self.sections:
                    section = self.sections[sec]
                    section.chapter = chap
                    section.reorder()
                    chap.sections.append( section )
                else:
                    sys.stderr.write( "WARNING: chapter '" +          \
                        chap.name + "' in " + chap.block.location() + \
                        " lists unknown section '" + sec + "'\n" )

        # check that all sections are in a chapter
        #
        others = []
        for sec in self.sections.values():
            if not sec.chapter:
                sec.reorder()
                others.append( sec )

        # create a new special chapter for all remaining sections
        # when necessary
        #
        if others:
            chap = DocChapter( None )
            chap.sections = others
            self.chapters.append( chap )


################################################################
##
##  DOC BLOCK CLASS
##
class  DocBlock:

    def  __init__( self, source, follow, processor ):
        processor.reset()

        self.source  = source
        self.code    = []
        self.type    = "ERRTYPE"
        self.name    = "ERRNAME"
        self.section = processor.section
        self.markups = processor.process_content( source.content )

        # compute block type from first markup tag
        try:
            self.type = self.markups[0].tag
        except:
            pass

        # compute block name from first markup paragraph
        try:
            markup = self.markups[0]
            para   = markup.fields[0].items[0]
            name   = para.words[0]
            m = re_identifier.match( name )
            if m:
                name = m.group( 1 )
            self.name = name
        except:
            pass

        if self.type == "section":
            # detect new section starts
            processor.set_section( self.name )
            processor.section.add_def( self )
        elif self.type == "chapter":
            # detect new chapter
            processor.add_chapter( self )
        else:
            processor.section.add_block( self )

        # now, compute the source lines relevant to this documentation
        # block. We keep normal comments in for obvious reasons (??)
        source = []
        for b in follow:
            if b.format:
                break
            for l in b.lines:
                # collect header macro definitions
                m = re_header_macro.match( l )
                if m:
                    processor.headers[m.group( 2 )] = m.group( 1 );

                # we use "/* */" as a separator
                if re_source_sep.match( l ):
                    break
                source.append( l )

        # now strip the leading and trailing empty lines from the sources
        start = 0
        end   = len( source ) - 1

        while start < end and not string.strip( source[start] ):
            start = start + 1

        while start < end and not string.strip( source[end] ):
            end = end - 1

        if start == end and not string.strip( source[start] ):
            self.code = []
        else:
            self.code = source[start:end + 1]

    def  location( self ):
        return self.source.location()

    def  get_markup( self, tag_name ):
        """Return the DocMarkup corresponding to a given tag in a block."""
        for m in self.markups:
            if m.tag == string.lower( tag_name ):
                return m
        return None

    def  get_markup_words( self, tag_name ):
        try:
            m = self.get_markup( tag_name )
            return m.fields[0].items[0].words
        except:
            return []

    def  get_markup_words_all( self, tag_name ):
        try:
            m = self.get_markup( tag_name )
            words = []
            for item in m.fields[0].items:
                # We honour empty lines in an `<Order>' section element by
                # adding the sentinel `/empty/'.  The formatter should then
                # convert it to an appropriate representation in the
                # `section_enter' function.
                words += item.words
                words.append( "/empty/" )
            return words
        except:
            return []

    def  get_markup_text( self, tag_name ):
        result = self.get_markup_words( tag_name )
        return string.join( result )

    def  get_markup_items( self, tag_name ):
        try:
            m = self.get_markup( tag_name )
            return m.fields[0].items
        except:
            return None

# eof
#!/usr/bin/env python
#
#  DocBeauty (c) 2003, 2004, 2008 David Turner <da...@freetype.org>
#
# This program is used to beautify the documentation comments used
# in the FreeType 2 public headers.
#

from sources import *
from content import *
from utils   import *

import sys, os, string, getopt


content_processor = ContentProcessor()


def  beautify_block( block ):
    if block.content:
        content_processor.reset()

        markups = content_processor.process_content( block.content )
        text    = []
        first   = 1

        for markup in markups:
            text.extend( markup.beautify( first ) )
            first = 0

        # now beautify the documentation "borders" themselves
        lines = [" /*************************************************************************"]
        for l in text:
            lines.append( "  *" + l )
        lines.append( "  */" )

        block.lines = lines


def  usage():
    print("\nDocBeauty 0.1 Usage information\n")
    print("  docbeauty [options] file1 [file2 ...]\n")
    print("using the following options:\n")
    print("  -h : print this page")
    print("  -b : backup original files with the 'orig' extension")
    print("")
    print("  --backup : same as -b")


def  main( argv ):
    """main program loop"""

    global output_dir

    try:
        opts, args = getopt.getopt( sys.argv[1:], \
                                    "hb",         \
                                    ["help", "backup"] )
    except getopt.GetoptError:
        usage()
        sys.exit( 2 )

    if args == []:
        usage()
        sys.exit( 1 )

    # process options
    #
    output_dir = None
    do_backup  = None

    for opt in opts:
        if opt[0] in ( "-h", "--help" ):
            usage()
            sys.exit( 0 )

        if opt[0] in ( "-b", "--backup" ):
            do_backup = 1

    # create context and processor
    source_processor = SourceProcessor()

    # retrieve the list of files to process
    file_list = make_file_list( args )
    for filename in file_list:
        source_processor.parse_file( filename )

        for block in source_processor.blocks:
            beautify_block( block )

        new_name = filename + ".new"
        ok       = None

        try:
            file = open( new_name, "wt" )
            for block in source_processor.blocks:
                for line in block.lines:
                    file.write( line )
                    file.write( "\n" )
            file.close()
        except:
            ok = 0


# if called from the command line
#
if __name__ == '__main__':
    main( sys.argv )


# eof
#!/usr/bin/env python
#
#  docmaker.py
#
#    Convert source code markup to HTML documentation.
#
#  Copyright 2002-2018 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.

#
# This program is a re-write of the original DocMaker tool used to generate
# the API Reference of the FreeType font rendering engine by converting
# in-source comments into structured HTML.
#
# This new version is capable of outputting XML data as well as accepting
# more liberal formatting options.  It also uses regular expression matching
# and substitution to speed up operation significantly.
#

from sources   import *
from content   import *
from utils     import *
from formatter import *
from tohtml    import *

import utils

import sys, glob, getopt


def  usage():
    print("\nDocMaker Usage information\n")
    print("  docmaker [options] file1 [file2 ...]\n")
    print("using the following options:\n")
    print("  -h : print this page")
    print("  -t : set project title, as in '-t \"My Project\"'")
    print("  -o : set output directory, as in '-o mydir'")
    print("  -p : set documentation prefix, as in '-p ft2'")
    print("")
    print("  --title  : same as -t, as in '--title=\"My Project\"'")
    print("  --output : same as -o, as in '--output=mydir'")
    print("  --prefix : same as -p, as in '--prefix=ft2'")


def  main(argv):
    """Main program loop."""

    global output_dir

    try:
        opts, args = getopt.getopt(sys.argv[1:],
                                   "ht:o:p:",
                                   ["help", "title=", "output=", "prefix="])
    except getopt.GetoptError:
        usage()
        sys.exit(2)

    if args == []:
        usage()
        sys.exit(1)

    # process options
    project_title = "Project"
    project_prefix = None
    output_dir = None

    for opt in opts:
        if opt[0] in ("-h", "--help"):
            usage()
            sys.exit(0)

        if opt[0] in ("-t", "--title"):
            project_title = opt[1]

        if opt[0] in ("-o", "--output"):
            utils.output_dir = opt[1]

        if opt[0] in ("-p", "--prefix"):
            project_prefix = opt[1]

    check_output()

    # create context and processor
    source_processor = SourceProcessor()
    content_processor = ContentProcessor()

    # retrieve the list of files to process
    file_list = make_file_list(args)
    for filename in file_list:
        source_processor.parse_file(filename)
        content_processor.parse_sources(source_processor)

    # process sections
    content_processor.finish()

    formatter = HtmlFormatter(content_processor,
                              project_title,
                              project_prefix)

    formatter.toc_dump()
    formatter.index_dump()
    formatter.section_dump_all()


# if called from the command line
if __name__ == '__main__':
    main(sys.argv)

# eof
#
#  formatter.py
#
#    Convert parsed content blocks to a structured document (library file).
#
#  Copyright 2002-2018 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.

#
# This is the base Formatter class.  Its purpose is to convert a content
# processor's data into specific documents (i.e., table of contents, global
# index, and individual API reference indices).
#
# You need to sub-class it to output anything sensible.  For example, the
# file `tohtml.py' contains the definition of the `HtmlFormatter' sub-class
# to output HTML.
#


from sources import *
from content import *
from utils   import *


################################################################
##
##  FORMATTER CLASS
##
class  Formatter:

    def  __init__( self, processor ):
        self.processor   = processor
        self.identifiers = {}
        self.chapters    = processor.chapters
        self.sections    = processor.sections.values()
        self.block_index = []

        # store all blocks in a dictionary
        self.blocks = []
        for section in self.sections:
            for block in section.blocks.values():
                self.add_identifier( block.name, block )

                # add enumeration values to the index, since this is useful
                for markup in block.markups:
                    if markup.tag == 'values':
                        for field in markup.fields:
                            self.add_identifier( field.name, block )

        self.block_index = self.identifiers.keys()
        self.block_index.sort( key = index_key )

        # also add section names to dictionary (without making them appear
        # in the index)
        for section in self.sections:
            self.add_identifier( section.name, section )

    def  add_identifier( self, name, block ):
        if name in self.identifiers:
            # duplicate name!
            sys.stderr.write( "WARNING: duplicate definition for"
                              + " '" + name + "' "
                              + "in " + block.location() + ", "
                              + "previous definition in "
                              + self.identifiers[name].location()
                              + "\n" )
        else:
            self.identifiers[name] = block

    #
    # formatting the table of contents
    #
    def  toc_enter( self ):
        pass

    def  toc_chapter_enter( self, chapter ):
        pass

    def  toc_section_enter( self, section ):
        pass

    def  toc_section_exit( self, section ):
        pass

    def  toc_chapter_exit( self, chapter ):
        pass

    def  toc_index( self, index_filename ):
        pass

    def  toc_exit( self ):
        pass

    def  toc_dump( self, toc_filename = None, index_filename = None ):
        output = None
        if toc_filename:
            output = open_output( toc_filename )

        self.toc_enter()

        for chap in self.processor.chapters:

            self.toc_chapter_enter( chap )

            for section in chap.sections:
                self.toc_section_enter( section )
                self.toc_section_exit( section )

            self.toc_chapter_exit( chap )

        self.toc_index( index_filename )

        self.toc_exit()

        if output:
            close_output( output )

    #
    # formatting the index
    #
    def  index_enter( self ):
        pass

    def  index_name_enter( self, name ):
        pass

    def  index_name_exit( self, name ):
        pass

    def  index_exit( self ):
        pass

    def  index_dump( self, index_filename = None ):
        output = None
        if index_filename:
            output = open_output( index_filename )

        self.index_enter()

        for name in self.block_index:
            self.index_name_enter( name )
            self.index_name_exit( name )

        self.index_exit()

        if output:
            close_output( output )

    #
    # formatting a section
    #
    def  section_enter( self, section ):
        pass

    def  block_enter( self, block ):
        pass

    def  markup_enter( self, markup, block = None ):
        pass

    def  field_enter( self, field, markup = None, block = None ):
        pass

    def  field_exit( self, field, markup = None, block = None ):
        pass

    def  markup_exit( self, markup, block = None ):
        pass

    def  block_exit( self, block ):
        pass

    def  section_exit( self, section ):
        pass

    def  section_dump( self, section, section_filename = None ):
        output = None
        if section_filename:
            output = open_output( section_filename )

        self.section_enter( section )

        for name in section.block_names:
            skip_entry = 0
            try:
                block = self.identifiers[name]
                # `block_names' can contain field names also,
                # which we filter out
                for markup in block.markups:
                    if markup.tag == 'values':
                        for field in markup.fields:
                            if field.name == name:
                                skip_entry = 1
            except:
                skip_entry = 1   # this happens e.g. for `/empty/' entries

            if skip_entry:
              continue

            self.block_enter( block )

            for markup in block.markups[1:]:   # always ignore first markup!
                self.markup_enter( markup, block )

                for field in markup.fields:
                    self.field_enter( field, markup, block )
                    self.field_exit( field, markup, block )

                self.markup_exit( markup, block )

            self.block_exit( block )

        self.section_exit( section )

        if output:
            close_output( output )

    def  section_dump_all( self ):
        for section in self.sections:
            self.section_dump( section )

# eof
#
#  sources.py
#
#    Convert source code comments to multi-line blocks (library file).
#
#  Copyright 2002-2018 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.

#
# This library file contains definitions of classes needed to decompose C
# source code files into a series of multi-line `blocks'.  There are two
# kinds of blocks.
#
#   - Normal blocks, which contain source code or ordinary comments.
#
#   - Documentation blocks, which have restricted formatting, and whose text
#     always start with a documentation markup tag like `<Function>',
#     `<Type>', etc.
#
# The routines to process the content of documentation blocks are contained
# in file `content.py'; the classes and methods found here only deal with
# text parsing and basic documentation block extraction.
#


import fileinput, re, string


################################################################
##
##  SOURCE BLOCK FORMAT CLASS
##
##  A simple class containing compiled regular expressions to detect
##  potential documentation format block comments within C source code.
##
##  The `column' pattern must contain a group to `unbox' the content of
##  documentation comment blocks.
##
##  Later on, paragraphs are converted to long lines, which simplifies the
##  regular expressions that act upon the text.
##
class SourceBlockFormat:

    def __init__( self, id, start, column, end ):
        """Create a block pattern, used to recognize special documentation
           blocks."""
        self.id     = id
        self.start  = re.compile( start, re.VERBOSE )
        self.column = re.compile( column, re.VERBOSE )
        self.end    = re.compile( end, re.VERBOSE )


#
# Format 1 documentation comment blocks.
#
#    /************************************/ (at least 2 asterisks)
#    /*                                  */
#    /*                                  */
#    /*                                  */
#    /************************************/ (at least 2 asterisks)
#
start = r'''
  \s*      # any number of whitespace
  /\*{2,}/ # followed by '/' and at least two asterisks then '/'
  \s*$     # probably followed by whitespace
'''

column = r'''
  \s*      # any number of whitespace
  /\*{1}   # followed by '/' and precisely one asterisk
  ([^*].*) # followed by anything (group 1)
  \*{1}/   # followed by one asterisk and a '/'
  \s*$     # probably followed by whitespace
'''

re_source_block_format1 = SourceBlockFormat( 1, start, column, start )


#
# Format 2 documentation comment blocks.
#
#    /************************************ (at least 2 asterisks)
#     *
#     *                                    (1 asterisk)
#     *
#     */                                   (1 or more asterisks)
#
start = r'''
  \s*     # any number of whitespace
  /\*{2,} # followed by '/' and at least two asterisks
  \s*$    # probably followed by whitespace
'''

column = r'''
  \s*           # any number of whitespace
  \*{1}(?![*/]) # followed by precisely one asterisk not followed by `/'
  (.*)          # then anything (group1)
'''

end = r'''
  \s*  # any number of whitespace
  \*+/ # followed by at least one asterisk, then '/'
'''

re_source_block_format2 = SourceBlockFormat( 2, start, column, end )


#
# The list of supported documentation block formats.  We could add new ones
# quite easily.
#
re_source_block_formats = [re_source_block_format1, re_source_block_format2]


#
# The following regular expressions correspond to markup tags within the
# documentation comment blocks.  They are equivalent despite their different
# syntax.
#
# A markup tag consists of letters or character `-', to be found in group 1.
#
# Notice that a markup tag _must_ begin a new paragraph.
#
re_markup_tag1 = re.compile( r'''\s*<((?:\w|-)*)>''' )  # <xxxx> format
re_markup_tag2 = re.compile( r'''\s*@((?:\w|-)*):''' )  # @xxxx: format

#
# The list of supported markup tags.  We could add new ones quite easily.
#
re_markup_tags = [re_markup_tag1, re_markup_tag2]


#
# A regular expression to detect a cross reference, after markup tags have
# been stripped off.
#
# Two syntax forms are supported:
#
#   @<name>
#   @<name>[<id>]
#
# where both `<name>' and `<id>' consist of alphanumeric characters, `_',
# and `-'.  Use `<id>' if there are multiple, valid `<name>' entries.
#
# Example: @foo[bar]
#
re_crossref = re.compile( r"""
                            @
                            (?P<name>(?:\w|-)+
                                     (?:\[(?:\w|-)+\])?)
                            (?P<rest>.*)
                          """, re.VERBOSE )

#
# Two regular expressions to detect italic and bold markup, respectively.
# Group 1 is the markup, group 2 the rest of the line.
#
# Note that the markup is limited to words consisting of letters, digits,
# the characters `_' and `-', or an apostrophe (but not as the first
# character).
#
re_italic = re.compile( r"_((?:\w|-)(?:\w|'|-)*)_(.*)" )     #  _italic_
re_bold   = re.compile( r"\*((?:\w|-)(?:\w|'|-)*)\*(.*)" )   #  *bold*

#
# This regular expression code to identify an URL has been taken from
#
#   https://mail.python.org/pipermail/tutor/2002-September/017228.html
#
# (with slight modifications).
#
urls = r'(?:https?|telnet|gopher|file|wais|ftp)'
ltrs = r'\w'
gunk = r'/#~:.?+=&%@!\-'
punc = r'.:?\-'
any  = "%(ltrs)s%(gunk)s%(punc)s" % { 'ltrs' : ltrs,
                                      'gunk' : gunk,
                                      'punc' : punc }
url  = r"""
         (
           \b                    # start at word boundary
           %(urls)s :            # need resource and a colon
           [%(any)s] +?          # followed by one or more of any valid
                                 # character, but be conservative and
                                 # take only what you need to...
           (?=                   # [look-ahead non-consumptive assertion]
             [%(punc)s]*         # either 0 or more punctuation
             (?:                 # [non-grouping parentheses]
               [^%(any)s] | $    # followed by a non-url char
                                 # or end of the string
             )
           )
         )
        """ % {'urls' : urls,
               'any'  : any,
               'punc' : punc }

re_url = re.compile( url, re.VERBOSE | re.MULTILINE )

#
# A regular expression that stops collection of comments for the current
# block.
#
re_source_sep = re.compile( r'\s*/\*\s*\*/' )   #  /* */

#
# A regular expression to find possible C identifiers while outputting
# source code verbatim, covering things like `*foo' or `(bar'.  Group 1 is
# the prefix, group 2 the identifier -- since we scan lines from left to
# right, sequentially splitting the source code into prefix and identifier
# is fully sufficient for our purposes.
#
re_source_crossref = re.compile( r'(\W*)(\w*)' )

#
# A regular expression that matches a list of reserved C source keywords.
#
re_source_keywords = re.compile( '''\\b ( typedef   |
                                          struct    |
                                          enum      |
                                          union     |
                                          const     |
                                          char      |
                                          int       |
                                          short     |
                                          long      |
                                          void      |
                                          signed    |
                                          unsigned  |
                                          \#include |
                                          \#define  |
                                          \#undef   |
                                          \#if      |
                                          \#ifdef   |
                                          \#ifndef  |
                                          \#else    |
                                          \#endif   ) \\b''', re.VERBOSE )


################################################################
##
##  SOURCE BLOCK CLASS
##
##  There are two important fields in a `SourceBlock' object.
##
##    self.lines
##      A list of text lines for the corresponding block.
##
##    self.content
##      For documentation comment blocks only, this is the block content
##      that has been `unboxed' from its decoration.  This is `None' for all
##      other blocks (i.e., sources or ordinary comments with no starting
##      markup tag)
##
class  SourceBlock:

    def  __init__( self, processor, filename, lineno, lines ):
        self.processor = processor
        self.filename  = filename
        self.lineno    = lineno
        self.lines     = lines[:]
        self.format    = processor.format
        self.content   = []

        if self.format == None:
            return

        words = []

        # extract comment lines
        lines = []

        for line0 in self.lines:
            m = self.format.column.match( line0 )
            if m:
                lines.append( m.group( 1 ) )

        # now, look for a markup tag
        for l in lines:
            l = string.strip( l )
            if len( l ) > 0:
                for tag in re_markup_tags:
                    if tag.match( l ):
                        self.content = lines
                        return

    def  location( self ):
        return "(" + self.filename + ":" + repr( self.lineno ) + ")"

    # debugging only -- not used in normal operations
    def dump( self ):
        if self.content:
            print("{{{content start---")
            for l in self.content:
                print(l)
            print("---content end}}}")
            return

        fmt = ""
        if self.format:
            fmt = repr(self.format.id) + " "

        for line in self.lines:
            print(line)


################################################################
##
##  SOURCE PROCESSOR CLASS
##
##  The `SourceProcessor' is in charge of reading a C source file and
##  decomposing it into a series of different `SourceBlock' objects.
##
##  A SourceBlock object consists of the following data.
##
##    - A documentation comment block using one of the layouts above.  Its
##      exact format will be discussed later.
##
##    - Normal sources lines, including comments.
##
##
class  SourceProcessor:

    def  __init__( self ):
        """Initialize a source processor."""
        self.blocks   = []
        self.filename = None
        self.format   = None
        self.lines    = []

    def  reset( self ):
        """Reset a block processor and clean up all its blocks."""
        self.blocks = []
        self.format = None

    def  parse_file( self, filename ):
        """Parse a C source file and add its blocks to the processor's
           list."""
        self.reset()

        self.filename = filename

        fileinput.close()
        self.format = None
        self.lineno = 0
        self.lines  = []

        for line in fileinput.input( filename ):
            # strip trailing newlines, important on Windows machines!
            if line[-1] == '\012':
                line = line[0:-1]

            if self.format == None:
                self.process_normal_line( line )
            else:
                if self.format.end.match( line ):
                    # A normal block end.  Add it to `lines' and create a
                    # new block
                    self.lines.append( line )
                    self.add_block_lines()
                elif self.format.column.match( line ):
                    # A normal column line.  Add it to `lines'.
                    self.lines.append( line )
                else:
                    # An unexpected block end.  Create a new block, but
                    # don't process the line.
                    self.add_block_lines()

                    # we need to process the line again
                    self.process_normal_line( line )

        # record the last lines
        self.add_block_lines()

    def  process_normal_line( self, line ):
        """Process a normal line and check whether it is the start of a new
           block."""
        for f in re_source_block_formats:
            if f.start.match( line ):
                self.add_block_lines()
                self.format = f
                self.lineno = fileinput.filelineno()

        self.lines.append( line )

    def  add_block_lines( self ):
        """Add the current accumulated lines and create a new block."""
        if self.lines != []:
            block = SourceBlock( self,
                                 self.filename,
                                 self.lineno,
                                 self.lines )

            self.blocks.append( block )
            self.format = None
            self.lines  = []

    # debugging only, not used in normal operations
    def  dump( self ):
        """Print all blocks in a processor."""
        for b in self.blocks:
            b.dump()

# eof
#
#  tohtml.py
#
#    A sub-class container of the `Formatter' class to produce HTML.
#
#  Copyright 2002-2018 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"
"https://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_header1 = '<h1 id="'
section_title_header2 = '">'
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 `&lt;',`&gt;', and `&amp;'.
#
def  html_quote( line ):
    result = string.replace( line,   "&", "&amp;" )
    result = string.replace( result, "<", "&lt;"  )
    result = string.replace( result, ">", "&gt;"  )
    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

        try:
            section_url = self.make_section_url( block.section )
        except:
            # we already have a section
            section_url = self.make_section_url( block )

        return section_url + "#" + 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( 'name' )
                rest = m.group( 'rest' )
                block = self.identifiers[name]
                url   = self.make_block_url( block )
                # display `foo[bar]' as `foo'
                name = re.sub( r'\[.*\]', '', name )
                # normalize url, following RFC 3986
                url = string.replace( url, "[", "(" )
                url = string.replace( url, "]", ")" )

                try:
                    # for sections, display title
                    url = ( '&lsquo;<a href="' + url + '">'
                            + block.title + '</a>&rsquo;'
                            + rest )
                except:
                    url = ( '<a href="' + url + '">'
                            + name + '</a>'
                            + rest )

                return url
            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&lsquo;\2&rsquo;\3',
                           line )
            # convert tilde into non-breakable space
            line = string.replace( line, "~", "&nbsp;" )

        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 ).rstrip() + '\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
                    try:
                      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>' )
                    except:
                      # sections don't have `markups'; however, we don't
                      # want references to sections here anyway
                      result = result + html_quote( line[:length] )

                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]
                    # display `foo[bar]' as `foo (bar)'
                    bname = string.replace( bname, "[", " (" )
                    bname = string.replace( bname, "]", ")"  )
                    # normalize url, following RFC 3986
                    url = string.replace( url, "[", "(" )
                    url = string.replace( url, "]", ")" )
                    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_header1 + section.name + section_title_header2
                + 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 + "&nbsp;"
                        else:
                            url = name
                            # display `foo[bar]' as `foo'
                            name = re.sub( r'\[.*\]', '', name )
                            # normalize url, following RFC 3986
                            url = string.replace( url, "[", "(" )
                            url = string.replace( url, "]", ")" )
                            line = ( line + '<a href="#' + url + '">'
                                     + 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:
            url = block.name
            # display `foo[bar]' as `foo'
            name = re.sub( r'\[.*\]', '', block.name )
            # normalize url, following RFC 3986
            url = string.replace( url, "[", "(" )
            url = string.replace( url, "]", ")" )
            print( '<h3 id="' + url + '">' + 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
#
#  utils.py
#
#    Auxiliary functions for the `docmaker' tool (library file).
#
#  Copyright 2002-2018 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.


import string, sys, os, glob, itertools


# current output directory
#
output_dir = None


# A function that generates a sorting key.  We want lexicographical order
# (primary key) except that capital letters are sorted before lowercase
# ones (secondary key).
#
# The primary key is implemented by lowercasing the input.  The secondary
# key is simply the original data appended, character by character.  For
# example, the sort key for `FT_x' is `fFtT__xx', while the sort key for
# `ft_X' is `fftt__xX'.  Since ASCII codes of uppercase letters are
# numerically smaller than the codes of lowercase letters, `fFtT__xx' gets
# sorted before `fftt__xX'.
#
def  index_key( s ):
    return string.join( itertools.chain( *zip( s.lower(), s ) ) )


# Sort `input_list', placing the elements of `order_list' in front.
#
def  sort_order_list( input_list, order_list ):
    new_list = order_list[:]
    for id in input_list:
        if not id in order_list:
            new_list.append( id )
    return new_list


# Divert standard output to a given project documentation file.  Use
# `output_dir' to determine the filename location if necessary and save the
# old stdout handle in a tuple that is returned by this function.
#
def  open_output( filename ):
    global output_dir

    if output_dir and output_dir != "":
        filename = output_dir + os.sep + filename

    old_stdout = sys.stdout
    new_file   = open( filename, "w" )
    sys.stdout = new_file

    return ( new_file, old_stdout )


# Close the output that was returned by `open_output'.
#
def  close_output( output ):
    output[0].close()
    sys.stdout = output[1]


# Check output directory.
#
def  check_output():
    global output_dir
    if output_dir:
        if output_dir != "":
            if not os.path.isdir( output_dir ):
                sys.stderr.write( "argument"
                                  + " '" + output_dir + "' "
                                  + "is not a valid directory\n" )
                sys.exit( 2 )
        else:
            output_dir = None


def  file_exists( pathname ):
    """Check that a given file exists."""
    result = 1
    try:
        file = open( pathname, "r" )
        file.close()
    except:
        result = None
        sys.stderr.write( pathname + " couldn't be accessed\n" )

    return result


def  make_file_list( args = None ):
    """Build a list of input files from command-line arguments."""
    file_list = []
    # sys.stderr.write( repr( sys.argv[1 :] ) + '\n' )

    if not args:
        args = sys.argv[1:]

    for pathname in args:
        if string.find( pathname, '*' ) >= 0:
            newpath = glob.glob( pathname )
            newpath.sort()  # sort files -- this is important because
                            # of the order of files
        else:
            newpath = [pathname]

        file_list.extend( newpath )

    if len( file_list ) == 0:
        file_list = None
    else:
        # now filter the file list to remove non-existing ones
        file_list = filter( file_exists, file_list )

    return file_list

# eof
_______________________________________________
Freetype-devel mailing list
Freetype-devel@nongnu.org
https://lists.nongnu.org/mailman/listinfo/freetype-devel

Reply via email to