Got a new version of StructuredText from Zope's CVS.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@9995 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2001-05-04 18:28:27 +00:00
parent e42c7b8567
commit ddfc587a2e
15 changed files with 1356 additions and 1564 deletions

View File

@@ -85,6 +85,7 @@
import re, ST, STDOM import re, ST, STDOM
from string import split, join, replace, expandtabs, strip, find from string import split, join, replace, expandtabs, strip, find
from STletters import letters,lettpunc,punctuations
StringType=type('') StringType=type('')
ListType=type([]) ListType=type([])
@@ -138,6 +139,12 @@ class StructuredTextSection(ST.StructuredTextParagraph):
(self, StructuredTextSectionTitle(src), subs), (self, StructuredTextSectionTitle(src), subs),
kw) kw)
def getColorizableTexts(self):
return self._src.getColorizableTexts()
def setColorizableTexts(self,src):
self._src.setColorizableTexts(src)
# a StructuredTextTable holds StructuredTextRows # a StructuredTextTable holds StructuredTextRows
class StructuredTextTable(ST.StructuredTextDocument): class StructuredTextTable(ST.StructuredTextDocument):
""" """
@@ -430,6 +437,7 @@ class DocumentClass:
return result return result
def doc_table(self,paragraph, expr = re.compile('(\s*)([||]+)').match): def doc_table(self,paragraph, expr = re.compile('(\s*)([||]+)').match):
#print "paragraph=>", type(paragraph), paragraph, paragraph._src
text = paragraph.getColorizableTexts()[0] text = paragraph.getColorizableTexts()[0]
m = expr(text) m = expr(text)
@@ -465,7 +473,7 @@ class DocumentClass:
tmp = [] tmp = []
for item in row: for item in row:
if item: if item:
tmp.append(item,cspan) tmp.append((item,cspan))
cspan = 0 cspan = 0
else: else:
cspan = cspan + 1 cspan = cspan + 1
@@ -493,7 +501,7 @@ class DocumentClass:
def doc_numbered( def doc_numbered(
self, paragraph, self, paragraph,
expr = re.compile('(\s*[a-zA-Z]+\.)|(\s*[0-9]+\.)|(\s*[0-9]+\s+)').match): expr = re.compile('(\s*[%s]+\.)|(\s*[0-9]+\.)|(\s*[0-9]+\s+)' % letters).match):
# This is the old expression. It had a nasty habit # This is the old expression. It had a nasty habit
# of grabbing paragraphs that began with a single # of grabbing paragraphs that began with a single
@@ -518,7 +526,7 @@ class DocumentClass:
def doc_description( def doc_description(
self, paragraph, self, paragraph,
delim = re.compile('\s+--\s+').search, delim = re.compile('\s+--\s+').search,
nb=re.compile(r'[^\0- ]').search, nb=re.compile(r'[^\000- ]').search,
): ):
top=paragraph.getColorizableTexts()[0] top=paragraph.getColorizableTexts()[0]
@@ -542,7 +550,7 @@ class DocumentClass:
delim=d) delim=d)
def doc_header(self, paragraph, def doc_header(self, paragraph,
expr = re.compile('[ a-zA-Z0-9.:/,-_*<>\?\'\"]+').match expr = re.compile('[ %s0-9.:/,-_*<>\?\'\"]+' % letters).match
): ):
subs=paragraph.getSubparagraphs() subs=paragraph.getSubparagraphs()
if not subs: return None if not subs: return None
@@ -562,9 +570,9 @@ class DocumentClass:
def doc_literal( def doc_literal(
self, s, self, s,
expr=re.compile( expr=re.compile(
"(?:\s|^)'" # open "(?:\s|^)'" # open
"([^ \t\n\r\f\v']|[^ \t\n\r\f\v'][^\n']*[^ \t\n\r\f\v'])" # contents "([^ \t\n\r\f\v']|[^ \t\n\r\f\v'][^\n']*[^ \t\n\r\f\v'])" # contents
"'(?:\s|[,.;:!?]|$)" # close "'(?:\s|[,.;:!?]|$)" # close
).search): ).search):
r=expr(s) r=expr(s)
@@ -576,7 +584,7 @@ class DocumentClass:
def doc_emphasize( def doc_emphasize(
self, s, self, s,
expr = re.compile('\s*\*([ \na-zA-Z0-9.:/;,\'\"\?]+)\*(?!\*|-)').search expr = re.compile('\s*\*([ \n%s0-9]+)\*(?!\*|-)' % lettpunc).search
): ):
r=expr(s) r=expr(s)
@@ -589,7 +597,7 @@ class DocumentClass:
def doc_inner_link(self, def doc_inner_link(self,
s, s,
expr1 = re.compile("\.\.\s*").search, expr1 = re.compile("\.\.\s*").search,
expr2 = re.compile("\[[a-zA-Z0-9]+\]").search): expr2 = re.compile("\[[%s0-9]+\]" % letters).search):
# make sure we dont grab a named link # make sure we dont grab a named link
if expr2(s) and expr1(s): if expr2(s) and expr1(s):
@@ -600,7 +608,7 @@ class DocumentClass:
return None return None
else: else:
# the .. is somewhere else, ignore it # the .. is somewhere else, ignore it
return (StructuredTextInnerLink(s[start2+1,end2-1],start2,end2)) return (StructuredTextInnerLink(s[start2+1:end2-1]),start2,end2)
return None return None
elif expr2(s) and not expr1(s): elif expr2(s) and not expr1(s):
start,end = expr2(s).span() start,end = expr2(s).span()
@@ -609,7 +617,7 @@ class DocumentClass:
def doc_named_link(self, def doc_named_link(self,
s, s,
expr=re.compile("(\.\.\s)(\[[a-zA-Z0-9]+\])").search): expr=re.compile("(\.\.\s)(\[[%s0-9]+\])" % letters).search):
result = expr(s) result = expr(s)
if result: if result:
@@ -624,7 +632,7 @@ class DocumentClass:
def doc_underline(self, def doc_underline(self,
s, s,
expr=re.compile("\_([a-zA-Z0-9\s\.,\?\/]+)\_").search): expr=re.compile("\s+\_([0-9%s ]+)\_" % lettpunc).search):
result = expr(s) result = expr(s)
if result: if result:
@@ -636,7 +644,7 @@ class DocumentClass:
def doc_strong(self, def doc_strong(self,
s, s,
expr = re.compile('\s*\*\*([ \na-zA-Z0-9.:/;\-,!\?\'\"]+)\*\*').search expr = re.compile('\s*\*\*([ \n%s0-9]+)\*\*' % lettpunc).search
): ):
r=expr(s) r=expr(s)
@@ -649,19 +657,9 @@ class DocumentClass:
def doc_href( def doc_href(
self, s, self, s,
expr1 = re.compile("(\"[ a-zA-Z0-9\n\-\.\,\;\(\)\/\:\/]+\")(:)([a-zA-Z0-9\:\/\.\~\-]+)([,]*\s*)").search, expr1 = re.compile("(\"[ %s0-9\n\-\.\,\;\(\)\/\:\/\*\']+\")(:)([a-zA-Z0-9\@\.\,\?\!\/\:\;\-\#\~]+)([,]*\s*)" % letters).search,
expr2 = re.compile('(\"[ a-zA-Z0-9\n\-\.\:\;\(\)\/]+\")([,]+\s+)([a-zA-Z0-9\@\.\,\?\!\/\:\;\-\#]+)(\s*)').search): expr2 = re.compile('(\"[ %s0-9\n\-\.\:\;\(\)\/\*\']+\")([,]+\s+)([a-zA-Z0-9\@\.\,\?\!\/\:\;\-\#\~]+)(\s*)' % letters).search):
#expr1=re.compile('\"([ a-zA-Z0-9.:/;,\n\~\(\)\-]+)\"'
# ':'
# '([a-zA-Z0-9.:/;,\n\~]+)(?=(\s+|\.|\!|\?))'
# ).search,
#expr2=re.compile('\"([ a-zA-Z0-9./:]+)\"'
# ',\s+'
# '([ a-zA-Z0-9@.:/;]+)(?=(\s+|\.|\!|\?))'
# ).search,
punctuation = re.compile("[\,\.\?\!\;]+").match
r=expr1(s) or expr2(s) r=expr1(s) or expr2(s)
if r: if r:
@@ -671,19 +669,16 @@ class DocumentClass:
start,e = r.span(1) start,e = r.span(1)
name = s[start:e] name = s[start:e]
name = replace(name,'"','',2) name = replace(name,'"','',2)
#start = start + 1
st,end = r.span(3) st,end = r.span(3)
if punctuation(s[end-1:end]):
end = end -1 if s[end-1:end] in punctuations: end-=1
link = s[st:end] link = s[st:end]
#end = end - 1
# name is the href title, link is the target # name is the href title, link is the target
# of the href # of the href
return (StructuredTextLink(name, href=link), return (StructuredTextLink(name, href=link),
start, end) start, end)
#return (StructuredTextLink(s[start:end], href=s[start:end]),
# start, end)
else: else:
return None return None

View File

