shithub: freetype+ttf2subf

ref: a39acf55f753b7a6cba7b3f0839505e5f420cea7
dir: /docs/docmaker.py/

View raw version
#!/usr/bin/env python
#
# DocMaker is a very simple program used to generate HTML documentation
# from the source files of the FreeType packages.
#

import fileinput, sys, string

html_header = """
<html>
<header>
<title>FreeType 2 API Reference</title>
<basefont face="Georgia, Arial, Helvetica, Geneva">
<style content="text/css">
  P { text-align=justify }
  H1 { text-align=center }
  H2 { text-align=center }
  LI { text-align=justify }
</style>
</header>
<body text="#000000"
      bgcolor="#FFFFFF"
      link="#0000EF"
      vlink="#51188E"
      alink="#FF0000">
<center><h1>FreeType 2 API Reference</h1></center>
<center><table width="80%"><tr><td>
"""

html_footer = """
</td></tr>
</table></center>
</body>
</html>
"""

code_header = """
<font color=blue><pre>
"""

code_footer = """
</pre></font>
"""

para_header = "<p>"
para_footer = "</p>"

block_header = """<table width="100%"><tr><td>"""
block_footer = "</table>"

source_header = """<table width="100%"><tr bgcolor="#D6E8FF" width="100%"><td><pre>
"""
source_footer = """</pre></table>
<br><br>
"""

# The FreeType 2 reference is extracted from the source files. These contain
# various comment blocks that follow one of the following formats:
#
#  /**************************
#   *
#   *  FORMAT1
#   *
#   *
#   *
#   *
#   *************************/
#
#  /**************************/
#  /*                        */
#  /*  FORMAT2               */
#  /*                        */
#  /*                        */
#  /*                        */
#  /*                        */
#
#  /**************************/
#  /*                        */
#  /*  FORMAT3               */
#  /*                        */
#  /*                        */
#  /*                        */
#  /*                        */
#  /**************************/
#
# Each block contains a list of markers, each one can be followed by
# some arbitrary text or a list of fields. Here's an example:
#
#    <Struct>
#       MyStruct
#
#    <Description>
#       this structure holds some data
#
#    <Fields>
#       x :: horizontal coordinate
#       y :: vertical coordinate
#
#
# This example defines three markers: 'Struct', 'Description' & 'Fields'
# The first two markers contain arbitrary text, while the last one contains
# a list of field
#
#
# each field is simple of the format:  WORD :: TEXT....
#
# Note that typically, each comment block is followed by some source
# code declaration that may need to be kept in the reference..
#


def make_block_list():
    """parse a file and extract comments blocks from it"""

    list   = []
    block  = []
    format = 0
 
    # we use "format" to store the state of our parser:
    #
    #  0 - wait for beginning of comment
    #  1 - parse comment format 1
    #  2 - parse comment format 2
    #
    #  4 - wait for beginning of source (or comment ??)
    #  5 - process source
    #
    
    comment = []
    source  = []
    state   = 0

    for line in fileinput.input():

        # remove trailing CR
        l = len(line)
        if l > 0 and line[l-1] == '\012':
            line = line[0:l-1]

        # stripped version of the line
        line2 = string.strip(line)
        l     = len(line2)

        # if this line begins with a comment and we're processing some source, exit
        # to state 0
        #
        if format >= 4 and l > 2 and line2[0:2] == '/*':
            list.append( (block,source) )
            format = 0
        
        if format == 0:  ##################### wait beginning of comment ###########
	    if l > 3 and line2[0:3] == '/**':
		i = 3
		while (i < l) and (line2[i] == '*'):
		    i = i+1

		if i == l:
		    # this is '/**' followed by any number of '*', the
		    # beginning of a Format 1 block
		    #
		    block  = []
		    source = []
		    format = 1

		elif (i == l-1) and (line2[i] == '/'):
		    # this is '/**' followed by any number of '*', followed
		    # by a '/', i.e. the beginning of a Format 2 or 3 block
		    #
		    block  = []
		    source = []
		    format = 2

	##############################################################
	#
	# FORMAT 1
	#
	elif format == 1:

	    # if the line doesn't begin with a "*", something went
	    # wrong, and we must exit, and forget the current block..
	    if (l == 0) or (line2[0] != '*'):
		block  = []
		format = 0
		
	    # otherwise, we test for an end of block, which is an
	    # arbitrary number of '*', followed by '/'
	    else:
		i = 1
		while (i < l) and (line2[i] == '*'):
		    i = i+1

		# test for the end of the block
		if (i < l) and (line2[i] == '/'):
		    if block != []:
		        format = 4
		    else:
		        format = 0
		else:
		    # otherwise simply append line to current block
                    block.append(line2)
                continue

	##############################################################
	#
	# FORMAT 2
	#
	elif format == 2:

	    # if the line doesn't begin with '/*' and end with '*/',
	    # this is the end of the format 2 format..
	    if (l < 4 ) or (line2[:2] != '/*') or (line2[-2:] != '*/'):
		if block != []:
			format = 4
		else:
			format = 0
	    else:
	        # remove the start and end comment delimiters, then right-strip
	        # the line
	        line2 = string.rstrip(line2[2:-2])

	        # check for end of a format2 block, i.e. a run of '*'
	        if string.count(line2,'*') == l-4:
		    if block != []:
			    format = 4
		    else:
			    format = 0
	        else:
		    # otherwise, add the line to the current block
		    block.append(line2)
		    
	        continue



        if format >= 4:  ########################## source processing ################

            if l > 0:
                format = 5
                
            if format == 5:
                source.append(line)


    if format >= 4:
        list.append([block,source])
        
    return list




    
