#!/usr/bin/env python
#
# DocMaker is a very simple program used to generate HTML documentation
# from the source files of the FreeType packages.
#
# I should really be using regular expressions to do this, but hey,
# i'm too lazy right now, and the damn thing seems to work :-)
# - David
#
import fileinput, sys, string, glob
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 }
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>
"""
html_footer = """\
</body>
</html>"""
section_title_header = "<center><h1>"
section_title_footer = "</h1></center>"
code_header = "<font color=blue><pre>"
code_footer = "</pre></font>"
para_header = "<p>"
para_footer = "</p>"
block_header = "<center><table width=75%><tr><td>"
block_footer = "</td></tr></table><hr width=75%></center>"
description_header = "<center><table width=87%><tr><td>"
description_footer = "</td></tr></table></center><br>"
marker_header = "<center><table width=87% cellpadding=5><tr bgcolor=#EEEEFF><td><em><b>"
marker_inter = "</b></em></td></tr><tr><td>"
marker_footer = "</td></tr></table></center>"
source_header = "<center><table width=87%><tr bgcolor=#D6E8FF width=100%><td><pre>"
source_footer = "</pre></table></center><br>"
chapter_header = "<center><table width=75%><tr><td><h2>"
chapter_inter = "</h2><ul>"
chapter_footer = "</ul></td></tr></table></center>"
current_section = None
# This function is used to sort the index. It's a simple lexicographical
# sort, except that it places capital letters before small ones.
#
def index_sort( s1, s2 ):
if not s1:
return -1
if not s2:
return 1
l1 = len( s1 )
l2 = len( s2 )
m1 = string.lower( s1 )
m2 = string.lower( s2 )
for i in range( l1 ):
if i >= l2 or m1[i] > m2[i]:
return 1
if m1[i] < m2[i]:
return -1
if s1[i] < s2[i]:
return -1
if s1[i] > s2[i]:
return 1
if l2 > l1:
return -1
return 0
# 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
# 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 fields.
#
# 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.
#
# Note that markers can alternatively be written as "@MARKER:"
# instead of "<MARKER>". All marker identifiers are converted to
# lower case during parsing in order to simply sorting.
#
# We associate with each block the following source lines that do not
# begin with a comment. For example, the following:
#
# /**********************************
# *
# * <mytag> blabla
# *
# */
#
# bla_bla_bla
# bilip_bilip
#
# /* - this comment acts as a separator - */
#
# blo_blo_blo
#
#
# will only keep the first two lines of sources with
# the "blabla" block.
#
# However, the comment will be kept, with following source lines
# if it contains a starting '#' or '@' as in:
#
# /*@.....*/
# /*#.....*/
# /* @.....*/
# /* #.....*/
#
#############################################################################
#
# 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 = 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 ):
for line in self.lines:
print "--" + line
print ""
def get_identifier( self ):
# this function should never be called
#
return "UNKNOWN_CODE_IDENTIFIER!"
def dump_html( self, identifiers = None ):
# clean the last empty lines
#
l = len( self.lines ) - 1
while l > 0 and string.strip( self.lines[l - 1] ) == "":
l = l - 1
# the code footer should be directly appended to the last code
# line to avoid an additional blank line
#
sys.stdout.write( code_header )
for line in self.lines[0 : l+1]:
sys.stdout.write( '\n' + line )
sys.stdout.write( code_footer )
#############################################################################
#
# The DocParagraph is used to store text paragraphs.
# 'self.words' is simply a list of words for the paragraph.
#
# The paragraph is filled line by line by the parser.
#
class DocParagraph:
def __init__( self ):
self.words = []
def add( self, line ):
# Get rid of unwanted spaces in the paragraph.
#
# The following two lines are the same as
#
# self.words.extend( string.split( line ) )
#
# but older Python versions don't have the `extend' attribute.
#
last = len( self.words )
self.words[last : last] = string.split( line )
# This function is used to retrieve the first word of a given
# paragraph.
#
def get_identifier( self ):
if self.words:
return self.words[0]
# should never happen
#
return "UNKNOWN_PARA_IDENTIFIER!"
def get_words( self ):
return self.words[:]
def dump( self, identifiers = None ):
max_width = 50
cursor = 0
line = ""
extra = None
alphanum = string.lowercase + string.uppercase + string.digits + '_'
for word in self.words:
# process cross references if needed
#
if identifiers and word and word[0] == '@':
word = word[1 :]
# we need to find non-alphanumeric characters
#
i = len( word )
while i > 0 and not word[i - 1] in alphanum:
i = i - 1
if i > 0:
extra = word[i :]
word = word[0 : i]
block = identifiers.get( word )
if block:
word = '<a href="' + block.html_address() + '">' + word + '</a>'
else:
word = '?' + word
if cursor + len( word ) + 1 > max_width:
print line
cursor = 0
line = ""
line = line + word
if not extra:
line = line + " "
cursor = cursor + len( word ) + 1
# handle trailing periods, commas, etc. at the end of
# cross references.
#
if extra:
if cursor + len( extra ) + 1 > max_width:
print line
cursor = 0
line = ""
line = line + extra + " "
cursor = cursor + len( extra ) + 1
extra = None
if cursor > 0:
print line
# print "