From: Karl T. K. <ka...@us...> - 2001-03-06 00:12:19
|
Update of /cvsroot/linuxdc/docs/tools/ht2html In directory usw-pr-cvs1:/tmp/cvs-serv31423 Added Files: Banner.py HTParser.py LinkFixer.py LinuxDCGenerator.py Sidebar.py Skeleton.py Log Message: Added a proper toolset --- NEW FILE --- """Generate a site links table for use in a banner. """ import sys try: from cStringIO import StringIO except IOError: from StringIO import StringIO class Banner: def __init__(self, links, cols=4): """Initialize the Banner instance. This class is intended to be a mixin-class with Skeleton. links must be a list of 2-tuples of the form: (URL, text). If URL is None, then the text is not hyperlinked. These are arranged in a table in order, evenly in the specified number of columns. """ self.__links = links self.__cols = cols rows, leftover = divmod(len(links), self.__cols) if leftover: rows = rows + 1 self.__rows = rows def get_banner(self): stdout = sys.stdout html = StringIO() try: sys.stdout = html self.__start_table() self.__do_table() self.__end_table() finally: sys.stdout = stdout return html.getvalue() def __start_table(self): print '<!-- start of site links table -->' print '<TABLE WIDTH="100%" BORDER=0' print self.get_banner_attributes() print ' COLS=%s ROWS=%s BGCOLOR="%s">' % ( self.__cols, self.__rows, self.get_bgcolor()) print '<TR>' def __end_table(self): print '</TR>' print '</TABLE><!-- end of site links table -->' def __do_table(self): col = 0 for item in self.__links: if len(item) == 3: url, text, extra = item else: url, text = item extra = '' if not url: s = text + extra else: s = '<a href="%s">%s</a>%s' % (url, text, extra) if col >= self.__cols: # break the row print '</TR><TR>' col = 0 print ' <TD BGCOLOR="%s">' % self.get_lightshade() print s print ' </TD>' col = col + 1 # fill rest of row with non-breaking spaces. while col and col < self.__cols: print ' <TD BGCOLOR="%s">' % self.get_lightshade() print ' </TD>' col = col + 1 from Skeleton import _Skeleton class _Banner(_Skeleton, Banner): def __init__(self, links): Banner.__init__(self, links) def get_banner(self): return Banner.get_banner(self) if __name__ == '__main__': t = _Banner([('page1.html', 'First Page'), ('page2.html', 'Second Page'), ('page3.html', 'Third Page'), ('page4.html', 'Fourth Page'), (None, '<b>Fifth Page</b>'), ('page6.html', 'Sixth Page'), ]) print t.makepage() --- NEW FILE --- """HTParser -- parse a www.python.org .ht file. """ import os import string import re import rfc822 from socket import gethostbyaddr, gethostbyname, gethostname from types import StringType class HTParser(rfc822.Message): def __init__(self, filename, default_author=None): self.__filename = filename self.__fp = fp = open(filename) self.__extraheaders = {} rfc822.Message.__init__(self, fp) # # massage some standard headers we require # # title if not self.has_key('title'): parts = string.split(self.__filename, os.sep) self.__extraheaders['title'] = parts[-1] # author if not self.has_key('author'): if default_author is not None: author = default_author else: domainname = None h, a, ip = gethostbyaddr(gethostbyname(gethostname())) for host in [h] + a: i = string.find(host, '.') if i > 0: domainname = host[i+1:] break if domainname: author = 'webmaster@' + domainname else: author = 'MUST SUPPLY AN AUTHOR' self.__extraheaders['author'] = author # public ivar self.sidebar = [] # override so we can access our own internal dictionary if the message # doesn't have it def __getitem__(self, item): try: return rfc822.Message.__getitem__(self, item) except KeyError: return self.__extraheaders[item] # might be using an older rfc822 def get(self, name, default=None): if self.has_key(name): return self.getheader(name) elif self.__extraheaders.has_key(name): return self.__extraheaders[name] else: return default def process_sidebar(self): # first process all link files. Either we hard code the use of # ./links.h or we look for a Links: header. If the header exists, it # must explicitly enumerate links.h linkfiles = self.get('links', 'links.h') for file in string.split(linkfiles): try: fp = open(string.strip(file)) except IOError: continue data = fp.read() fp.close() self.__parse(data) # Other-links header specifies more links in-lined otherlinks = self.get('other-links') if otherlinks: self.__parse(otherlinks) # always have an email address self.sidebar.append('Email Us') author = self.get('author') # guaranteed email = self.get('author-email', author) self.sidebar.append(('mailto:' + email, author)) # regular expressions used in massage() cre = re.compile( r'(<h3>(?P<h3>.*?)</h3>)|' r'(<li>(<a href="?(?P<link>[^<>"]*)"?>(?P<contents>[^<>]*)</a>)?)' r'(?P<extra>[^\n]*)', re.DOTALL | re.IGNORECASE) def __parse(self, text): """Apply various bits of magic to the links in this list. """ start = 0 end = len(text) while 1: mo = self.cre.search(text, start) if not mo: ## if start <> end: ## s = string.strip(text[start:]) ## tags.append(s) break mstart = mo.start(0) ## s = string.strip(text[start:mstart]) ## if s: ## tags.append(s) h3, link, contents, extra = \ mo.group('h3', 'link', 'contents', 'extra') if link is None: link = '' if contents is None: contents = '' if h3: self.sidebar.append(string.strip(h3)) elif extra: l = map(string.strip, (link, contents)) l.append(extra) self.sidebar.append(tuple(l)) else: link = tuple(map(string.strip, (link, contents))) self.sidebar.append(link) start = mo.end(0) --- NEW FILE --- """LinkFixer class. Does two things. Takes as input entry lists as expected by the Sidebar and Banner classes, and returns a massaged list where any current links are boldified and turned into non-links. Also supports global string interpolation over the links using a dictionary. """ import sys import string from types import StringType class LinkFixer: def __init__(self, myurl, rootdir='.', relthis='.', verbose=0): self.__rootdir = rootdir self.__relthis = relthis self.__verbose = verbose self.msg('rootdir=%s, relthis=%s', rootdir, relthis) self.__myurl = self.normalize(myurl) def msg(self, fmt, *args): if self.__verbose: msg = fmt % args if msg[-1] <> '\n': msg = msg + '\n' sys.stdout.write(msg) def normalize(self, url): self.msg('url= %s', url) if url is None: return if url == './': url = 'index.html' elif url[-1] == '/': url = url + 'index.html' absurl = string.join([self.__rootdir, self.__relthis, url], '/') # normalize the path, kind of the way os.path.normpath() does. # urlparse ought to have something like this... parts = [] for p in string.split(absurl, '/'): if p == '.': continue if p == '..' and len(parts) > 0: del parts[-1] parts.append(p) absurl = string.join(parts, '/') self.msg('absurl= %s', absurl) return absurl def massage(self, links, dict=None, aboves=0): """Substitute in situ before massaging. With dict, do a dictionary substitution on only the URLs first. With aboves=1, boldify links if they are above myurl. """ for i in range(len(links)): item = links[i] if type(item) == StringType: continue if len(item) == 3: url, text, extra = item else: url, text = item extra = '' try: url = url % dict except TypeError: pass if url: absurl = self.normalize(url) else: absurl = None self.msg('%s is %s ==? %s', url, absurl, self.__myurl) if url is None: links[i] = (url, text, extra) elif absurl is None: links[i] = (absurl, text, extra) elif absurl == self.__myurl: links[i] = (None, '<b>' + text + '</b>', extra) elif aboves and self.above(absurl, self.__myurl): links[i] = (url, '<b>' + text + '</b>', extra) else: links[i] = (url, text, extra) def above(self, absurl, myurl): """Return true if absurl is above myurl.""" # Only do one level of checking, and don't match on the root, since # that's always going to be above everything else. myurl = self.normalize(myurl) i = string.rfind(myurl, '/') j = string.rfind(absurl, '/') if i > 0 and j > 0 and \ absurl[:j] == myurl[:i] and \ myurl[i+1:] <> 'index.html': return 1 return 0 ## while myurl: ## print ' ', self.normalize(myurl) ## if absurl == self.normalize(myurl + '/'): ## return 1 ## i = string.rfind(myurl, '/') ## if i < 0: ## return 0 ## myurl = myurl[:i] ## return 0 def rootdir(self): return self.__rootdir def relthis(self): return self.__relthis def myurl(self): return self.__myurl --- NEW FILE --- """Generates LinuxDC's style. """ import os from Skeleton import Skeleton from Sidebar import Sidebar, BLANKCELL from Banner import Banner from HTParser import HTParser from LinkFixer import LinkFixer author = "ka...@us..."; sitelinks = [ ('%(rootdir)s/index.html', 'Home'), ('%(rootdir)s/index.html', 'FAQ'), ('%(rootdir)s/index.html', 'Kernel HOWTO'), ('%(rootdir)s/index.html', 'Tools HOWTO') ] BLANKCELL = (None, " ") SEPARATOR = (None, "<hr size=10 noshade>") leftlinks = [ # ('http://linuxdc.sourceforge.net/', '''<center><img border=0 src="%(rootdir)s/images/LinuxPoweredSmall.gif"></center>'''), BLANKCELL, ('None', '<b>%(section)s</b>'), SEPARATOR, ('%(rootdir)s/index.html', 'Kernel installation HOWTO'), ('%(rootdir)s/index.html', 'Toolchain installation HOWTO'), ('%(rootdir)s/index.html', 'Unofficial DC Technical Manual'), ('%(rootdir)s/index.html', 'TODO list'), SEPARATOR ] class LinuxDCGenerator(Skeleton, Sidebar, Banner): def __init__(self, file, rootdir, relthis): self.__body = None root, ext = os.path.splitext(file) html = root + '.html' p = self.__parser = HTParser(file, author) f = self.__linkfixer = LinkFixer(html, rootdir, relthis) p.process_sidebar() # massage our links self.__d = {'rootdir': rootdir, 'section': self.get_section() } self.__linkfixer.massage(p.sidebar, self.__d) # tweak for i in leftlinks: if i[0]: p.sidebar.append((i[0] % self.__d, i[1] % self.__d)) else: p.sidebar.append(i) copyright = self.__parser.get('copyright', '2000-2001') p.sidebar.append((None, '© ' + copyright)) p.sidebar.append((None, 'The LinuxDC Team')) Sidebar.__init__(self, p.sidebar) # # fix up our site links, no relthis because the site links are # relative to the root of my web pages # sitelink_fixer = LinkFixer(f.myurl(), rootdir) sitelink_fixer.massage(sitelinks, self.__d, aboves=1) Banner.__init__(self, sitelinks, cols=2) def get_corner(self): rootdir = self.__linkfixer.rootdir() return ''' <center> <a href="%(rootdir)s/index.html"> <img border=0 src="%(rootdir)s/images/linuxdc.jpg"></a></center>''' \ % self.__d def get_corner_bgcolor(self): return 'black' def get_banner(self): return Banner.get_banner(self) def get_title(self): return self.__parser.get('title') def get_section(self): return self.__parser.get('section') def get_sidebar(self): return Sidebar.get_sidebar(self) def get_banner_attributes(self): return 'CELLSPACING=0 CELLPADDING=0' def get_body(self): if self.__body is None: self.__body = self.__parser.fp.read() return self.__body # python.org color scheme overrides def get_lightshade(self): return '#99ccff' def get_mediumshade(self): return '#3399ff' def get_darkshade(self): return '#003366' --- NEW FILE --- """Sidebar generator. """ import sys from types import StringType try: from cStringIO import StringIO except IOError: from StringIO import StringIO # a useful constant BLANKCELL = (None, ' ') class Sidebar: def __init__(self, links): """Initialize the Sidebar instance. This class is indented to be a mixin-class with Skeleton. links must be a list of elements for the sidebar. Each entry in the list can either be a string, indicating that the entry is a category header, or a 2-tuple of the form: (URL, text) indicating it is taken as a link to include under the category. If the entry is a two tuple, the URL can be None to indicate that there is no link to that text. """ self.links = links def get_sidebar(self): stdout = sys.stdout html = StringIO() try: sys.stdout = html self.__start_table() self.__do_link() self.__finish() finally: sys.stdout = stdout return html.getvalue() def __start_table(self): print '<!-- start of sidebar table -->' print '<TABLE WIDTH="100%" BORDER=0 CELLSPACING=0 CELLPADDING=3' print ' BGCOLOR="%s">' % self.get_bgcolor() def __finish(self): print '</TABLE><!-- end of sidebar table -->' def __do_link(self): done_one = 0 for item in self.links: if type(item) == StringType: # category header if done_one: # get some separation between header and last item print '<TR><TD BGCOLOR="%s"> ' % ( self.get_lightshade()) else: done_one = 1 print '<TR><TD BGCOLOR="%s"><B><FONT COLOR="%s">' % ( self.get_darkshade(), self.get_bgcolor()) print item print '</FONT></B></TD></TR>' else: if len(item) == 3: url, text, extra = item else: url, text = item extra = '' if url is None: s = text else: s = '<A HREF="%s">%s</A>' % (url, text) print '<TR><TD BGCOLOR="%s">' % self.get_lightshade() print '%s%s' % (s, extra) print '</TD></TR>' from Skeleton import _Skeleton from Banner import _Banner class _Sidebar(_Skeleton, _Banner, Sidebar): def __init__(self, sitelinks, toclinks): _Banner.__init__(self, sitelinks) Sidebar.__init__(self, toclinks) def get_sidebar(self): return Sidebar.get_sidebar(self) def get_banner(self): return _Banner.get_banner(self) if __name__ == '__main__': t = _Sidebar( # banner links [('page1.html', 'First Page'), ('page2.html', 'Second Page'), ('page3.html', 'Third Page'), ('page4.html', 'Fourth Page'), (None, '<b>Fifth Page</b>'), ('page6.html', 'Sixth Page'), ], # sidebar links ['Special Topics', ('topics.html', 'Topic Guides'), ('download.html', 'Downloads'), ('windows.html', 'Windows'), ('jpython.html', 'JPython'), ('tkinter.html', 'Tkinter'), ('emacs.html', 'Emacs'), 'See also', ('conferences.html', 'Python Conferences'), ('sitemap.html', 'Site map'), ('mirrors.html', 'Mirror sites'), (None, '<b>What is Python?</b>'), 'Exits', ('starship.html', '(New) Starship'), ('starship.html', 'Old Starship'), ('cnri.html', 'CNRI'), 'Email us', (None, 'For help with Python:'), ('help.html', 'pyt...@py...'), (None, 'For help with Website:'), ('web.html', 'web...@py...'), (None, '<br>'), ('pp.html', '[Python Powered]'), ]) print t.makepage() --- NEW FILE --- """Skeleton class. Should be sub-classed to provide basic generation of able-contained HTML document. """ from ht2html import __version__ import sys import time try: from cStringIO import StringIO except IOError: from StringIO import StringIO class Skeleton: # # for sub-classes to override # def get_banner(self): """Returns HTML for the top banner, or None if no banner. """ return None def get_sidebar(self): """Returns HTML for the left sidebar or None. """ return None def get_banner_width(self): """HTML `width' of banner column as a percentage. Should be a string that does not include the percent sign (e.g. "90"). This affects the column containing both the banner and body text (if they exist). """ return "90" def get_corner(self): """Returns HTML for the upper-left corner or None. Note that if get_banner() and get_sidebar() both return None, then get_corner() is ignored. Also if both get_banner() and get_sidebar() return a string, but get_corner() returns None, the smallest blank corner possible is emitted. """ return None def get_body(self): """Returns HTML for the internal document body. Note that using this method causes the get_sidebar() -- if there is one -- to run the full height of the page. If you don't want this, then make sure get_cont() returns a string. """ return '<b>Intentionally left blank</b>' def get_cont(self): """Returns HTML for the continuation of the body. When this method returns a string, and there is a get_sidebar(), the continuation text appears below the get_sidebar() and get_body() at the full width of the screen. If there is no get_sidebar(), then having a get_cont() is pointless. """ return None def get_title(self): """Return the title of the page. Required.""" return 'Intentionally left blank' def get_meta(self): """Return extra meta-data. Must be a string.""" return '' def get_headers(self): """Return extra header information. Must be a string.""" return '' def get_bgcolor(self): """Return the background color""" return '#ffffff' def get_fgcolor(self): """Return foreground color""" return '#000000' def get_linkcolor(self): """Return link color""" return '#0000bb' def get_vlinkcolor(self): """Return vlink color""" return '#551a8b' def get_alinkcolor(self): """Return alink color""" return '#ff0000' def get_corner_bgcolor(self): """Return the background color for the corner""" return self.get_lightshade() # Barry's prefs def get_lightshade(self): """Return lightest of 3 color scheme shade.""" return '#cdba96' def get_mediumshade(self): """Return middle of 3 color scheme shade.""" return '#cc9966' def get_darkshade(self): """Return darkest of 3 color scheme shade.""" return '#b78900' def get_body_attributes(self): """Return extra attributes for the body start tag.""" return 'TOPMARGIN="0" LEFTMARGIN="0" MARGINWIDTH="0" MARGINHEIGHT="0"' def get_banner_attributes(self): """Return extra attributes for the TABLE in the banner.""" return 'CELLSPACING=0 CELLPADDING=2' # Call this method def makepage(self): banner = self.get_banner() sidebar = self.get_sidebar() corner = self.get_corner() body = self.get_body() cont = self.get_cont() html = StringIO() stdout = sys.stdout closed = 0 try: sys.stdout = html self.__do_head() self.__start_body() print '<!-- start of page table -->' print '<TABLE WIDTH="100%" BORDER=0 CELLSPACING=0 CELLPADDING=0>' if banner is not None: print '<!-- start of banner row -->' print '<TR>' if corner is not None: self.__do_corner(corner) print '<!-- start of banner -->' print '<TD WIDTH="%s%%" BGCOLOR="%s">' % ( self.get_banner_width(), self.get_lightshade()) print banner print '</TD><!-- end of banner -->' print '</TR><!-- end of banner row -->' # if there is a body but no sidebar, then we'll just close the # table right here and put the body (and any cont) in the full # page. if there is a sidebar but no body, then we still create # the new row and just populate the body cell with a non-breaking # space. Watch out though because we don't want to close the # table twice if sidebar is None: print '</TABLE><!-- end of page table -->' closed = 1 else: print '<TR><!-- start of sidebar/body row -->' self.__do_sidebar(sidebar) if body is not None: if closed: print body else: self.__do_body(body) if not closed: print '</TR><!-- end of sidebar/body row -->' print '</TABLE><!-- end of page table -->' if cont is not None: self.__do_cont(cont) self.__finish_all() finally: sys.stdout = stdout return html.getvalue() def __do_corner(self, corner): print '<!-- start of corner cells -->' print '<TD WIDTH=150 VALIGN=CENTER BGCOLOR="%s">' % ( self.get_corner_bgcolor()) # it is important not to have a newline between the corner text and # the table close tag, otherwise layout is messed up if corner is None: print ' ', else: print corner, print '</TD>' print '<TD WIDTH=15 BGCOLOR="%s"> </TD><!--spacer-->' % ( self.get_lightshade()) print '<!-- end of corner cells -->' def __do_sidebar(self, sidebar): print '<!-- start of sidebar cells -->' print '<TD WIDTH=150 VALIGN=TOP BGCOLOR="%s">' % ( self.get_lightshade()) print sidebar print '</TD>' print '<TD WIDTH=15> </TD><!--spacer-->' print '<!-- end of sidebar cell -->' def __do_body(self, body): print '<!-- start of body cell -->' print '<TD VALIGN=TOP WIDTH="%s%%"><BR>' % ( self.get_banner_width()) print body print '</TD><!-- end of body cell -->' def __do_cont(self, cont): print '<!-- start of continued wide-body text -->' print cont print '<!-- end of continued wide-body text -->' def __do_head(self): """Return the HTML <head> stuff.""" print '''\ <HTML> <!-- THIS PAGE IS AUTOMATICALLY GENERATED. DO NOT EDIT. --> <!-- %(time)s --> <!-- USING HT2HTML %(version)s --> <!-- SEE http://www.python.org/~bwarsaw/software/pyware.html --> <!-- User-specified headers: Title: %(title)s %(headers)s --> <HEAD> <TITLE>%(title)s</TITLE> %(meta)s </HEAD>''' % {'title' : self.get_title(), 'headers': self.get_headers(), 'meta' : self.get_meta(), 'time' : time.ctime(time.time()), 'version': __version__, } def __start_body(self): print '''\ <BODY BGCOLOR="%(bgcolor)s" TEXT="%(fgcolor)s" %(extraattrs)s LINK="%(linkcolor)s" VLINK="%(vlinkcolor)s" ALINK="%(alinkcolor)s">''' % { 'bgcolor' : self.get_bgcolor(), 'fgcolor' : self.get_fgcolor(), 'linkcolor' : self.get_linkcolor(), 'vlinkcolor': self.get_vlinkcolor(), 'alinkcolor': self.get_alinkcolor(), 'extraattrs': self.get_body_attributes(), } def __finish_all(self): print '</BODY></HTML>' # test script class _Skeleton(Skeleton): def get_banner(self): return '<b>The Banner</b>' def get_sidebar(self): return '''<ul><li>Sidebar line 1 <li>Sidebar line 2 <li>Sidebar line 3 </ul>''' def get_corner(self): return '<center><em>CORNER</em></center>' def get_body(self): return 'intentionally left blank ' * 110 def get_cont(self): return 'wide stuff ' * 100 def get_corner_bgcolor(self): return 'yellow' def get_banner_width(self): return "80" if __name__ == '__main__': t = _Skeleton() print t.makepage() |