@@ -0,0 +1,625 @@
#! /usr/bin/env python -- # -*- python -*-
##############################################################################
#
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# This license has been certified as Open Source(tm).
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions in source code must retain the above copyright
# notice, this list of conditions, and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# 3. Digital Creations requests that attribution be given to Zope
# in any manner possible. Zope includes a "Powered by Zope"
# button that is installed by default. While it is not a license
# violation to remove this button, it is requested that the
# attribution remain. A significant investment has been put
# into Zope, and this effort will continue if the Zope community
# continues to grow. This is one way to assure that growth.
#
# 4. All advertising materials and documentation mentioning
# features derived from or use of this software must display
# the following acknowledgement:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# In the event that the product being advertised includes an
# intact Zope distribution (with copyright and license included)
# then this clause is waived.
#
# 5. Names associated with Zope or Digital Creations must not be used to
# endorse or promote products derived from this software without
# prior written permission from Digital Creations.
#
# 6. Modified redistributions of any form whatsoever must retain
# the following acknowledgment:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# Intact (re-)distributions of any official Zope release do not
# require an external acknowledgement.
#
# 7. Modifications are encouraged but must be packaged separately as
# patches to official Zope releases. Distributions that do not
# clearly separate the patches from the original work must be clearly
# labeled as unofficial distributions. Modifications which do not
# carry the name Zope may be packaged in any form, as long as they
# conform to all of the clauses above.
#
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations. Specific
# attributions are listed in the accompanying credits file.
#
##############################################################################
'''Structured Text Manipulation
Parse a structured text string into a form that can be used with
structured formats, like html.
Structured text is text that uses indentation and simple
symbology to indicate the structure of a document.
A structured string consists of a sequence of paragraphs separated by
one or more blank lines. Each paragraph has a level which is defined
as the minimum indentation of the paragraph. A paragraph is a
sub-paragraph of another paragraph if the other paragraph is the last
preceding paragraph that has a lower level.
Special symbology is used to indicate special constructs:
- A single-line paragraph whose immediately succeeding paragraphs are lower
level is treated as a header.
- A paragraph that begins with a '-', '*', or 'o' is treated as an
unordered list (bullet) element.
- A paragraph that begins with a sequence of digits followed by a
white-space character is treated as an ordered list element.
- A paragraph that begins with a sequence of sequences, where each
sequence is a sequence of digits or a sequence of letters followed
by a period, is treated as an ordered list element.
- A paragraph with a first line that contains some text, followed by
some white-space and '--' is treated as
a descriptive list element. The leading text is treated as the
element title.
- Sub-paragraphs of a paragraph that ends in the word 'example' or the
word 'examples', or '::' is treated as example code and is output as is.
- Text enclosed single quotes (with white-space to the left of the
first quote and whitespace or punctuation to the right of the second quote)
is treated as example code.
- Text surrounded by '*' characters (with white-space to the left of the
first '*' and whitespace or punctuation to the right of the second '*')
is emphasized.
- Text surrounded by '**' characters (with white-space to the left of the
first '**' and whitespace or punctuation to the right of the second '**')
is made strong.
- Text surrounded by '_' underscore characters (with whitespace to the left
and whitespace or punctuation to the right) is made underlined.
- Text encloded by double quotes followed by a colon, a URL, and concluded
by punctuation plus white space, *or* just white space, is treated as a
hyper link. For example:
"Zope":http://www.zope.org/ is ...
Is interpreted as '<a href="http://www.zope.org/">Zope</a> is ....'
Note: This works for relative as well as absolute URLs.
- Text enclosed by double quotes followed by a comma, one or more spaces,
an absolute URL and concluded by punctuation plus white space, or just
white space, is treated as a hyper link. For example:
"mail me", mailto:amos@digicool.com.
Is interpreted as '<a href="mailto:amos@digicool.com">mail me</a>.'
- Text enclosed in brackets which consists only of letters, digits,
underscores and dashes is treated as hyper links within the document.
For example:
As demonstrated by Smith [12] this technique is quite effective.
Is interpreted as '... by Smith <a href="#12">[12]</a> this ...'. Together
with the next rule this allows easy coding of references or end notes.
- Text enclosed in brackets which is preceded by the start of a line, two
periods and a space is treated as a named link. For example:
.. [12] "Effective Techniques" Smith, Joe ...
Is interpreted as '<a name="12">[12]</a> "Effective Techniques" ...'.
Together with the previous rule this allows easy coding of references or
end notes.
- A paragraph that has blocks of text enclosed in '||' is treated as a
table. The text blocks correspond to table cells and table rows are
denoted by newlines. By default the cells are center aligned. A cell
can span more than one column by preceding a block of text with an
equivalent number of cell separators '||'. Newlines and '|' cannot
be a part of the cell text. For example:
|||| **Ingredients** ||
|| *Name* || *Amount* ||
||Spam||10||
||Eggs||3||
is interpreted as::
<TABLE BORDER=1 CELLPADDING=2>
<TR>
<TD ALIGN=CENTER COLSPAN=2> <strong>Ingredients</strong> </TD>
</TR>
<TR>
<TD ALIGN=CENTER COLSPAN=1> <em>Name</em> </TD>
<TD ALIGN=CENTER COLSPAN=1> <em>Amount</em> </TD>
</TR>
<TR>
<TD ALIGN=CENTER COLSPAN=1>Spam</TD>
<TD ALIGN=CENTER COLSPAN=1>10</TD>
</TR>
<TR>
<TD ALIGN=CENTER COLSPAN=1>Eggs</TD>
<TD ALIGN=CENTER COLSPAN=1>3</TD>
</TR>
</TABLE>
'''
import ts_regex
import regex
from ts_regex import gsub
from string import split, join, strip, find
import string,re
def untabify(aString,
indent_tab=ts_regex.compile('\(\n\|^\)\( *\)\t').search_group,
):
'''\
Convert indentation tabs to spaces.
'''
result=''
rest=aString
while 1:
ts_results = indent_tab(rest, (1,2))
if ts_results:
start, grps = ts_results
lnl=len(grps[0])
indent=len(grps[1])
result=result+rest[:start]
rest="\n%s%s" % (' ' * ((indent/8+1)*8),
rest[start+indent+1+lnl:])
else:
return result+rest
def indent(aString, indent=2):
"""Indent a string the given number of spaces"""
r=split(untabify(aString),'\n')
if not r: return ''
if not r[-1]: del r[-1]
tab=' '*level
return "%s%s\n" % (tab,join(r,'\n'+tab))
def reindent(aString, indent=2, already_untabified=0):
"reindent a block of text, so that the minimum indent is as given"
if not already_untabified: aString=untabify(aString)
l=indent_level(aString)[0]
if indent==l: return aString
r=[]
append=r.append
if indent > l:
tab=' ' * (indent-l)
for s in split(aString,'\n'): append(tab+s)
else:
l=l-indent
for s in split(aString,'\n'): append(s[l:])
return join(r,'\n')
def indent_level(aString,
indent_space=ts_regex.compile('\n\( *\)').search_group,
):
'''\
Find the minimum indentation for a string, not counting blank lines.
'''
start=0
text='\n'+aString
indent=l=len(text)
while 1:
ts_results = indent_space(text, (1,2), start)
if ts_results:
start, grps = ts_results
i=len(grps[0])
start=start+i+1
if start < l and text[start] != '\n': # Skip blank lines
if not i: return (0,aString)
if i < indent: indent = i
else:
return (indent,aString)
def paragraphs(list,start):
l=len(list)
level=list[start][0]
i=start+1
while i < l and list[i][0] > level: i=i+1
return i-1-start
def structure(list):
if not list: return []
i=0
l=len(list)
r=[]
while i < l:
sublen=paragraphs(list,i)
i=i+1
r.append((list[i-1][1],structure(list[i:i+sublen])))
i=i+sublen
return r
class Table:
CELL=' <TD ALIGN=CENTER COLSPAN=%i>%s</TD>\n'
ROW=' <TR>\n%s </TR>\n'
TABLE='\n<TABLE BORDER=1 CELLPADDING=2>\n%s</TABLE>'
def create(self,aPar,
td_reg=re.compile(r'[ \t\n]*\|\|([^\0x00|]*)')
):
'''parses a table and returns nested list representing the
table'''
self.table=[]
text=filter(None,split(aPar,'\n'))
for line in text:
row=[]
while 1:
mo = td_reg.match(line)
if not mo: return 0
pos = mo.end(1)
row.append(mo.group(1))
if pos==len(line):break
line=line[pos:]
self.table.append(row)
return 1
def html(self):
'''Creates an HTML representation of table'''
htmltable=[]
for row in self.table:
htmlrow=[]
colspan=1
for cell in row:
if cell=='':
colspan=colspan+1
continue
else:
htmlrow.append(self.CELL%(colspan,cell))
colspan=1
htmltable.append(self.ROW%join(htmlrow,''))
return self.TABLE%join(htmltable,'')
table=Table()
class StructuredText:
"""Model text as structured collection of paragraphs.
Structure is implied by the indentation level.
This class is intended as a base classes that do actual text
output formatting.
"""
def __init__(self, aStructuredString, level=0,
paragraph_divider=regex.compile('\(\r?\n *\)+\r?\n'),
):
'''Convert a structured text string into a structured text object.
Aguments:
aStructuredString -- The string to be parsed.
level -- The level of top level headings to be created.
'''
pat = ' \"([%s0-9-_,./?=@~&]*)\":' % string.letters+ \
'([-:%s0-9_,./?=@#~&]*?)' % string.letters + \
'([.:?;] )'
p_reg = re.compile(pat,re.M)
aStructuredString = p_reg.sub(r'<a href="\2">\1</a>\3 ' , aStructuredString)
pat = ' \"([%s0-9-_,./?=@~&]*)\", ' % string.letters+ \
'([-:%s0-9_,./?=@#~&]*?)' % string.letters + \
'([.:?;] )'
p_reg = re.compile(pat,re.M)
aStructuredString = p_reg.sub(r'<a href="\2">\1</a>\3 ' , aStructuredString)
protoless = find(aStructuredString, '<a href=":')
if protoless != -1:
aStructuredString = re.sub('<a href=":', '<a href="',
aStructuredString)
self.level=level
paragraphs=ts_regex.split(untabify(aStructuredString),
paragraph_divider)
paragraphs=map(indent_level,paragraphs)
self.structure=structure(paragraphs)
def __str__(self):
return str(self.structure)
ctag_prefix=r'([\x00- \\(]|^)'
ctag_suffix=r'([\x00- ,.:;!?\\)]|$)'
ctag_middle=r'[%s]([^\x00- %s][^%s]*[^\x00- %s]|[^%s])[%s]'
ctag_middl2=r'[%s][%s]([^\x00- %s][^%s]*[^\x00- %s]|[^%s])[%s][%s]'
def ctag(s,
em=re.compile(
ctag_prefix+(ctag_middle % (("*",)*6) )+ctag_suffix),
strong=re.compile(
ctag_prefix+(ctag_middl2 % (("*",)*8))+ctag_suffix),
under=re.compile(
ctag_prefix+(ctag_middle % (("_",)*6) )+ctag_suffix),
code=re.compile(
ctag_prefix+(ctag_middle % (("\'",)*6))+ctag_suffix),
):
if s is None: s=''
s=strong.sub(r'\1<strong>\2</strong>\3',s)
s=under.sub( r'\1<u>\2</u>\3',s)
s=code.sub( r'\1<code>\2</code>\3',s)
s=em.sub( r'\1<em>\2</em>\3',s)
return s
class HTML(StructuredText):
'''\
An HTML structured text formatter.
'''\
def __str__(self,
extra_dl=re.compile("</dl>\n<dl>"),
extra_ul=re.compile("</ul>\n<ul>"),
extra_ol=re.compile("</ol>\n<ol>"),
):
'''\
Return an HTML string representation of the structured text data.
'''
s=self._str(self.structure,self.level)
s=extra_dl.sub('\n',s)
s=extra_ul.sub('\n',s)
s=extra_ol.sub('\n',s)
return s
def ul(self, before, p, after):
if p: p="<p>%s</p>" % strip(ctag(p))
return ('%s<ul><li>%s\n%s\n</li></ul>\n'
% (before,p,after))
def ol(self, before, p, after):
if p: p="<p>%s</p>" % strip(ctag(p))
return ('%s<ol><li>%s\n%s\n</li></ol>\n'
% (before,p,after))
def dl(self, before, t, d, after):
return ('%s<dl><dt>%s</dt><dd><p>%s</p>\n%s\n</dd></dl>\n'
% (before,ctag(t),ctag(d),after))
def head(self, before, t, level, d):
if level > 0 and level < 6:
return ('%s<h%d>%s</h%d>\n%s\n'
% (before,level,strip(ctag(t)),level,d))
t="<p><strong>%s</strong></p>" % strip(ctag(t))
return ('%s<dl><dt>%s\n</dt><dd>%s\n</dd></dl>\n'
% (before,t,d))
def normal(self,before,p,after):
return '%s<p>%s</p>\n%s\n' % (before,ctag(p),after)
def pre(self,structure,tagged=0):
if not structure: return ''
if tagged:
r=''
else:
r='<PRE>\n'
for s in structure:
r="%s%s\n\n%s" % (r,html_quote(s[0]),self.pre(s[1],1))
if not tagged: r=r+'</PRE>\n'
return r
def table(self,before,table,after):
return '%s<p>%s</p>\n%s\n' % (before,ctag(table),after)
def _str(self,structure,level,
# Static
bullet=ts_regex.compile('[ \t\n]*[o*-][ \t\n]+\([^\0]*\)'
).match_group,
example=ts_regex.compile('[\0- ]examples?:[\0- ]*$'
).search,
dl=ts_regex.compile('\([^\n]+\)[ \t]+--[ \t\n]+\([^\0]*\)'
).match_group,
nl=ts_regex.compile('\n').search,
ol=ts_regex.compile(
'[ \t]*\(\([0-9]+\|[%s]+\)[.)]\)+[ \t\n]+\([^\0]*\|$\)' % string.letters
).match_group,
olp=ts_regex.compile('[ \t]*([0-9]+)[ \t\n]+\([^\0]*\|$\)'
).match_group,
):
r=''
for s in structure:
ts_results = bullet(s[0], (1,))
if ts_results:
p = ts_results[1]
if s[0][-2:]=='::' and s[1]: ps=self.pre(s[1])
else: ps=self._str(s[1],level)
r=self.ul(r,p,ps)
continue
ts_results = ol(s[0], (3,))
if ts_results:
p = ts_results[1]
if s[0][-2:]=='::' and s[1]: ps=self.pre(s[1])
else: ps=self._str(s[1],level)
r=self.ol(r,p,ps)
continue
ts_results = olp(s[0], (1,))
if ts_results:
p = ts_results[1]
if s[0][-2:]=='::' and s[1]: ps=self.pre(s[1])
else: ps=self._str(s[1],level)
r=self.ol(r,p,ps)
continue
ts_results = dl(s[0], (1,2))
if ts_results:
t,d = ts_results[1]
r=self.dl(r,t,d,self._str(s[1],level))
continue
if example(s[0]) >= 0 and s[1]:
# Introduce an example, using pre tags:
r=self.normal(r,s[0],self.pre(s[1]))
continue
if s[0][-2:]=='::' and s[1]:
# Introduce an example, using pre tags:
r=self.normal(r,s[0][:-1],self.pre(s[1]))
continue
if table.create(s[0]):
## table support.
r=self.table(r,table.html(),self._str(s[1],level))
continue
else:
if nl(s[0]) < 0 and s[1] and s[0][-1:] != ':':
# Treat as a heading
t=s[0]
r=self.head(r,t,level,
self._str(s[1],level and level+1))
else:
r=self.normal(r,s[0],self._str(s[1],level))
return r
def html_quote(v,
character_entities=(
(re.compile('&'), '&amp;'),
(re.compile("<"), '&lt;' ),
(re.compile(">"), '&gt;' ),
(re.compile('"'), '&quot;')
)): #"
text=str(v)
for re,name in character_entities:
text=re.sub(name,text)
return text
def html_with_references(text, level=1):
text = re.sub(
r'[\0\n]\.\. \[([0-9_%s-]+)\]' % string.letters,
r'\n <a name="\1">[\1]</a>',
text)
text = re.sub(
r'([\x00- ,])\[(?P<ref>[0-9_%s-]+)\]([\x00- ,.:])' % string.letters,
r'\1<a href="#\2">[\2]</a>\3',
text)
text = re.sub(
r'([\0- ,])\[([^]]+)\.html\]([\0- ,.:])',
r'\1<a href="\2.html">[\2]</a>\3',
text)
return HTML(text,level=level)
def main():
import sys, getopt
opts,args=getopt.getopt(sys.argv[1:],'twl')
if args:
[infile]=args
s=open(infile,'r').read()
else:
s=sys.stdin.read()
if opts:
if filter(lambda o: o[0]=='-w', opts):
print 'Content-Type: text/html\n'
if filter(lambda o: o[0]=='-l', opts):
import locale
locale.setlocale(locale.LC_ALL,"")
if s[:2]=='#!':
s=re.sub('^#![^\n]+','',s)
mo = re.compile('([\0-\n]*\n)').match(s)
if mo is not None:
s = s[len(mo.group(0)) :]
s=str(html_with_references(s))
if s[:4]=='<h1>':
t=s[4:find(s,'</h1>')]
s='''<html><head><title>%s</title>
</head><body>
%s
</body></html>
''' % (t,s)
print s
else:
print html_with_references(s)
if __name__=="__main__": main()

View File