# This function is only used for debugging
#
def dump_block_list( list ):
    """dump a comment block list"""
    for block in list:
	print "----------------------------------------"
	for line in block[0]:
		print line
	for line in block[1]:
		print line

    print "---------the end-----------------------"



##############################################################################
#
# The DocCode class is used to store source code lines
#
class DocCode:

    def __init__(self,margin=0):
	self.lines  = []
	self.margin = margin 
	
    def	add(self,line):
        # remove margin whitespace
        if string.strip( line[:self.margin] ) == "": line = line[self.margin:]
        self.lines.append(line)

    
    def dump(self):

	max_width = 50

        for line in self.lines:
            print "--" + line

	print ""


    def dump_html(self):
    
        # clean the last empty lines
        l = len(self.lines)-1
        while (len > 0) and string.strip(lines[len-1])=="":
          len = len-1

        print code_header
        for line in self.lines[0:len]:
            print lines
        print code_footer


##############################################################################
#
# The DocParagraph is used to store either simple text paragraphs
# self.words is simply a list of words for the paragraph
#
class DocParagraph:

    def __init__(self):
	self.words  = []
	
    def	add(self,line):
        # get rid of unwanted spaces in the paragraph
        self.words.extend( string.split(line) )
	
    
    def dump(self):

	max_width = 50
        cursor    = 0
        line      = ""
        
        for word in self.words:
    
	    if cursor+len(word)+1 > max_width:
	        print line
	        cursor = 0
	        line = ""
    
	    line   = line + word + " "
	    cursor = cursor + len(word) + 1
	    
        if cursor > 0:
	    print line

	print ""


    def dump_html(self):
    
        print para_header
        self.dump()
        print para_footer


###########################################################################
#
# DocContent is used to store the content of a given marker.
#
#
#
class DocContent:

    def  __init__(self,lines_list):
        self.items    = []
        code_mode     = 0
        code_margin   = 0
        text          = []
        paragraph     = None
        code          = None
        elements      = []
        field         = None

	for aline in lines_list:
	
	    if code_mode == 0:
		line   = string.lstrip(aline) 
	        l      = len(line)
		margin = len(aline) - l

		# if the line is empty, this is the end of the current
		# paragraph
		if l == 0 or line == '{':
                
		    if paragraph:
                        elements.append(paragraph)
                        paragraph = None
                    
                    if line == "":
                        continue
                        
                    code_mode   = 1
                    code_margin = margin
                    code        = None
                    continue
	    
		words = string.split(line)
		
		# test for a field delimiter on the start of the line, i.e.
		# the token `::'
		#
		if len(words) >= 2 and words[1] == "::":
		    if paragraph:
                        elements.append(paragraph)
                        paragraph = None

                    self.items.append( (field,elements) )
		
		    field    = words[0]
		    elements = []
		    words    = words[2:]
			
		if len(words) > 0:
		    line = string.join(words)
		    if not paragraph:
			paragraph = DocParagraph()
		    paragraph.add( line )
	    
	    else:
		line = aline
		
		# the code block ends with a line that has a single '}' on it
		if line == " "*code_margin+'}':
                
                    if code:
                        elements.append(code)
                        code = None
                        
		    code_mode   = 0
		    code_margin = 0
		
		# otherwise, add the line to the current paragraph
		else:
                    if not code:
                        code = DocCode()
		    code.add(line)
	
	if paragraph:
	    elements.append(paragraph)

        if code:
            elements.append(code)
	    
	self.items.append( (field,elements) )



    def dump(self):
	for item in self.items:
            field = item[0]
	    if field:
                print "<field "+field+">"

	    for element in item[1]:
                element.dump()
	    
	    if field:
                print "</field>	"

    def dump_html(self):
        
        n        = len(self.items)
        in_table = 0
        
        for i in range(n):
            item  = self.items[i]
            field = item[0]
            
            if not field:
            
                if in_table:
                  print "</td></tr></table>"
                  in_table = 0
                  
                for element in item[1]:
                  element.dump_html()
            else:
                if not in_table:
                    print "<table cellpadding=4><tr valign=top><td>"
                    in_table = 1
                else:
                    print "</td></tr><tr valign=top><td>"
                
                print "<b>"+field+"</b></td><td>"
                
                for element in item[1]:
                    element.dump_html()

        if in_table:
            print "</td></tr></table>"