@@ -88,238 +88,245 @@ from string import join, split, find, lstrip
class DocBookClass: class DocBookClass:
element_types={ element_types={
'#text': '_text', '#text': '_text',
'StructuredTextDocument': 'document', 'StructuredTextDocument': 'document',
'StructuredTextParagraph': 'paragraph', 'StructuredTextParagraph': 'paragraph',
'StructuredTextExample': 'example', 'StructuredTextExample': 'example',
'StructuredTextBullet': 'bullet', 'StructuredTextBullet': 'bullet',
'StructuredTextNumbered': 'numbered', 'StructuredTextNumbered': 'numbered',
'StructuredTextDescription': 'description', 'StructuredTextDescription': 'description',
'StructuredTextDescriptionTitle': 'descriptionTitle', 'StructuredTextDescriptionTitle': 'descriptionTitle',
'StructuredTextDescriptionBody': 'descriptionBody', 'StructuredTextDescriptionBody': 'descriptionBody',
'StructuredTextSection': 'section', 'StructuredTextSection': 'section',
'StructuredTextSectionTitle': 'sectionTitle', 'StructuredTextSectionTitle': 'sectionTitle',
'StructuredTextLiteral': 'literal', 'StructuredTextLiteral': 'literal',
'StructuredTextEmphasis': 'emphasis', 'StructuredTextEmphasis': 'emphasis',
'StructuredTextStrong': 'strong', 'StructuredTextStrong': 'strong',
'StructuredTextLink': 'link', 'StructuredTextLink': 'link',
'StructuredTextXref': 'xref', 'StructuredTextXref': 'xref',
} 'StructuredTextSGML': 'sgml',
}
def dispatch(self, doc, level, output): def dispatch(self, doc, level, output):
getattr(self, self.element_types[doc.getNodeName()])(doc, level, output) getattr(self, self.element_types[doc.getNodeName()])(doc, level, output)
def __call__(self, doc, level=1): def __call__(self, doc, level=1):
r=[] r=[]
self.dispatch(doc, level-1, r.append) self.dispatch(doc, level-1, r.append)
return join(r,'') return join(r,'')
def _text(self, doc, level, output): def _text(self, doc, level, output):
if doc.getNodeName() == 'StructuredTextLiteral': if doc.getNodeName() == 'StructuredTextLiteral':
output(doc.getNodeValue()) output(doc.getNodeValue())
else: else:
output(lstrip(doc.getNodeValue())) output(lstrip(doc.getNodeValue()))
def document(self, doc, level, output): def document(self, doc, level, output):
output('<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN">\n') output('<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN">\n')
output('<book>\n') output('<book>\n')
children=doc.getChildNodes() children=doc.getChildNodes()
if (children and if (children and
children[0].getNodeName() == 'StructuredTextSection'): children[0].getNodeName() == 'StructuredTextSection'):
output('<title>%s</title>' % children[0].getChildNodes()[0].getNodeValue()) output('<title>%s</title>' % children[0].getChildNodes()[0].getNodeValue())
for c in children: for c in children:
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</book>\n')
def section(self, doc, level, output):
output('\n<section>\n')
children=doc.getChildNodes()
for c in children:
getattr(self, self.element_types[c.getNodeName()])(c, level+1, output)
output('\n</section>\n')
def sectionTitle(self, doc, level, output):
output('<title>')
for c in doc.getChildNodes():
try:
getattr(self, self.element_types[c.getNodeName()])(c, level, output) getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</book>\n') except:
print "failed", c.getNodeName(), c
output('</title>\n')
def section(self, doc, level, output): def description(self, doc, level, output):
output('\n<sect%s>\n' % (level + 1)) p=doc.getPreviousSibling()
children=doc.getChildNodes() if p is None or p.getNodeName() is not doc.getNodeName():
for c in children: output('<variablelist>\n')
getattr(self, self.element_types[c.getNodeName()])(c, level+1, output) for c in doc.getChildNodes():
output('\n</sect%s>\n' % (level + 1)) getattr(self, self.element_types[c.getNodeName()])(c, level, output)
n=doc.getNextSibling()
if n is None or n.getNodeName() is not doc.getNodeName():
output('</variablelist>\n')
def sectionTitle(self, doc, level, output): def descriptionTitle(self, doc, level, output):
output('<title>') output('<varlistentry><term>\n')
for c in doc.getChildNodes(): for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output) getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</title>\n') output('</term>\n')
def description(self, doc, level, output): def descriptionBody(self, doc, level, output):
p=doc.getPreviousSibling() output('<listitem><para>\n')
if p is None or p.getNodeName() is not doc.getNodeName(): for c in doc.getChildNodes():
output('<variablelist>\n') getattr(self, self.element_types[c.getNodeName()])(c, level, output)
for c in doc.getChildNodes(): output('</para></listitem>\n')
getattr(self, self.element_types[c.getNodeName()])(c, level, output) output('</varlistentry>\n')
n=doc.getNextSibling()
if n is None or n.getNodeName() is not doc.getNodeName():
output('</variablelist>\n')
def descriptionTitle(self, doc, level, output): def bullet(self, doc, level, output):
output('<varlistentry><term>\n') p=doc.getPreviousSibling()
for c in doc.getChildNodes(): if p is None or p.getNodeName() is not doc.getNodeName():
getattr(self, self.element_types[c.getNodeName()])(c, level, output) output('<itemizedlist>\n')
output('</term>\n') output('<listitem><para>\n')
def descriptionBody(self, doc, level, output): for c in doc.getChildNodes():
output('<listitem><para>\n') getattr(self, self.element_types[c.getNodeName()])(c, level, output)
for c in doc.getChildNodes(): n=doc.getNextSibling()
getattr(self, self.element_types[c.getNodeName()])(c, level, output) output('</para></listitem>\n')
output('</para></listitem>\n') if n is None or n.getNodeName() is not doc.getNodeName():
output('</varlistentry>\n') output('</itemizedlist>\n')
def bullet(self, doc, level, output): def numbered(self, doc, level, output):
p=doc.getPreviousSibling() p=doc.getPreviousSibling()
if p is None or p.getNodeName() is not doc.getNodeName(): if p is None or p.getNodeName() is not doc.getNodeName():
output('<itemizedlist>\n') output('<orderedlist>\n')
output('<listitem><para>\n') output('<listitem><para>\n')
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
n=doc.getNextSibling()
output('</para></listitem>\n')
if n is None or n.getNodeName() is not doc.getNodeName():
output('</orderedlist>\n')
for c in doc.getChildNodes(): def example(self, doc, level, output):
getattr(self, self.element_types[c.getNodeName()])(c, level, output) i=0
n=doc.getNextSibling() for c in doc.getChildNodes():
output('</para></listitem>\n') if i==0:
if n is None or n.getNodeName() is not doc.getNodeName(): output('<programlisting>\n<![CDATA[\n')
output('</itemizedlist>\n') ##
## eek. A ']]>' in your body will break this...
def numbered(self, doc, level, output): ##
p=doc.getPreviousSibling() output(prestrip(c.getNodeValue()))
if p is None or p.getNodeName() is not doc.getNodeName(): output('\n]]></programlisting>\n')
output('<orderedlist>\n') else:
output('<listitem><para>\n')
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
n=doc.getNextSibling()
output('</para></listitem>\n')
if n is None or n.getNodeName() is not doc.getNodeName():
output('</orderedlist>\n')
def example(self, doc, level, output):
i=0
for c in doc.getChildNodes():
if i==0:
output('<programlisting>\n<![CDATA[\n')
##
## eek. A ']]>' in your body will break this...
##
output(prestrip(c.getNodeValue()))
output('\n]]></programlisting>\n')
else:
getattr(self, self.element_types[c.getNodeName()])(
c, level, output)
def paragraph(self, doc, level, output):
output('<para>\n\n')
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])( getattr(self, self.element_types[c.getNodeName()])(
c, level, output) c, level, output)
output('</para>\n\n')
def link(self, doc, level, output): def paragraph(self, doc, level, output):
# output('<link linkend="%s">' % doc.href) output('<para>\n\n')
for c in doc.getChildNodes(): for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output) getattr(self, self.element_types[c.getNodeName()])(
# output('</link>') c, level, output)
output('</para>\n\n')
def emphasis(self, doc, level, output): def link(self, doc, level, output):
output('<emphasis>') output('<ulink url="%s">' % doc.href)
for c in doc.getChildNodes(): for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output) getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</emphasis> ') output('</ulink>')
def literal(self, doc, level, output): def emphasis(self, doc, level, output):
output('<literal>') output('<emphasis>')
for c in doc.getChildNodes(): for c in doc.getChildNodes():
output(c.getNodeValue()) getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</literal>') output('</emphasis> ')
def strong(self, doc, level, output): def literal(self, doc, level, output):
output('<emphasis>') output('<literal>')
for c in doc.getChildNodes(): for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output) output(c.getNodeValue())
output('</emphasis>') output('</literal>')
def strong(self, doc, level, output):
output('<emphasis>')
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</emphasis>')
def xref(self, doc, level, output):
output('<xref linkend="%s"/>' % doc.getNodeValue())
def sgml(self, doc, level, output):
output(doc.getNodeValue())
def xref(self, doc, level, output):
output('<xref linkend="%s">' % doc.getNodeValue())
def prestrip(v): def prestrip(v):
v=string.replace(v, '\r\n', '\n') v=string.replace(v, '\r\n', '\n')
v=string.replace(v, '\r', '\n') v=string.replace(v, '\r', '\n')
v=string.replace(v, '\t', ' ') v=string.replace(v, '\t', ' ')
lines=string.split(v, '\n') lines=string.split(v, '\n')
indent=len(lines[0]) indent=len(lines[0])
for line in lines: for line in lines:
if not len(line): continue if not len(line): continue
i=len(line)-len(string.lstrip(line)) i=len(line)-len(string.lstrip(line))
if i < indent: if i < indent:
indent=i indent=i
nlines=[] nlines=[]
for line in lines: for line in lines:
nlines.append(line[indent:]) nlines.append(line[indent:])
return string.join(nlines, '\r\n') return string.join(nlines, '\n')
class DocBookChapter(DocBookClass): class DocBookChapter(DocBookClass):
def document(self, doc, level, output): def document(self, doc, level, output):
output('<chapter>\n') output('<chapter>\n')
children=doc.getChildNodes() children=doc.getChildNodes()
if (children and if (children and
children[0].getNodeName() == 'StructuredTextSection'): children[0].getNodeName() == 'StructuredTextSection'):
output('<title>%s</title>' % children[0].getChildNodes()[0].getNodeValue()) output('<title>%s</title>' % children[0].getChildNodes()[0].getNodeValue())
for c in children[0].getChildNodes()[1:]: for c in children[0].getChildNodes()[1:]:
getattr(self, self.element_types[c.getNodeName()])(c, level, output) getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</chapter>\n') output('</chapter>\n')
ets = DocBookClass.element_types ets = DocBookClass.element_types
ets.update({'StructuredTextImage': 'image'}) ets.update({'StructuredTextImage': 'image'})
class DocBookChapterWithFigures(DocBookChapter): class DocBookChapterWithFigures(DocBookChapter):
element_types = ets element_types = ets
def image(self, doc, level, output): def image(self, doc, level, output):
if hasattr(doc, 'key'): if hasattr(doc, 'key'):
output('<figure id="%s"><title>%s</title>\n' % (doc.key, doc.getNodeValue()) ) output('<figure id="%s"><title>%s</title>\n' % (doc.key, doc.getNodeValue()) )
else: else:
output('<figure><title>%s</title>\n' % doc.getNodeValue()) output('<figure><title>%s</title>\n' % doc.getNodeValue())
## for c in doc.getChildNodes(): ## for c in doc.getChildNodes():
## getattr(self, self.element_types[c.getNodeName()])(c, level, output) ## getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('<graphic fileref="%s"></graphic>\n</figure>\n' % doc.href) output('<graphic fileref="%s"></graphic>\n</figure>\n' % doc.href)
class DocBookArticle(DocBookClass): class DocBookArticle(DocBookClass):
def document(self, doc, level, output): def document(self, doc, level, output):
output('<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V3.1//EN">\n') output('<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN">\n')
output('<article>\n') output('<article>\n')
children=doc.getChildNodes() children=doc.getChildNodes()
if (children and if (children and
children[0].getNodeName() == 'StructuredTextSection'): children[0].getNodeName() == 'StructuredTextSection'):
output('<artheader>\n<title>%s</title>\n</artheader>\n' % output('<articleinfo>\n<title>%s</title>\n</articleinfo>\n' %
children[0].getChildNodes()[0].getNodeValue()) children[0].getChildNodes()[0].getNodeValue())
for c in children: for c in children:
getattr(self, self.element_types[c.getNodeName()])(c, level, output) getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</article>\n') output('</article>\n')
class DocBookBook: class DocBookBook:
def __init__(self, title=''): def __init__(self, title=''):
self.title = title self.title = title
self.chapters = [] self.chapters = []
def addChapter(self, chapter): def addChapter(self, chapter):
self.chapters.append(chapter) self.chapters.append(chapter)
def read(self): def read(self):
out = '<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN">\n<book>\n' out = '<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN">\n<book>\n'
out = out + '<title>%s</title>\n' % self.title out = out + '<title>%s</title>\n' % self.title
for chapter in self.chapters: for chapter in self.chapters:
out = out + chapter + '\n</book>\n' out = out + chapter + '\n</book>\n'
return out return out
def __str__(self): def __str__(self):
return self.read() return self.read()

View File

@@ -85,19 +85,31 @@
import re, ST, STDOM import re, ST, STDOM
from string import split, join, replace, expandtabs, strip, find, rstrip from string import split, join, replace, expandtabs, strip, find, rstrip
from STletters import *
StringType=type('') StringType=type('')
ListType=type([]) ListType=type([])
def flatten(obj, append):
if obj.getNodeType()==STDOM.TEXT_NODE:
append(obj.getNodeValue())
else:
for child in obj.getChildNodes():
flatten(child, append)
class StructuredTextExample(ST.StructuredTextParagraph): class StructuredTextExample(ST.StructuredTextParagraph):
"""Represents a section of document with literal text, as for examples""" """Represents a section of document with literal text, as for examples"""
def __init__(self, subs, **kw): def __init__(self, subs, **kw):
t=[]; a=t.append t=[]
for s in subs: a(s.getNodeValue()) a=t.append
apply(ST.StructuredTextParagraph.__init__, for s in subs:
(self, join(t,'\n\n'), ()), flatten(s, a)
kw) apply(ST.StructuredTextParagraph.__init__,
(self, join(t,'\n\n'), ()),
kw)
def getColorizableTexts(self): return () def getColorizableTexts(self): return ()
def setColorizableTexts(self, src): pass # never color examples def setColorizableTexts(self, src): pass # never color examples
@@ -138,8 +150,14 @@ class StructuredTextSection(ST.StructuredTextParagraph):
(self, StructuredTextSectionTitle(src), subs), (self, StructuredTextSectionTitle(src), subs),
kw) kw)
def getColorizableTexts(self):
return self._src.getColorizableTexts()
def setColorizableTexts(self,src):
self._src.setColorizableTexts(src)
# a StructuredTextTable holds StructuredTextRows # a StructuredTextTable holds StructuredTextRows
class StructuredTextTable(ST.StructuredTextDocument): class StructuredTextTable(ST.StructuredTextParagraph):
""" """
rows is a list of lists containing tuples, which rows is a list of lists containing tuples, which
represent the columns/cells in each rows. represent the columns/cells in each rows.
@@ -148,7 +166,7 @@ class StructuredTextTable(ST.StructuredTextDocument):
""" """
def __init__(self, rows, src, subs, **kw): def __init__(self, rows, src, subs, **kw):
apply(ST.StructuredTextDocument.__init__,(self,subs),kw) apply(ST.StructuredTextParagraph.__init__,(self,subs),kw)
self._rows = [] self._rows = []
for row in rows: for row in rows:
if row: if row:
@@ -208,21 +226,27 @@ class StructuredTextTable(ST.StructuredTextDocument):
return self.setColorizableTexts() return self.setColorizableTexts()
# StructuredTextRow holds StructuredTextColumns # StructuredTextRow holds StructuredTextColumns
class StructuredTextRow(ST.StructuredTextDocument): class StructuredTextRow(ST.StructuredTextParagraph):
def __init__(self,row,kw): def __init__(self,row,kw):
""" """
row is a list of tuples, where each tuple is row is a list of tuples, where each tuple is
the raw text for a cell/column and the span the raw text for a cell/column and the span
of that cell/column". of that cell/column.
EX EX
[('this is column one',1), ('this is column two',1)] [('this is column one',1), ('this is column two',1)]
""" """
apply(ST.StructuredTextDocument.__init__,(self,[]),kw) apply(ST.StructuredTextParagraph.__init__,(self,[]),kw)
self._columns = [] self._columns = []
for column in row: for column in row:
self._columns.append(StructuredTextColumn(column[0],column[1],kw)) self._columns.append(StructuredTextColumn(column[0],
column[1],
column[2],
column[3],
column[4],
kw))
def getColumns(self): def getColumns(self):
return [self._columns] return [self._columns]
@@ -245,10 +269,12 @@ class StructuredTextColumn(ST.StructuredTextParagraph):
or StructuredTextTableData. or StructuredTextTableData.
""" """
def __init__(self,text,span,kw): def __init__(self,text,span,align,valign,typ,kw):
# print "StructuredTextColumn", text, span
apply(ST.StructuredTextParagraph.__init__,(self,text,[]),kw) apply(ST.StructuredTextParagraph.__init__,(self,text,[]),kw)
self._span = span self._span = span
self._align = align
self._valign = valign
self._type = typ
def getSpan(self): def getSpan(self):
return self._span return self._span
@@ -256,9 +282,27 @@ class StructuredTextColumn(ST.StructuredTextParagraph):
def _getSpan(self): def _getSpan(self):
return self._span return self._span
class StructuredTextTableHeader(ST.StructuredTextDocument): pass def getAlign(self):
return self._align
class StructuredTextTableData(ST.StructuredTextDocument): pass def _getAlign(self):
return self.getAlign()
def getValign(self):
return self._valign
def _getValign(self):
return self.getValign()
def getType(self):
return self._type
def _getType(self):
return self.getType()
class StructuredTextTableHeader(ST.StructuredTextParagraph): pass
class StructuredTextTableData(ST.StructuredTextParagraph): pass
class StructuredTextMarkup(STDOM.Element): class StructuredTextMarkup(STDOM.Element):
@@ -296,6 +340,8 @@ class StructuredTextSGML(StructuredTextMarkup): pass
class StructuredTextLink(StructuredTextMarkup): pass class StructuredTextLink(StructuredTextMarkup): pass
class StructuredTextXref(StructuredTextMarkup): pass
class DocumentClass: class DocumentClass:
""" """
Class instance calls [ex.=> x()] require a structured text Class instance calls [ex.=> x()] require a structured text
@@ -309,7 +355,6 @@ class DocumentClass:
instance with a strong instance stored in its string instance with a strong instance stored in its string
""" """
#'doc_table',
paragraph_types = [ paragraph_types = [
'doc_bullet', 'doc_bullet',
'doc_numbered', 'doc_numbered',
@@ -322,11 +367,13 @@ class DocumentClass:
#'doc_named_link', #'doc_named_link',
#'doc_underline', #'doc_underline',
text_types = [ text_types = [
'doc_sgml',
'doc_href', 'doc_href',
'doc_strong', 'doc_strong',
'doc_emphasize', 'doc_emphasize',
'doc_literal', 'doc_literal',
'doc_sgml' 'doc_sgml',
'doc_xref',
] ]
def __call__(self, doc): def __call__(self, doc):
@@ -425,7 +472,6 @@ class DocumentClass:
st=type('')): st=type('')):
result=[] result=[]
for paragraph in raw_paragraphs: for paragraph in raw_paragraphs:
#print type(paragraph)
if paragraph.getNodeName() != 'StructuredTextParagraph': if paragraph.getNodeName() != 'StructuredTextParagraph':
result.append(paragraph) result.append(paragraph)
continue continue
@@ -445,21 +491,20 @@ class DocumentClass:
break break
else: else:
new_paragraphs=ST.StructuredTextParagraph(paragraph.getColorizableTexts()[0], new_paragraphs=ST.StructuredTextParagraph(paragraph.getColorizableTexts()[0],
self.color_paragraphs(paragraph.getSubparagraphs()), self.color_paragraphs(paragraph.getSubparagraphs()),
indent=paragraph.indent), indent=paragraph.indent),
# color the inline StructuredText types # color the inline StructuredText types
# for each StructuredTextParagraph # for each StructuredTextParagraph
for paragraph in new_paragraphs: for paragraph in new_paragraphs:
if paragraph.getNodeName() is "StructuredTextTable": if paragraph.getNodeName() is "StructuredTextTable":
#print "we have a table"
cells = paragraph.getColumns() cells = paragraph.getColumns()
text = paragraph.getColorizableTexts() text = paragraph.getColorizableTexts()
text = map(ST.StructuredText,text) text = map(ST.StructuredText,text)
text = map(self.__call__,text) text = map(self.__call__,text)
#for index in range(len(text)): for t in range(len(text)):
# text[index].setColorizableTexts(map(self.color_text,text[index].getColorizableTexts())) text[t] = text[t].getSubparagraphs()
paragraph.setColorizableTexts(text) paragraph.setColorizableTexts(text)
paragraph.setColorizableTexts( paragraph.setColorizableTexts(
@@ -470,7 +515,7 @@ class DocumentClass:
return result return result
def doc_table(self, paragraph, expr = re.compile('\s*\|[-]+\|').match): def doc_table(self, paragraph, expr = re.compile(r'\s*\|[-]+\|').match):
text = paragraph.getColorizableTexts()[0] text = paragraph.getColorizableTexts()[0]
m = expr(text) m = expr(text)
@@ -480,42 +525,86 @@ class DocumentClass:
return None return None
rows = [] rows = []
rows = split(text,'\n')
spans = [] spans = []
ROWS = [] ROWS = []
COLS = [] COLS = []
indexes = []
ignore = []
TDdivider = re.compile("[\-]+").match TDdivider = re.compile("[\-]+").match
THdivider = re.compile("[\=]+").match THdivider = re.compile("[\=]+").match
col = re.compile('\|').search
innertable = re.compile('\|([-]+|[=]+)\|').search
# find where the column markers are located
col = re.compile('\|').search
text = strip(text) text = strip(text)
rows = split(text,'\n') rows = split(text,'\n')
foo = ""
for row in range(len(rows)): for row in range(len(rows)):
rows[row] = strip(rows[row]) rows[row] = strip(rows[row])
for row in rows: # have indexes store if a row is a divider
tmp = strip(row) # or a cell part
tmp = row[1:len(tmp)-1] # remove leading and trailing | for index in range(len(rows)):
offset = 0 tmpstr = rows[index][1:len(rows[index])-1]
if TDdivider(tmpstr):
indexes.append("TDdivider")
elif THdivider(tmpstr):
indexes.append("THdivider")
else:
indexes.append("cell")
for index in range(len(indexes)):
if indexes[index] is "TDdivider" or indexes[index] is THdivider:
ignore = [] # reset ignore
#continue # skip dividers
tmp = strip(rows[index]) # clean the row up
tmp = tmp[1:len(tmp)-1] # remove leading + trailing |
offset = 0
# find the start and end of inner
# tables. ignore everything between
if innertable(tmp):
tmpstr = strip(tmp)
while innertable(tmpstr):
start,end = innertable(tmpstr).span()
if not (start,end-1) in ignore:
ignore.append(start,end-1)
tmpstr = " " + tmpstr[end:]
# find the location of column dividers
# NOTE: |'s in inner tables do not count
# as column dividers
if col(tmp): if col(tmp):
while col(tmp): while col(tmp):
start,end = col(tmp).span() bar = 1 # true if start is not in ignore
start,end = col(tmp).span()
if not start+offset in spans: if not start+offset in spans:
spans.append(start + offset) for s,e in ignore:
COLS.append((tmp[0:start],start+offset)) if start+offset >= s or start+offset <= e:
tmp = " " + tmp[end:] bar = None
offset = offset + (start) break
if bar: # start is clean
spans.append(start+offset)
if not bar:
foo = foo + tmp[:end]
tmp = tmp[end:]
offset = offset + end
else:
COLS.append((foo + tmp[0:start],start+offset))
foo = ""
tmp = " " + tmp[end:]
offset = offset + start
if not offset+len(tmp) in spans: if not offset+len(tmp) in spans:
spans.append(offset+len(tmp)) spans.append(offset+len(tmp))
COLS.append((tmp,offset+len(tmp))) COLS.append((foo + tmp,offset+len(tmp)))
foo = ""
ROWS.append(COLS) ROWS.append(COLS)
COLS = [] COLS = []
spans.sort() spans.sort()
ROWS = ROWS[1:len(ROWS)] ROWS = ROWS[1:len(ROWS)]
# find each column span # find each column span
@@ -528,9 +617,9 @@ class DocumentClass:
cols.append(tmp) cols.append(tmp)
tmp = [] tmp = []
cur = 1 # the current column span cur = 1
tmp = [] tmp = []
C = [] # holds the span of each cell C = []
for col in cols: for col in cols:
for span in spans: for span in spans:
if not span in col: if not span in col:
@@ -541,14 +630,47 @@ class DocumentClass:
C.append(tmp) C.append(tmp)
tmp = [] tmp = []
# make rows contain the cell's text and the span
# of that cell
for index in range(len(C)): for index in range(len(C)):
for i in range(len(C[index])): for i in range(len(C[index])):
ROWS[index][i] = (ROWS[index][i][0],C[index][i]) ROWS[index][i] = (ROWS[index][i][0],C[index][i])
rows = ROWS rows = ROWS
# now munge the table cells together # label things as either TableData or
# Table header
TD = []
TH = []
all = []
for index in range(len(indexes)):
if indexes[index] is "TDdivider":
TD.append(index)
all.append(index)
if indexes[index] is "THdivider":
TH.append(index)
all.append(index)
TD = TD[1:]
dividers = all[1:]
#print "TD => ", TD
#print "TH => ", TH
#print "all => ", all, "\n"
for div in dividers:
if div in TD:
index = all.index(div)
for rowindex in range(all[index-1],all[index]):
for i in range(len(rows[rowindex])):
rows[rowindex][i] = (rows[rowindex][i][0],
rows[rowindex][i][1],
"td")
else:
index = all.index(div)
for rowindex in range(all[index-1],all[index]):
for i in range(len(rows[rowindex])):
rows[rowindex][i] = (rows[rowindex][i][0],
rows[rowindex][i][1],
"th")
# now munge the multi-line cells together
# as paragraphs
ROWS = [] ROWS = []
COLS = [] COLS = []
for row in rows: for row in rows:
@@ -556,16 +678,97 @@ class DocumentClass:
if not COLS: if not COLS:
COLS = range(len(row)) COLS = range(len(row))
for i in range(len(COLS)): for i in range(len(COLS)):
COLS[i] = ["",1] COLS[i] = ["",1,""]
if TDdivider(row[index][0]) or THdivider(row[index][0]): if TDdivider(row[index][0]) or THdivider(row[index][0]):
ROWS.append(COLS) ROWS.append(COLS)
COLS = [] COLS = []
else: else:
COLS[index][0] = COLS[index][0] + rstrip(row[index][0]) + "\n" COLS[index][0] = COLS[index][0] + (row[index][0]) + "\n"
COLS[index][1] = row[index][1] COLS[index][1] = row[index][1]
return StructuredTextTable(ROWS,text,subs,indent=paragraph.indent) COLS[index][2] = row[index][2]
def doc_bullet(self, paragraph, expr = re.compile('\s*[-*o]\s+').match): # now that each cell has been munged together,
# determine the cell's alignment.
# Default is to center. Also determine the cell's
# vertical alignment, top, middle, bottom. Default is
# to middle
rows = []
cols = []
for row in ROWS:
for index in range(len(row)):
topindent = 0
bottomindent = 0
leftindent = 0
rightindent = 0
left = []
right = []
text = row[index][0]
text = split(text,'\n')
text = text[:len(text)-1]
align = ""
valign = ""
for t in text:
t = strip(t)
if not t:
topindent = topindent + 1
else:
break
text.reverse()
for t in text:
t = strip(t)
if not t:
bottomindent = bottomindent + 1
else:
break
text.reverse()
tmp = join(text[topindent:len(text)-bottomindent],"\n")
pars = re.compile("\n\s*\n").split(tmp)
for par in pars:
if index > 0:
par = par[1:]
par = split(par, ' ')
for p in par:
if not p:
leftindent = leftindent+1
else:
break
left.append(leftindent)
leftindent = 0
par.reverse()
for p in par:
if not p:
rightindent = rightindent + 1
else:
break
right.append(rightindent)
rightindent = 0
left.sort()
right.sort()
if topindent == bottomindent:
valign="middle"
elif topindent < 1:
valign="top"
elif bottomindent < 1:
valign="bottom"
else:
valign="middle"
if left[0] < 1:
align = "left"
elif right[0] < 1:
align = "right"
elif left[0] > 1 and right[0] > 1:
align="center"
else:
align="left"
cols.append(row[index][0],row[index][1],align,valign,row[index][2])
rows.append(cols)
cols = []
return StructuredTextTable(rows,text,subs,indent=paragraph.indent)
def doc_bullet(self, paragraph, expr = re.compile(r'\s*[-*o]\s+').match):
top=paragraph.getColorizableTexts()[0] top=paragraph.getColorizableTexts()[0]
m=expr(top) m=expr(top)
@@ -583,7 +786,7 @@ class DocumentClass:
def doc_numbered( def doc_numbered(
self, paragraph, self, paragraph,
expr = re.compile('(\s*[a-zA-Z]+\.)|(\s*[0-9]+\.)|(\s*[0-9]+\s+)').match): expr = re.compile(r'(\s*[%s]+\.)|(\s*[0-9]+\.)|(\s*[0-9]+\s+)' % letters).match):
# This is the old expression. It had a nasty habit # This is the old expression. It had a nasty habit
# of grabbing paragraphs that began with a single # of grabbing paragraphs that began with a single
@@ -607,8 +810,8 @@ class DocumentClass:
def doc_description( def doc_description(
self, paragraph, self, paragraph,
delim = re.compile('\s+--\s+').search, delim = re.compile(r'\s+--\s+').search,
nb=re.compile(r'[^\0- ]').search, nb=re.compile(r'[^\000- ]').search,
): ):
top=paragraph.getColorizableTexts()[0] top=paragraph.getColorizableTexts()[0]
@@ -632,7 +835,7 @@ class DocumentClass:
delim=d) delim=d)
def doc_header(self, paragraph, def doc_header(self, paragraph,
expr = re.compile('[ a-zA-Z0-9.:/,-_*<>\?\'\"]+').match expr = re.compile(r'[ %s0-9.:/,-_*<>\?\'\"]+' % letters).match
): ):
subs=paragraph.getSubparagraphs() subs=paragraph.getSubparagraphs()
if not subs: return None if not subs: return None
@@ -650,9 +853,9 @@ class DocumentClass:
def doc_literal( def doc_literal(
self, s, self, s,
expr=re.compile( expr=re.compile(
"(?:\s|^)'" # open r"(?:\s|^)'" # open
"([^ \t\n\r\f\v']|[^ \t\n\r\f\v'][^\n']*[^ \t\n\r\f\v'])" # contents r"([^ \t\n\r\f\v']|[^ \t\n\r\f\v'][^\n']*[^ \t\n\r\f\v'])" # contents
"'(?:\s|[,.;:!?]|$)" # close r"'(?:\s|[,.;:!?]|$)" # close
).search): ).search):
r=expr(s) r=expr(s)
@@ -664,7 +867,7 @@ class DocumentClass:
def doc_emphasize( def doc_emphasize(
self, s, self, s,
expr = re.compile('\s*\*([ \na-zA-Z0-9.:/;,\'\"\?]+)\*(?!\*|-)').search expr = re.compile(r'\s*\*([ \n%s0-9]+)\*(?!\*|-)' % lettpunc).search
): ):
r=expr(s) r=expr(s)
@@ -676,8 +879,8 @@ class DocumentClass:
def doc_inner_link(self, def doc_inner_link(self,
s, s,
expr1 = re.compile("\.\.\s*").search, expr1 = re.compile(r"\.\.\s*").search,
expr2 = re.compile("\[[a-zA-Z0-9]+\]").search): expr2 = re.compile(r"\[[%s0-9]+\]" % letters ).search):
# make sure we dont grab a named link # make sure we dont grab a named link
if expr2(s) and expr1(s): if expr2(s) and expr1(s):
@@ -697,7 +900,7 @@ class DocumentClass:
def doc_named_link(self, def doc_named_link(self,
s, s,
expr=re.compile("(\.\.\s)(\[[a-zA-Z0-9]+\])").search): expr=re.compile(r"(\.\.\s)(\[[%s0-9]+\])" % letters).search):
result = expr(s) result = expr(s)
if result: if result:
@@ -711,7 +914,7 @@ class DocumentClass:
def doc_underline(self, def doc_underline(self,
s, s,
expr=re.compile("\_([a-zA-Z0-9\s\.,\?]+)\_").search): expr=re.compile(r"\s+\_([%s0-9\s]+)\_" % lettpunc).search):
result = expr(s) result = expr(s)
if result: if result:
@@ -723,7 +926,7 @@ class DocumentClass:
def doc_strong(self, def doc_strong(self,
s, s,
expr = re.compile('\s*\*\*([ \na-zA-Z0-9.:/;\-,!\?\'\"]+)\*\*').search expr = re.compile(r'\s*\*\*([ \n%s0-9]+)\*\*' % lettpunc).search
): ):
r=expr(s) r=expr(s)
@@ -733,13 +936,16 @@ class DocumentClass:
else: else:
return None return None
def doc_href( ## Some constants to make the doc_href() regex easier to read.
_DQUOTEDTEXT = r'("[%s0-9\n\-\.\,\;\(\)\/\:\/\*\']+")' % letters ## double quoted text
_URL_AND_PUNC = r'([%s0-9\@\.\,\?\!\/\:\;\-\#\~]+)' % letters
_SPACES = r'(\s*)'
self, s, def doc_href(self, s,
expr1 = re.compile("(\"[ a-zA-Z0-9\n\-\.\,\;\(\)\/\:\/]+\")(:)([a-zA-Z0-9\:\/\.\~\-]+)([,]*\s*)").search, expr1 = re.compile(_DQUOTEDTEXT + "(:)" + _URL_AND_PUNC + _SPACES).search,
expr2 = re.compile('(\"[ a-zA-Z0-9\n\-\.\:\;\(\)\/]+\")([,]+\s+)([a-zA-Z0-9\@\.\,\?\!\/\:\;\-\#]+)(\s*)').search): expr2 = re.compile(_DQUOTEDTEXT + r'(\,\s+)' + _URL_AND_PUNC + _SPACES).search):
punctuation = re.compile("[\,\.\?\!\;]+").match punctuation = re.compile(r"[\,\.\?\!\;]+").match
r=expr1(s) or expr2(s) r=expr1(s) or expr2(s)
if r: if r:
@@ -766,7 +972,7 @@ class DocumentClass:
else: else:
return None return None
def doc_sgml(self,s,expr=re.compile("\<[a-zA-Z0-9\.\=\'\"\:\/\-\#\+\s]+\>").search): def doc_sgml(self,s,expr=re.compile(r"\<[%s0-9\.\=\'\"\:\/\-\#\+\s\*]+\>" % letters).search):
""" """
SGML text is ignored and outputed as-is SGML text is ignored and outputed as-is
""" """
@@ -775,3 +981,18 @@ class DocumentClass:
start,end = r.span() start,end = r.span()
text = s[start:end] text = s[start:end]
return (StructuredTextSGML(text),start,end) return (StructuredTextSGML(text),start,end)
def doc_xref(self, s,
expr = re.compile('\[([%s0-9\-.:/;,\n\~]+)\]' % letters).search
):
r = expr(s)
if r:
start, end = r.span(1)
return (StructuredTextXref(s[start:end]), start-1, end+1)
else:
return None