######################################################################################
#
#
# The DocBlock class is used to store a given comment block. It contains
# a list of markers, as well as a list of contents for each marker.
#
#
class DocBlock:

    def __init__(self, block_line_list=[], source_line_list=[]):
	self.markers  = []
	self.contents = []
        self.source   = source_line_list
    
        marker   = ""
        content  = []
        alphanum = string.letters + string.digits + "_"
    
        for line in block_line_list:
    	    line2  = string.lstrip(line)
	    l      = len(line2)
	    margin = len(line) - l
	
	    if l > 3 and line2[0] == '<':
	        i = 1
	        while i < l and line2[i] in alphanum: i = i+1
	        if i < l and line2[i] == '>':
		    if marker or content:
    			self.add( marker, content )
		    marker  = line2[1:i]
		    content = []
		    line2   = string.lstrip(line2[i+1:])
		    l       = len(line2)
		    line    = " "*margin + line2
	
	    content.append(line)
	
	if marker or content:
	    self.add( marker, content )
	
	
    def add( self, marker, lines ):
    
	# remove the first and last empty lines from the content list
	l = len(lines)
	if l > 0:
	    i = 0
	    while l > 0 and string.strip(lines[l-1]) == "": l = l-1
	    while i < l and string.strip(lines[i]) == "": i = i+1
	    lines = lines[i:l]
	    l     = len(lines)
	    
	# add a new marker only if its marker and its content list aren't empty
	if l > 0 and marker:
	    self.markers.append(marker)
	    self.contents.append(lines)

    def dump( self ):
	for i in range( len(self.markers) ):
	    print "["+self.markers[i]+"]"
	    for line in self.contents[i]:
		print "-- "+line

    def doc_contents(self):
	contents = []
	for item in self.contents:
	  contents.append( DocContent(item) )
	return contents


def dump_doc_blocks( block_list ):
    for block in block_list:
	docblock = DocBlock(block)
	docblock.dump()
	print "<<------------------->>"


#
#
#
def dump_single_content( block_list ):

    block = block_list[0]
    docblock = DocBlock(block)

    print "<block>"
    for i in range(len(docblock.markers)):
	marker   = docblock.markers[i]
	contents = docblock.contents[i]
	
	print "<marker "+marker+">"
	doccontent = DocContent( contents )

	doccontent.dump()
		
	print "</marker>"
	
    print "</block>"        

def dump_doc_contents( block_list ):

    for block in block_list:
	docblock = DocBlock(block)
	print "<block>"
	
	for i in range(len(docblock.markers)):
	    print "<marker "+docblock.markers[i]+">"
	    content = DocContent( docblock.contents[i] )
	    content.dump()
	    print "</marker>"
	print "</block>"

def dump_html_1( block_list ):
    
    print html_header
    
    types = [ 'Type', 'Struct', 'FuncType', 'Function', 'Constant', 'Enumeration' ]
    for block in block_list:
    
          docblock = DocBlock(block[0],block[1])
          
          if len(docblock.markers) == 0:
              continue

          print block_header

          for i in range(len(docblock.markers)):
              marker   = docblock.markers[i]
              content  = docblock.contents[i]
              dcontent = DocContent( content )
              
              if marker=="Description":
                  print "<ul><p>"
                  dcontent.dump()
                  print "</p></ul>"
                  
              elif marker in types:
                  print "<h3><font color=blue>"+content[0]+"</font></h3>"
              else:
                  print "<h4>"+marker+"</h4>"
                  print "<ul><p>"
                  dcontent.dump_html()
                  print "</p></ul>"
                  
              print ""

          print block_footer
                        
          # print source code
          lines = block[1]
          l     = len(lines)-1
          while (l >= 0) and string.strip(lines[l])=="":
            l = l-1
          print source_header
          for line in lines[0:l+1]:
            print  line
          print source_footer
              
    print html_footer

    
    


def main(argv):
    """main program loop"""
    print "extracting comment blocks from sources .."
    list = make_block_list()
    
    dump_html_1( list )


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