View File

@@ -84,6 +84,7 @@
############################################################################## ##############################################################################
from string import join, split, find from string import join, split, find
from cgi import escape
import re, sys, ST import re, sys, ST
class HTMLClass: class HTMLClass:
@@ -172,34 +173,34 @@ class HTMLClass:
def bullet(self, doc, level, output): def bullet(self, doc, level, output):
p=doc.getPreviousSibling() p=doc.getPreviousSibling()
if p is None or p.getNodeName() is not doc.getNodeName(): if p is None or p.getNodeName() is not doc.getNodeName():
output('<ul>\n') output('\n<ul>\n')
output('<li>') output('<li>')
for c in doc.getChildNodes(): for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output) getattr(self, self.element_types[c.getNodeName()])(c, level, output)
n=doc.getNextSibling() n=doc.getNextSibling()
output('</li>\n') output('</li>\n')
if n is None or n.getNodeName() is not doc.getNodeName(): if n is None or n.getNodeName() is not doc.getNodeName():
output('</ul>\n') output('\n</ul>\n')
def numbered(self, doc, level, output): def numbered(self, doc, level, output):
p=doc.getPreviousSibling() p=doc.getPreviousSibling()
if p is None or p.getNodeName() is not doc.getNodeName(): if p is None or p.getNodeName() is not doc.getNodeName():
output('<ol>\n') output('\n<ol>\n')
output('<li>') output('<li>')
for c in doc.getChildNodes(): for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output) getattr(self, self.element_types[c.getNodeName()])(c, level, output)
n=doc.getNextSibling() n=doc.getNextSibling()
output('</li>\n') output('</li>\n')
if n is None or n.getNodeName() is not doc.getNodeName(): if n is None or n.getNodeName() is not doc.getNodeName():
output('</ol>\n') output('\n</ol>\n')
def example(self, doc, level, output): def example(self, doc, level, output):
i=0 i=0
for c in doc.getChildNodes(): for c in doc.getChildNodes():
if i==0: if i==0:
output('<pre>') output('\n<pre>\n')
output(html_quote(c.getNodeValue())) output(escape(c.getNodeValue()))
output('</pre>\n') output('\n</pre>\n')
else: else:
getattr(self, self.element_types[c.getNodeName()])( getattr(self, self.element_types[c.getNodeName()])(
c, level, output) c, level, output)
@@ -214,7 +215,7 @@ class HTMLClass:
else: else:
getattr(self, self.element_types[c.getNodeName()])( getattr(self, self.element_types[c.getNodeName()])(
c, level, output) c, level, output)
output('</p>') output('</p>\n')
def link(self, doc, level, output): def link(self, doc, level, output):
output('<a href="%s">' % doc.href) output('<a href="%s">' % doc.href)
@@ -231,7 +232,7 @@ class HTMLClass:
def literal(self, doc, level, output): def literal(self, doc, level, output):
output('<code>') output('<code>')
for c in doc.getChildNodes(): for c in doc.getChildNodes():
output(html_quote(c.getNodeValue())) output(escape(c.getNodeValue()))
output('</code>') output('</code>')
def strong(self, doc, level, output): def strong(self, doc, level, output):
@@ -268,6 +269,10 @@ class HTMLClass:
for c in doc.getChildNodes(): for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output) getattr(self, self.element_types[c.getNodeName()])(c, level, output)
def xref(self, doc, level, output):
val = doc.getNodeValue()
output('<a href="#%s">[%s]</a>' % (val, val) )
def table(self,doc,level,output): def table(self,doc,level,output):
""" """
A StructuredTextTable holds StructuredTextRow(s) which A StructuredTextTable holds StructuredTextRow(s) which
@@ -279,29 +284,23 @@ class HTMLClass:
for row in doc.getRows()[0]: for row in doc.getRows()[0]:
output("<tr>\n") output("<tr>\n")
for column in row.getColumns()[0]: for column in row.getColumns()[0]:
str = "<td colspan=%s>" % column.getSpan() if hasattr(column,"getAlign"):
str = "<%s colspan=%s align=%s valign=%s>" % (column.getType(),
column.getSpan(),
column.getAlign(),
column.getValign())
else:
str = "<td colspan=%s>" % column.getSpan()
output(str) output(str)
#for c in doc.getChildNodes():
# getattr(self, self.element_types[c.getNodeName()])(c, level, output)
for c in column.getChildNodes(): for c in column.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output) getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output("</td>\n") if hasattr(column,"getType"):
output("</"+column.getType()+">\n")
else:
output("</td>\n")
output("</tr>\n") output("</tr>\n")
output("</table>\n") output("</table>\n")
def html_quote(v, name='(Unknown name)', md={},
character_entities=(
(('&'), '&amp;'),
(('<'), '&lt;' ),
(('>'), '&gt;' ),
(('\213'), '&lt;' ),
(('\233'), '&gt;' ),
(('"'), '&quot;'))): #"
text=str(v)
for re,name in character_entities:
if find(text, re) >= 0: text=join(split(text,re),name)
return text

View File

@@ -109,21 +109,16 @@ class HTMLWithImages(HTMLClass):
output('</body>\n') output('</body>\n')
output('</html>\n') output('</html>\n')
def image(self, doc, level, output):
output('<img src="%s" alt="%s">' % (doc.href, doc.getNodeValue()))
def image(self, doc, level, output): def image(self, doc, level, output):
if hasattr(doc, 'key'): if hasattr(doc, 'key'):
output('<a name="%s"></a>\n<img src="%s" alt="%s">' % (doc.key, doc.href, doc.getNodeValue())) output('<a name="%s"></a>\n' % doc.key)
else: output('<img src="%s" alt="%s">\n' % (doc.href, doc.getNodeValue()))
output('<img src="%s" alt="%s">' % (doc.href, doc.getNodeValue())) if doc.getNodeValue() and hasattr(doc, 'key'):
output('<p><b>Figure %s</b> %s</p>\n' % (doc.key, doc.getNodeValue()))
def xref(self, doc, level, output): def xref(self, doc, level, output):
val = doc.getNodeValue() val = doc.getNodeValue()
output('<a href="#%s">%s</a>' % (val, val) ) output('<a href="#%s">Figure %s</a>' % (val, val) )

View File

@@ -1,170 +0,0 @@
##############################################################################
#
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# This license has been certified as Open Source(tm).
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions in source code must retain the above copyright
# notice, this list of conditions, and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# 3. Digital Creations requests that attribution be given to Zope
# in any manner possible. Zope includes a "Powered by Zope"
# button that is installed by default. While it is not a license
# violation to remove this button, it is requested that the
# attribution remain. A significant investment has been put
# into Zope, and this effort will continue if the Zope community
# continues to grow. This is one way to assure that growth.
#
# 4. All advertising materials and documentation mentioning
# features derived from or use of this software must display
# the following acknowledgement:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# In the event that the product being advertised includes an
# intact Zope distribution (with copyright and license included)
# then this clause is waived.
#
# 5. Names associated with Zope or Digital Creations must not be used to
# endorse or promote products derived from this software without
# prior written permission from Digital Creations.
#
# 6. Modified redistributions of any form whatsoever must retain
# the following acknowledgment:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# Intact (re-)distributions of any official Zope release do not
# require an external acknowledgement.
#
# 7. Modifications are encouraged but must be packaged separately as
# patches to official Zope releases. Distributions that do not
# clearly separate the patches from the original work must be clearly
# labeled as unofficial distributions. Modifications which do not
# carry the name Zope may be packaged in any form, as long as they
# conform to all of the clauses above.
#
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations. Specific
# attributions are listed in the accompanying credits file.
#
##############################################################################
'''
$Id$'''
from StructuredText import * # :-)
def ctag(s):
# Blech, wish we could use character tags
if s is None: s=''
s=gsub(strong,'\\1<bold>\\2<plain>\\3',s)
s=gsub(code, '\\1<family Courier>\\2<family Times>\\3',s)
s=gsub(em, '\\1<italic>\\2<plain>\\3',s)
return join(map(strip,split(s,'\n')),'\n')
class MML(StructuredText):
'''\
An MML structured text formatter.
'''\
def __str__(self,
):
'''\
Return an HTML string representation of the structured text data.
'''
s=self._str(self.structure,self.level)
return s
def ul(self, before, p, after):
return ("%s\n\n<Bulleted>\n%s%s"
% (before, ctag(p), after))
def ol(self, before, p, after):
return ("%s\n\n<Numbered>\n%s%s"
% (before, ctag(p), after))
def dl(self, before, t, d, after):
return ("%s\n\n<Term>\n%s\n\n<Definition>\n%s%s"
% (before,ctag(t),ctag(d),after))
def head(self, before, t, level, d):
return ("%s\n\n<Heading%d>\n%s%s"
% (before,level,ctag(t),d))
def normal(self,before,p,after):
return "%s\n\n<Body>\n%s%s" % (before, ctag(p), after)
def pre(self,structure,r=None):
if r is None: r=['']
for s in structure:
for line in split(s[0],'\n'):
r.append('\n<PRE>')
r.append(line)
self.pre(s[1],r)
return join(r,'\n')
def _str(self,structure,level):
r=''
for s in structure:
# print s[0],'\n', len(s[1]), '\n\n'
if bullet.match(s[0]) >= 0:
p=bullet.group(1)
r=self.ul(r,p,self._str(s[1],level))
elif ol.match(s[0]) >= 0:
p=ol.group(3)
r=self.ol(r,p,self._str(s[1],level))
elif olp.match(s[0]) >= 0:
p=olp.group(1)
r=self.ol(r,p,self._str(s[1],level))
elif dl.match(s[0]) >= 0:
t,d=dl.group(1,2)
r=self.dl(r,t,d,self._str(s[1],level))
elif example.search(s[0]) >= 0 and s[1]:
# Introduce an example, using pre tags:
r=self.normal(r,s[0],self.pre(s[1]))
elif s[0][-2:]=='::' and s[1]:
# Introduce an example, using pre tags:
r=self.normal(r,s[0][:-1],self.pre(s[1]))
elif nl.search(s[0]) < 0 and s[1] and s[0][-1:] != ':':
# Treat as a heading
t=s[0]
r=self.head(r,t,level,
self._str(s[1],level and level+1))
else:
r=self.normal(r,s[0],self._str(s[1],level))
return r

View File

@@ -26,7 +26,9 @@ def insert(struct, top, level):
""" """
#print "struct", struct, top-1 #print "struct", struct, top-1
if not top-1 in range(len(struct)): if not top-1 in range(len(struct)):
return None if struct:
return struct[len(struct)-1].getSubparagraphs()
return struct
run = struct[top-1] run = struct[top-1]
i = 0 i = 0
while i+1 < level: while i+1 < level:
@@ -142,8 +144,11 @@ def StructuredText(paragraphs, paragraph_delimiter=re.compile('\n\s*\n')):
if result > 0: if result > 0:
currentlevel = result currentlevel = result
currentindent = indent currentindent = indent
run = insert(struct,level,currentlevel) if not level:
run.append(StructuredTextParagraph(paragraph, indent=indent, level=currentlevel)) struct.append(StructuredTextParagraph(paragraph, indent=indent, level=currentlevel))
else:
run = insert(struct,level,currentlevel)
run.append(StructuredTextParagraph(paragraph, indent=indent, level=currentlevel))
else: else:
if insert(struct,level,currentlevel): if insert(struct,level,currentlevel):
run = insert(struct,level,currentlevel) run = insert(struct,level,currentlevel)

View File

@@ -178,7 +178,7 @@ class ParentNode:
if not children: if not children:
return None return None
n=chidren[0] n=children[0]
if type(n) is st: if type(n) is st:
n=TextNode(n) n=TextNode(n)
@@ -554,7 +554,7 @@ class Element(Node):
return self.getNodeType() return self.getNodeType()
def _get_NodeValue(self, type=type, st=type('')): def _get_NodeValue(self, type=type, st=type('')):
return self.GetNodeValue(type,st) return self.getNodeValue(type,st)
def _get_ParentNode(self): def _get_ParentNode(self):
return self.getParentNode() return self.getParentNode()

View File

@@ -18,7 +18,7 @@ Using Structured Text
st=StructuredText.Basic(raw) st=StructuredText.Basic(raw)
The output of 'StructuredText.Basic' is simply a The output of 'StructuredText.Basic' is simply a
StructuredTextDocumemt object containing StructuredTextParagraph StructuredTextDocument object containing StructuredTextParagraph
objects arranged in a hierarchy. Paragraphs are delimited by strings objects arranged in a hierarchy. Paragraphs are delimited by strings
of two or more whitespace characters beginning and ending with of two or more whitespace characters beginning and ending with
newline characters. Hierarchy is indicated by indentation. The newline characters. Hierarchy is indicated by indentation. The

View File

@@ -0,0 +1,15 @@
import string
try:
del string
import locale
locale.setlocale(locale.LC_ALL,"")
except:
pass
import string
letters = string.letters
punctuations = string.punctuation
lettpunc = letters + punctuations

View File

@@ -1,4 +1,3 @@
#! /usr/bin/env python -- # -*- python -*-
############################################################################## ##############################################################################
# #
# Zope Public License (ZPL) Version 1.0 # Zope Public License (ZPL) Version 1.0
@@ -83,751 +82,67 @@
# attributions are listed in the accompanying credits file. # attributions are listed in the accompanying credits file.
# #
############################################################################## ##############################################################################
'''Structured Text Manipulation
Parse a structured text string into a form that can be used with """ Alias module for StructuredTextClassic compatibility which makes
structured formats, like html. use of StructuredTextNG """
Structured text is text that uses indentation and simple
symbology to indicate the structure of a document.
A structured string consists of a sequence of paragraphs separated by import HTMLClass, DocumentClass, ClassicDocumentClass
one or more blank lines. Each paragraph has a level which is defined from ST import Basic
as the minimum indentation of the paragraph. A paragraph is a
sub-paragraph of another paragraph if the other paragraph is the last
preceding paragraph that has a lower level.
Special symbology is used to indicate special constructs: import re, string,sys
from STletters import letters
- A single-line paragraph whose immediately succeeding paragraphs are lower Document = ClassicDocumentClass.DocumentClass()
level is treated as a header. HTMLNG = HTMLClass.HTMLClass()
- A paragraph that begins with a '-', '*', or 'o' is treated as an def HTML(aStructuredString, level=0):
unordered list (bullet) element. st = Basic(aStructuredString)
doc = Document(st)
return HTMLNG(doc)
- A paragraph that begins with a sequence of digits followed by a def StructuredText(aStructuredString, level=0):
white-space character is treated as an ordered list element. return HTML(aStructuredString,level)
- A paragraph that begins with a sequence of sequences, where each
sequence is a sequence of digits or a sequence of letters followed
by a period, is treated as an ordered list element.
- A paragraph with a first line that contains some text, followed by
some white-space and '--' is treated as
a descriptive list element. The leading text is treated as the
element title.
- Sub-paragraphs of a paragraph that ends in the word 'example' or the
word 'examples', or '::' is treated as example code and is output as is.
- Text enclosed single quotes (with white-space to the left of the
first quote and whitespace or puctuation to the right of the second quote)
is treated as example code.
- Text surrounded by '*' characters (with white-space to the left of the
first '*' and whitespace or puctuation to the right of the second '*')
is emphasized.
- Text surrounded by '**' characters (with white-space to the left of the
first '**' and whitespace or puctuation to the right of the second '**')
is made strong.
- Text surrounded by '_' underscore characters (with whitespace to the left
and whitespace or punctuation to the right) is made underlined.
- Text encloded by double quotes followed by a colon, a URL, and concluded
by punctuation plus white space, *or* just white space, is treated as a
hyper link. For example:
"Zope":http://www.zope.org/ is ...
Is interpreted as '<a href="http://www.zope.org/">Zope</a> is ....'
Note: This works for relative as well as absolute URLs.
- Text enclosed by double quotes followed by a comma, one or more spaces,
an absolute URL and concluded by punctuation plus white space, or just
white space, is treated as a hyper link. For example:
"mail me", mailto:amos@digicool.com.
Is interpreted as '<a href="mailto:amos@digicool.com">mail me</a>.'
- Text enclosed in brackets which consists only of letters, digits,
underscores and dashes is treated as hyper links within the document.
For example:
As demonstrated by Smith [12] this technique is quite effective.
Is interpreted as '... by Smith <a href="#12">[12]</a> this ...'. Together
with the next rule this allows easy coding of references or end notes.
- Text enclosed in brackets which is preceded by the start of a line, two
periods and a space is treated as a named link. For example:
.. [12] "Effective Techniques" Smith, Joe ...
Is interpreted as '<a name="12">[12]</a> "Effective Techniques" ...'.
Together with the previous rule this allows easy coding of references or
end notes.
- A paragraph that has blocks of text enclosed in '||' is treated as a
table. The text blocks correspond to table cells and table rows are
denoted by newlines. By default the cells are center aligned. A cell
can span more than one column by preceding a block of text with an
equivalent number of cell separators '||'. Newlines and '|' cannot
be a part of the cell text. For example:
|||| **Ingredients** ||
|| *Name* || *Amount* ||
||Spam||10||
||Eggs||3||
is interpreted as::
<TABLE BORDER=1 CELLPADDING=2>
<TR>
<TD ALIGN=CENTER COLSPAN=2> <strong>Ingredients</strong> </TD>
</TR>
<TR>
<TD ALIGN=CENTER COLSPAN=1> <em>Name</em> </TD>
<TD ALIGN=CENTER COLSPAN=1> <em>Amount</em> </TD>
</TR>
<TR>
<TD ALIGN=CENTER COLSPAN=1>Spam</TD>
<TD ALIGN=CENTER COLSPAN=1>10</TD>
</TR>
<TR>
<TD ALIGN=CENTER COLSPAN=1>Eggs</TD>
<TD ALIGN=CENTER COLSPAN=1>3</TD>
</TR>
</TABLE>
$Id$'''
# Copyright
#
# Copyright 1996 Digital Creations, L.C., 910 Princess Anne
# Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
# rights reserved. Copyright in this software is owned by DCLC,
# unless otherwise indicated. Permission to use, copy and
# distribute this software is hereby granted, provided that the
# above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear. Note that
# any product, process or technology described in this software
# may be the subject of other Intellectual Property rights
# reserved by Digital Creations, L.C. and are not licensed
# hereunder.
#
# Trademarks
#
# Digital Creations & DCLC, are trademarks of Digital Creations, L.C..
# All other trademarks are owned by their respective companies.
#
# No Warranty
#
# The software is provided "as is" without warranty of any kind,
# either express or implied, including, but not limited to, the
# implied warranties of merchantability, fitness for a particular
# purpose, or non-infringement. This software could include
# technical inaccuracies or typographical errors. Changes are
# periodically made to the software; these changes will be
# incorporated in new editions of the software. DCLC may make
# improvements and/or changes in this software at any time
# without notice.
#
# Limitation Of Liability
#
# In no event will DCLC be liable for direct, indirect, special,
# incidental, economic, cover, or consequential damages arising
# out of the use of or inability to use this software even if
# advised of the possibility of such damages. Some states do not
# allow the exclusion or limitation of implied warranties or
# limitation of liability for incidental or consequential
# damages, so the above limitation or exclusion may not apply to
# you.
#
#
# If you have questions regarding this software,
# contact:
#
# Jim Fulton, jim@digicool.com
#
# (540) 371-6909
#
# $Log$
# Revision 1.1 2001/03/10 05:07:20 RD
# Added some simple sample apps
#
# Revision 1.27 2000/04/21 13:38:10 jim
# Added closing list tags. Woo hoo!
#
# Revision 1.26 2000/03/14 17:22:04 brian
# Allow ~ in hrefs.
#
# Revision 1.25 2000/02/17 00:53:24 klm
# HTML._str(): We were getting preformatted examples rendered twice,
# second time without preformatting. Problem was a missing 'continue'
# in one of the cases.
#
# Revision 1.24 1999/12/13 16:32:48 klm
# Incorporated pavlos christoforou's mods to handle simple tables. From
# his web page at http://www.zope.org/Members/gaaros/StructuredText:
#
# Structured Text module with table support
#
# A paragraph that has blocks of text enclosed in '||' is treated as a
# table. The text blocks correspond to table cells and table rows are
# denoted by newlines. By default the cells are center aligned. You can
# change the defaults by modifying the CELL,ROW and TABLE class
# attributes in class Table. A cell can span more than one column by
# preceding a block of text with an equivalent number of cell separators
# '||'. Newlines and '|' cannot be a part of the cell text. If you need
# newlines use <BR>. For example:
#
# |||| **Ingredients** ||
# || *Name* || *Amount* ||
# ||Spam||10||
# ||Eggs||3||
#
# Revision 1.23 1999/08/03 20:49:05 jim
# Fixed to allow list elements to introduce examples.
#
# Restructured _str using continue to avoid excessive nesting.
#
# Revision 1.22 1999/08/02 22:01:28 jim
# Fixed a bunch of bugs introduced by making ts_regex actually thread
# safe.
#
# Also localized a bunch of regular expressions
# using "static" variables (aka always default arguments).
#
# Revision 1.21 1999/08/02 13:26:52 jim
# paragraph_divider needs to be a regular (thread-unsafe) regex
# since it gets passed to ts_regex.split, which is thread-safe
# and wants to use regs.
#
# Revision 1.20 1999/07/21 13:33:59 jim
# untabified.
#
# Revision 1.19 1999/07/15 16:43:15 jim
# Checked in Scott Robertson's thread-safety fixes.
#
# Revision 1.18 1999/03/24 00:03:18 klm
# Provide for relative links, eg <a href="file_in_same_dir">whatever</a>,
# as:
#
# "whatever", :file_in_same_dir
#
# or
#
# "whatever"::file_in_same_dir
#
# .__init__(): relax the second gsub, using a '*' instead of a '+', so
# the stuff before the ':' can be missing, and also do postprocessing so
# any resulting '<a href=":file_in_same_dir">'s have the superfluous ':'
# removed. *Seems* good!
#
# Revision 1.17 1999/03/12 23:21:39 klm
# Gratuituous checkin to test my cvs *update* logging hook.
#
# Revision 1.16 1999/03/12 17:12:12 klm
# Added support for underlined elements, in the obvious way (and
# included an entry in the module docstring for it).
#
# Added an entry in the module docstring describing what i *guess* is
# the criterion for identifying header elements. (I'm going to have to
# delve into and understand the framework a bit better before *knowing*
# this is the case.)
#
# Revision 1.15 1999/03/11 22:40:18 klm
# Handle links that include '#' named links.
#
# Revision 1.14 1999/03/11 01:35:19 klm
# Fixed a small typo, and refined the module docstring link example, in
# order to do a checkin to exercise the CVS repository mirroring. Might
# as well include my last checkin message, with some substantial stuff:
#
# Links are now recognized whether or not the candidate strings are
# terminated with punctuation before the trailing whitespace. The old
# form - trailing punctuation then whitespace - is preserved, but the
# punctuation is now unnecessary.
#
# The regular expressions are a bit more complicated, but i've factored
# out the common parts and but them in variables with suggestive names,
# which may make them easier to understand.
#
# Revision 1.13 1999/03/11 00:49:57 klm
# Links are now recognized whether or not the candidate strings are
# terminated with punctuation before the trailing whitespace. The old
# form - trailing punctuation then whitespace - is preserved, but the
# punctuation is now unnecessary.
#
# The regular expressions are a bit more complicated, but i've factored
# out the common parts and but them in variables with suggestive names,
# which may make them easier to understand.
#
# Revision 1.12 1999/03/10 00:15:46 klm
# Committing with version 1.0 of the license.
#
# Revision 1.11 1999/02/08 18:13:12 klm
# Trival checkin (spelling fix "preceedeing" -> "preceding" and similar)
# to see what pitfalls my environment presents to accomplishing a
# successful checkin. (It turns out that i can't do it from aldous because
# the new version of cvs doesn't support the '-t' and '-f' options in the
# cvswrappers file...)
#
# Revision 1.10 1998/12/29 22:30:43 amos
# Improved doc string to describe hyper link and references capabilities.
#
# Revision 1.9 1998/12/04 20:15:31 jim
# Detabification and new copyright.
#
# Revision 1.8 1998/02/27 18:45:22 jim
# Various updates, including new indentation utilities.
#
# Revision 1.7 1997/12/12 15:39:54 jim
# Added level as argument for html_with_references.
#
# Revision 1.6 1997/12/12 15:27:25 jim
# Added additional pattern matching for HTML references.
#
# Revision 1.5 1997/03/08 16:01:03 jim
# Moved code to recognize: "foo bar", url.
# into object initializer, so it gets applied in all cases.
#
# Revision 1.4 1997/02/17 23:36:35 jim
# Added support for "foo title", http:/foohost/foo
#
# Revision 1.3 1996/12/06 15:57:37 jim
# Fixed bugs in character tags.
#
# Added -t command-line option to generate title if:
#
# - The first paragraph is one line (i.e. a heading) and
#
# - All other paragraphs are indented.
#
# Revision 1.2 1996/10/28 13:56:02 jim
# Fixed bug in ordered lists.
# Added option for either HTML-style headings or descriptive-list style
# headings.
#
# Revision 1.1 1996/10/23 14:00:45 jim
# *** empty log message ***
#
#
#
import ts_regex, regex
from ts_regex import gsub
from string import split, join, strip, find
def untabify(aString,
indent_tab=ts_regex.compile('\(\n\|^\)\( *\)\t').search_group,
):
'''\
Convert indentation tabs to spaces.
'''
result=''
rest=aString
while 1:
ts_results = indent_tab(rest, (1,2))
if ts_results:
start, grps = ts_results
lnl=len(grps[0])
indent=len(grps[1])
result=result+rest[:start]
rest="\n%s%s" % (' ' * ((indent/8+1)*8),
rest[start+indent+1+lnl:])
else:
return result+rest
def indent(aString, indent=2):
"""Indent a string the given number of spaces"""
r=split(untabify(aString),'\n')
if not r: return ''
if not r[-1]: del r[-1]
tab=' '*level
return "%s%s\n" % (tab,join(r,'\n'+tab))
def reindent(aString, indent=2, already_untabified=0):
"reindent a block of text, so that the minimum indent is as given"
if not already_untabified: aString=untabify(aString)
l=indent_level(aString)[0]
if indent==l: return aString
r=[]
append=r.append
if indent > l:
tab=' ' * (indent-l)
for s in split(aString,'\n'): append(tab+s)
else:
l=l-indent
for s in split(aString,'\n'): append(s[l:])
return join(r,'\n')
def indent_level(aString,
indent_space=ts_regex.compile('\n\( *\)').search_group,
):
'''\
Find the minimum indentation for a string, not counting blank lines.
'''
start=0
text='\n'+aString
indent=l=len(text)
while 1:
ts_results = indent_space(text, (1,2), start)
if ts_results:
start, grps = ts_results
i=len(grps[0])
start=start+i+1
if start < l and text[start] != '\n': # Skip blank lines
if not i: return (0,aString)
if i < indent: indent = i
else:
return (indent,aString)
def paragraphs(list,start):
l=len(list)
level=list[start][0]
i=start+1
while i < l and list[i][0] > level: i=i+1
return i-1-start
def structure(list):
if not list: return []
i=0
l=len(list)
r=[]
while i < l:
sublen=paragraphs(list,i)
i=i+1
r.append((list[i-1][1],structure(list[i:i+sublen])))
i=i+sublen
return r
class Table:
CELL=' <TD ALIGN=CENTER COLSPAN=%i>%s</TD>\n'
ROW=' <TR>\n%s </TR>\n'
TABLE='\n<TABLE BORDER=1 CELLPADDING=2>\n%s</TABLE>'
def create(self,aPar,td=ts_regex.compile(
'[ \t\n]*||\([^\0|]*\)').match_group):
'''parses a table and returns nested list representing the
table'''
self.table=[]
text=filter(None,split(aPar,'\n'))
for line in text:
row=[]
while 1:
pos=td(line,(1,))
if not pos:return 0
row.append(pos[1])
if pos[0]==len(line):break
line=line[pos[0]:]
self.table.append(row)
return 1
def html(self):
'''Creates an HTML representation of table'''
htmltable=[]
for row in self.table:
htmlrow=[]
colspan=1
for cell in row:
if cell=='':
colspan=colspan+1
continue
else:
htmlrow.append(self.CELL%(colspan,cell))
colspan=1
htmltable.append(self.ROW%join(htmlrow,''))
return self.TABLE%join(htmltable,'')
optional_trailing_punctuation = '\(,\|\([.:?;]\)\)?'
trailing_space = '\([\0- ]\)'
not_punctuation_or_whitespace = "[^-,.?:\0- ]"
table=Table()
class StructuredText:
"""Model text as structured collection of paragraphs.
Structure is implied by the indentation level.
This class is intended as a base classes that do actual text
output formatting.
"""
def __init__(self, aStructuredString, level=0,
paragraph_divider=regex.compile('\(\n *\)+\n'),
):
'''Convert a structured text string into a structured text object.
Aguments:
aStructuredString -- The string to be parsed.
level -- The level of top level headings to be created.
'''
aStructuredString = gsub(
'\"\([^\"\0]+\)\":' # title: <"text":>
+ ('\([-:a-zA-Z0-9_,./?=@#~]+%s\)'
% not_punctuation_or_whitespace)
+ optional_trailing_punctuation
+ trailing_space,
'<a href="\\2">\\1</a>\\4\\5\\6',
aStructuredString)
aStructuredString = gsub(
'\"\([^\"\0]+\)\",[\0- ]+' # title: <"text", >
+ ('\([a-zA-Z]*:[-:a-zA-Z0-9_,./?=@#~]*%s\)'
% not_punctuation_or_whitespace)
+ optional_trailing_punctuation
+ trailing_space,
'<a href="\\2">\\1</a>\\4\\5\\6',
aStructuredString)
protoless = find(aStructuredString, '<a href=":')
if protoless != -1:
aStructuredString = gsub('<a href=":', '<a href="',
aStructuredString)
self.level=level
paragraphs=ts_regex.split(untabify(aStructuredString),
paragraph_divider)
paragraphs=map(indent_level,paragraphs)
self.structure=structure(paragraphs)
def __str__(self):
return str(self.structure)
ctag_prefix="\([\0- (]\|^\)"
ctag_suffix="\([\0- ,.:;!?)]\|$\)"
ctag_middle="[%s]\([^\0- %s][^%s]*[^\0- %s]\|[^%s]\)[%s]"
ctag_middl2="[%s][%s]\([^\0- %s][^%s]*[^\0- %s]\|[^%s]\)[%s][%s]"
def ctag(s,
em=regex.compile(
ctag_prefix+(ctag_middle % (("*",)*6) )+ctag_suffix),
strong=regex.compile(
ctag_prefix+(ctag_middl2 % (("*",)*8))+ctag_suffix),
under=regex.compile(
ctag_prefix+(ctag_middle % (("_",)*6) )+ctag_suffix),
code=regex.compile(
ctag_prefix+(ctag_middle % (("\'",)*6))+ctag_suffix),
):
if s is None: s=''
s=gsub(strong,'\\1<strong>\\2</strong>\\3',s)
s=gsub(under, '\\1<u>\\2</u>\\3',s)
s=gsub(code, '\\1<code>\\2</code>\\3',s)
s=gsub(em, '\\1<em>\\2</em>\\3',s)
return s
class HTML(StructuredText):
'''\
An HTML structured text formatter.
'''\
def __str__(self,
extra_dl=regex.compile("</dl>\n<dl>"),
extra_ul=regex.compile("</ul>\n<ul>"),
extra_ol=regex.compile("</ol>\n<ol>"),
):
'''\
Return an HTML string representation of the structured text data.
'''
s=self._str(self.structure,self.level)
s=gsub(extra_dl,'\n',s)
s=gsub(extra_ul,'\n',s)
s=gsub(extra_ol,'\n',s)
return s
def ul(self, before, p, after):
if p: p="<p>%s</p>" % strip(ctag(p))
return ('%s<ul><li>%s\n%s\n</li></ul>\n'
% (before,p,after))
def ol(self, before, p, after):
if p: p="<p>%s</p>" % strip(ctag(p))
return ('%s<ol><li>%s\n%s\n</li></ol>\n'
% (before,p,after))
def dl(self, before, t, d, after):
return ('%s<dl><dt>%s</dt><dd><p>%s</p>\n%s\n</dd></dl>\n'
% (before,ctag(t),ctag(d),after))
def head(self, before, t, level, d):
if level > 0 and level < 6:
return ('%s<h%d>%s</h%d>\n%s\n'
% (before,level,strip(ctag(t)),level,d))
t="<p><strong>%s</strong><p>" % strip(ctag(t))
return ('%s<dl><dt>%s\n</dt><dd>%s\n</dd></dl>\n'
% (before,t,d))
def normal(self,before,p,after):
return '%s<p>%s</p>\n%s\n' % (before,ctag(p),after)
def pre(self,structure,tagged=0):
if not structure: return ''
if tagged:
r=''
else:
r='<PRE>\n'
for s in structure:
r="%s%s\n\n%s" % (r,html_quote(s[0]),self.pre(s[1],1))
if not tagged: r=r+'</PRE>\n'
return r
def table(self,before,table,after):
return '%s<p>%s</p>\n%s\n' % (before,ctag(table),after)
def _str(self,structure,level,
# Static
bullet=ts_regex.compile('[ \t\n]*[o*-][ \t\n]+\([^\0]*\)'
).match_group,
example=ts_regex.compile('[\0- ]examples?:[\0- ]*$'
).search,
dl=ts_regex.compile('\([^\n]+\)[ \t]+--[ \t\n]+\([^\0]*\)'
).match_group,
nl=ts_regex.compile('\n').search,
ol=ts_regex.compile(
'[ \t]*\(\([0-9]+\|[a-zA-Z]+\)[.)]\)+[ \t\n]+\([^\0]*\|$\)'
).match_group,
olp=ts_regex.compile('[ \t]*([0-9]+)[ \t\n]+\([^\0]*\|$\)'
).match_group,
):
r=''
for s in structure:
ts_results = bullet(s[0], (1,))
if ts_results:
p = ts_results[1]
if s[0][-2:]=='::' and s[1]: ps=self.pre(s[1])
else: ps=self._str(s[1],level)
r=self.ul(r,p,ps)
continue
ts_results = ol(s[0], (3,))
if ts_results:
p = ts_results[1]
if s[0][-2:]=='::' and s[1]: ps=self.pre(s[1])
else: ps=self._str(s[1],level)
r=self.ol(r,p,ps)
continue
ts_results = olp(s[0], (1,))
if ts_results:
p = ts_results[1]
if s[0][-2:]=='::' and s[1]: ps=self.pre(s[1])
else: ps=self._str(s[1],level)
r=self.ol(r,p,ps)
continue
ts_results = dl(s[0], (1,2))
if ts_results:
t,d = ts_results[1]
r=self.dl(r,t,d,self._str(s[1],level))
continue
if example(s[0]) >= 0 and s[1]:
# Introduce an example, using pre tags:
r=self.normal(r,s[0],self.pre(s[1]))
continue
if s[0][-2:]=='::' and s[1]:
# Introduce an example, using pre tags:
r=self.normal(r,s[0][:-1],self.pre(s[1]))
continue
if table.create(s[0]):
## table support.
r=self.table(r,table.html(),self._str(s[1],level))
continue
else:
if nl(s[0]) < 0 and s[1] and s[0][-1:] != ':':
# Treat as a heading
t=s[0]
r=self.head(r,t,level,
self._str(s[1],level and level+1))
else:
r=self.normal(r,s[0],self._str(s[1],level))
return r
def html_quote(v,
character_entities=(
(regex.compile('&'), '&amp;'),
(regex.compile("<"), '&lt;' ),
(regex.compile(">"), '&gt;' ),
(regex.compile('"'), '&quot;')
)): #"
text=str(v)
for re,name in character_entities:
text=gsub(re,name,text)
return text
def html_with_references(text, level=1): def html_with_references(text, level=1):
text = gsub( text = re.sub(
'[\0\n].. \[\([-_0-9_a-zA-Z-]+\)\]', r'[\000\n]\.\. \[([0-9_%s-]+)\]' % letters,
'\n <a name="\\1">[\\1]</a>', r'\n <a name="\1">[\1]</a>',
text) text)
text = gsub( text = re.sub(
'\([\0- ,]\)\[\([0-9_a-zA-Z-]+\)\]\([\0- ,.:]\)', r'([\000- ,])\[(?P<ref>[0-9_%s-]+)\]([\000- ,.:])' % letters,
'\\1<a href="#\\2">[\\2]</a>\\3', r'\1<a href="#\2">[\2]</a>\3',
text) text)
text = gsub( text = re.sub(
'\([\0- ,]\)\[\([^]]+\)\.html\]\([\0- ,.:]\)', r'([\000- ,])\[([^]]+)\.html\]([\000- ,.:])',
'\\1<a href="\\2.html">[\\2]</a>\\3', r'\1<a href="\2.html">[\2]</a>\3',
text) text)
return HTML(text,level=level) return HTML(text,level=level)
def html_quote(v,
character_entities=(
(re.compile('&'), '&amp;'),
(re.compile("<"), '&lt;' ),
(re.compile(">"), '&gt;' ),
(re.compile('"'), '&quot;')
)): #"
text=str(v)
for re,name in character_entities:
text=re.sub(name,text)
return text
def main():
import sys, getopt
opts,args=getopt.getopt(sys.argv[1:],'tw') if __name__=='__main__':
import getopt
if args: opts,args = getopt.getopt(sys.argv[1:],'',[])
[infile]=args
s=open(infile,'r').read()
else:
s=sys.stdin.read()
if opts: for k,v in opts:
pass
if filter(lambda o: o[0]=='-w', opts):
print 'Content-Type: text/html\n'
if s[:2]=='#!': for f in args:
s=ts_regex.sub('^#![^\n]+','',s) print HTML(open(f).read())
r=ts_regex.compile('\([\0-\n]*\n\)')
ts_results = r.match_group(s, (1,))
if ts_results:
s=s[len(ts_results[1]):]
s=str(html_with_references(s))
if s[:4]=='<h1>':
t=s[4:find(s,'</h1>')]
s='''<html><head><title>%s</title>
</head><body>
%s
</body></html>
''' % (t,s)
print s
else:
print html_with_references(s)
if __name__=="__main__": main()

View File

@@ -104,7 +104,7 @@ Document=DocumentClass.DocumentClass()
DocumentWithImages=DocumentWithImages.DocumentWithImages() DocumentWithImages=DocumentWithImages.DocumentWithImages()
HTMLWithImages=HTMLWithImages.HTMLWithImages() HTMLWithImages=HTMLWithImages.HTMLWithImages()
DocBookBook=DocBookClass.DocBookBook DocBookBook=DocBookClass.DocBookBook()
DocBookChapter=DocBookClass.DocBookChapter() DocBookChapter=DocBookClass.DocBookChapter()
DocBookChapterWithFigures=DocBookClass.DocBookChapterWithFigures() DocBookChapterWithFigures=DocBookClass.DocBookChapterWithFigures()
DocBookArticle=DocBookClass.DocBookArticle() DocBookArticle=DocBookClass.DocBookArticle()

View File

@@ -1,215 +0,0 @@
##############################################################################
#
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# This license has been certified as Open Source(tm).
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions in source code must retain the above copyright
# notice, this list of conditions, and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# 3. Digital Creations requests that attribution be given to Zope
# in any manner possible. Zope includes a "Powered by Zope"
# button that is installed by default. While it is not a license
# violation to remove this button, it is requested that the
# attribution remain. A significant investment has been put
# into Zope, and this effort will continue if the Zope community
# continues to grow. This is one way to assure that growth.
#
# 4. All advertising materials and documentation mentioning
# features derived from or use of this software must display
# the following acknowledgement:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# In the event that the product being advertised includes an
# intact Zope distribution (with copyright and license included)
# then this clause is waived.
#
# 5. Names associated with Zope or Digital Creations must not be used to
# endorse or promote products derived from this software without
# prior written permission from Digital Creations.
#
# 6. Modified redistributions of any form whatsoever must retain
# the following acknowledgment:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# Intact (re-)distributions of any official Zope release do not
# require an external acknowledgement.
#
# 7. Modifications are encouraged but must be packaged separately as
# patches to official Zope releases. Distributions that do not
# clearly separate the patches from the original work must be clearly
# labeled as unofficial distributions. Modifications which do not
# carry the name Zope may be packaged in any form, as long as they
# conform to all of the clauses above.
#
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations. Specific
# attributions are listed in the accompanying credits file.
#
##############################################################################
"""Provide a thread-safe interface to regex
"""
import regex, regsub #, Sync
from regex import *
from regsub import split, sub, gsub, splitx, capwords
try:
import thread
except:
class allocate_lock:
def acquire(*args): pass
def release(*args): pass
else:
class SafeFunction:
_l=thread.allocate_lock()
_a=_l.acquire
_r=_l.release
def __init__(self, f):
self._f=f
def __call__(self, *args, **kw):
self._a()
try: return apply(self._f, args, kw)
finally: self._r()
split=SafeFunction(split)
sub=SafeFunction(sub)
gsub=SafeFunction(gsub)
splitx=SafeFunction(splitx)
capwords=SafeFunction(capwords)
allocate_lock=thread.allocate_lock
class compile:
_r=None
groupindex=None
def __init__(self, *args):
self._r=r=apply(regex.compile,args)
self._init(r)
def _init(self, r):
lock=allocate_lock()
self.__a=lock.acquire
self.__r=lock.release
self.translate=r.translate
self.givenpat=r.givenpat
self.realpat=r.realpat
def match(self, string, pos=0):
self.__a()
try: return self._r.match(string, pos)
finally: self.__r()
def search(self, string, pos=0):
self.__a()
try: return self._r.search(string, pos)
finally: self.__r()
def search_group(self, str, group, pos=0):
"""Search a string for a pattern.
If the pattern was not found, then None is returned,
otherwise, the location where the pattern was found,
as well as any specified group are returned.
"""
self.__a()
try:
r=self._r
l=r.search(str, pos)
if l < 0: return None
return l, apply(r.group, group)
finally: self.__r()
def match_group(self, str, group, pos=0):
"""Match a pattern against a string
If the string does not match the pattern, then None is
returned, otherwise, the length of the match, as well
as any specified group are returned.
"""
self.__a()
try:
r=self._r
l=r.match(str, pos)
if l < 0: return None
return l, apply(r.group, group)
finally: self.__r()
def search_regs(self, str, pos=0):
"""Search a string for a pattern.
If the pattern was not found, then None is returned,
otherwise, the 'regs' attribute of the expression is
returned.
"""
self.__a()
try:
r=self._r
r.search(str, pos)
return r.regs
finally: self.__r()
def match_regs(self, str, pos=0):
"""Match a pattern against a string
If the string does not match the pattern, then None is
returned, otherwise, the 'regs' attribute of the expression is
returned.
"""
self.__a()
try:
r=self._r
r.match(str, pos)
return r.regs
finally: self.__r()
class symcomp(compile):
def __init__(self, *args):
self._r=r=apply(regex.symcomp,args)
self._init(r)
self.groupindex=r.groupindex

View File

@@ -102,12 +102,12 @@ class StxFrame(wxFrame):
def LoadStxText(self, text): def LoadStxText(self, text):
# Old ST # Old ST
html = str(StructuredText.html_with_references(text)) #html = str(StructuredText.html_with_references(text))
# NG Version # NG Version
#st = StructuredText.Basic(text) st = StructuredText.Basic(text)
#doc = StructuredText.Document(st) doc = StructuredText.Document(st)
#html = StructuredText.HTML(doc) html = StructuredText.HTMLNG(doc)
self.htmlWin.SetPage(html) self.htmlWin.SetPage(html)
self.editWin.SetValue(text) self.editWin.SetValue(text)