Added some simple sample apps

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@9492 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn 2001-03-10 05:07:25 +00:00
parent 56bd6aaca9
commit c12bc4de58
22 changed files with 47966 additions and 1 deletions

View File

@ -5,6 +5,9 @@ include licence/*.txt
include b
include b.bat
include SWIG/*
exclude SWIG/CVS/*
include demo/*.py
include demo/bitmaps/*.bmp
include demo/bitmaps/*.ico
@ -22,6 +25,16 @@ include demo/data/*.i
include demo/data/*.h
include demo/data/*.py
include samples/doodle/*.txt
include samples/doodle/*.py
include samples/doodle/sample.ddl
include samples/stxview/*.txt
include samples/stxview/*.py
include samples/stxview/*.stx
include samples/stxview/StructuredText/*.py
include samples/wxProject/*.txt
include samples/wxProject/*.py
include wxPython/lib/*.py
include wxPython/lib/*.txt
include wxPython/lib/editor/*.py
@ -29,7 +42,6 @@ include wxPython/lib/editor/*.txt
exclude wxPython/*
exclude tests
exclude SWIG
include src/*.i
include src/*.py

View File

@ -0,0 +1,212 @@
# doodle.py
"""
This module contains the DoodleWindow class which is a window that you
can do simple drawings upon.
"""
from wxPython.wx import *
#----------------------------------------------------------------------
class DoodleWindow(wxWindow):
menuColours = { 100 : 'Black',
101 : 'Yellow',
102 : 'Red',
103 : 'Green',
104 : 'Blue',
105 : 'Purple',
106 : 'Brown',
107 : 'Aquamarine',
108 : 'Forest Green',
109 : 'Light Blue',
110 : 'Goldenrod',
111 : 'Cyan',
112 : 'Orange',
113 : 'Navy',
114 : 'Dark Grey',
115 : 'Light Grey',
}
maxThickness = 16
def __init__(self, parent, ID):
wxWindow.__init__(self, parent, ID)
self.SetBackgroundColour(wxWHITE)
self.listeners = []
self.thickness = 1
self.SetColour("Black")
self.lines = []
self.x = self.y = 0
self.MakeMenu()
# hook some mouse events
EVT_LEFT_DOWN(self, self.OnLeftDown)
EVT_LEFT_UP(self, self.OnLeftUp)
EVT_RIGHT_UP(self, self.OnRightUp)
EVT_MOTION(self, self.OnMotion)
# and the refresh event
EVT_PAINT(self, self.OnPaint)
def __del__(self):
self.menu.Destroy()
def SetColour(self, colour):
"""Set a new colour and make a matching pen"""
self.colour = colour
self.pen = wxPen(wxNamedColour(self.colour), self.thickness, wxSOLID)
self.Notify()
def SetThickness(self, num):
"""Set a new line thickness and make a matching pen"""
self.thickness = num
self.pen = wxPen(wxNamedColour(self.colour), self.thickness, wxSOLID)
self.Notify()
def GetLinesData(self):
return self.lines[:]
def SetLinesData(self, lines):
self.lines = lines[:]
self.Refresh()
def MakeMenu(self):
"""Make a menu that can be popped up later"""
menu = wxMenu()
keys = self.menuColours.keys()
keys.sort()
for k in keys:
text = self.menuColours[k]
menu.Append(k, text, checkable=true)
EVT_MENU_RANGE(self, 100, 200, self.OnMenuSetColour)
EVT_UPDATE_UI_RANGE(self, 100, 200, self.OnCheckMenuColours)
menu.Break()
for x in range(1, self.maxThickness+1):
menu.Append(x, str(x), checkable=true)
EVT_MENU_RANGE(self, 1, self.maxThickness, self.OnMenuSetThickness)
EVT_UPDATE_UI_RANGE(self, 1, self.maxThickness, self.OnCheckMenuThickness)
self.menu = menu
# These two event handlers are called before the menu is displayed
# to determine which items should be checked.
def OnCheckMenuColours(self, event):
text = self.menuColours[event.GetId()]
if text == self.colour:
event.Check(true)
else:
event.Check(false)
def OnCheckMenuThickness(self, event):
if event.GetId() == self.thickness:
event.Check(true)
else:
event.Check(false)
def OnLeftDown(self, event):
"""called when the left mouse button is pressed"""
self.curLine = []
self.x, self.y = event.GetPositionTuple()
self.CaptureMouse()
def OnLeftUp(self, event):
"""called when the left mouse button is released"""
self.lines.append( (self.colour, self.thickness, self.curLine) )
self.curLine = []
self.ReleaseMouse()
def OnRightUp(self, event):
"""called when the right mouse button is released, will popup the menu"""
pt = event.GetPosition()
self.PopupMenu(self.menu, pt)
def OnMotion(self, event):
"""
Called when the mouse is in motion. If the left button is
dragging then draw a line from the last event position to the
current one. Save the coordinants for redraws.
"""
if event.Dragging() and event.LeftIsDown():
dc = wxClientDC(self)
dc.BeginDrawing()
dc.SetPen(self.pen)
pos = event.GetPositionTuple()
coords = (self.x, self.y) + pos
self.curLine.append(coords)
dc.DrawLine(self.x, self.y, pos[0], pos[1])
self.x, self.y = pos
dc.EndDrawing()
def OnPaint(self, event):
"""
Called when the window is exposed. Redraws all the lines that have
been drawn already.
"""
dc = wxPaintDC(self)
dc.BeginDrawing()
for colour, thickness, line in self.lines:
pen = wxPen(wxNamedColour(colour), thickness, wxSOLID)
dc.SetPen(pen)
for coords in line:
apply(dc.DrawLine, coords)
dc.EndDrawing()
# Event handlers for the popup menu, uses the event ID to determine
# the colour or the thickness to set.
def OnMenuSetColour(self, event):
self.SetColour(self.menuColours[event.GetId()])
def OnMenuSetThickness(self, event):
self.SetThickness(event.GetId())
# Observer pattern. Listeners are registered and then notified
# whenever doodle settings change.
def AddListener(self, listener):
self.listeners.append(listener)
def Notify(self):
for other in self.listeners:
other.Update(self.colour, self.thickness)
#----------------------------------------------------------------------
class DoodleFrame(wxFrame):
def __init__(self, parent):
wxFrame.__init__(self, parent, -1, "Doodle Frame", size=(800,600))
self.doodle = DoodleWindow(self, -1)
#----------------------------------------------------------------------
class DoodleApp(wxApp):
def OnInit(self):
frame = DoodleFrame(None)
frame.Show(true)
self.SetTopWindow(frame)
return true
#----------------------------------------------------------------------
if __name__ == '__main__':
app = DoodleApp(0)
app.MainLoop()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,369 @@
# superdoodle.py
"""
This module implements the SuperDoodle demo application. It takes the
DoodleWindow previously presented and reuses it in a much more
intelligent Frame. This one has a menu and a statusbar, is able to
save and reload doodles, clear the workspace, and has a simple control
panel for setting color and line thickness in addition to the popup
menu that DoodleWindow provides. There is also a nice About dialog
implmented using an wxHtmlWindow.
"""
from wxPython.wx import *
from doodle import DoodleWindow
import os, cPickle
#----------------------------------------------------------------------
idNEW = 11001
idOPEN = 11002
idSAVE = 11003
idSAVEAS = 11004
idCLEAR = 11005
idEXIT = 11006
idABOUT = 11007
class DoodleFrame(wxFrame):
"""
A DoodleFrame contains a DoodleWindow and a ControlPanel and manages
their layout with a wxBoxSizer. A menu and associated event handlers
provides for saving a doodle to a file, etc.
"""
title = "Do a doodle"
def __init__(self, parent):
wxFrame.__init__(self, parent, -1, self.title, size=(800,600))
self.CreateStatusBar()
self.MakeMenu()
self.filename = None
self.doodle = DoodleWindow(self, -1)
cPanel = ControlPanel(self, -1, self.doodle)
# Create a sizer to layout the two windows side-by-side.
# Both will grow vertically, the doodle window will grow
# horizontally as well.
box = wxBoxSizer(wxHORIZONTAL)
box.Add(cPanel, 0, wxEXPAND)
box.Add(self.doodle, 1, wxEXPAND)
# Tell the frame that it should layout itself in response to
# size events.
self.SetAutoLayout(true)
self.SetSizer(box)
def SaveFile(self):
if self.filename:
data = self.doodle.GetLinesData()
f = open(self.filename, 'w')
cPickle.dump(data, f)
f.close()
def ReadFile(self):
if self.filename:
try:
f = open(self.filename, 'r')
data = cPickle.load(f)
f.close()
self.doodle.SetLinesData(data)
except cPickle.UnpicklingError:
wxMessageBox("%s is not a doodle file." % self.filename,
"oops!", style=wxOK|wxICON_EXCLAMATION)
def MakeMenu(self):
# create the file menu
menu1 = wxMenu()
menu1.Append(idOPEN, "&Open", "Open a doodle file")
menu1.Append(idSAVE, "&Save", "Save the doodle")
menu1.Append(idSAVEAS, "Save &As", "Save the doodle in a new file")
menu1.AppendSeparator()
menu1.Append(idCLEAR, "&Clear", "Clear the current doodle")
menu1.AppendSeparator()
menu1.Append(idEXIT, "E&xit", "Terminate the application")
# and the help menu
menu2 = wxMenu()
menu2.Append(idABOUT, "&About", "Display the gratuitous 'about this app' thingamajig")
# and add them to a menubar
menuBar = wxMenuBar()
menuBar.Append(menu1, "&File")
menuBar.Append(menu2, "&Help")
self.SetMenuBar(menuBar)
EVT_MENU(self, idOPEN, self.OnMenuOpen)
EVT_MENU(self, idSAVE, self.OnMenuSave)
EVT_MENU(self, idSAVEAS, self.OnMenuSaveAs)
EVT_MENU(self, idCLEAR, self.OnMenuClear)
EVT_MENU(self, idEXIT, self.OnMenuExit)
EVT_MENU(self, idABOUT, self.OnMenuAbout)
wildcard = "Doodle files (*.ddl)|*.ddl|All files (*.*)|*.*"
def OnMenuOpen(self, event):
dlg = wxFileDialog(self, "Open doodle file...",
style=wxOPEN, wildcard = self.wildcard)
if dlg.ShowModal() == wxID_OK:
self.filename = dlg.GetPath()
self.ReadFile()
self.SetTitle(self.title + ' -- ' + self.filename)
dlg.Destroy()
def OnMenuSave(self, event):
if not self.filename:
self.OnMenuSaveAs(event)
else:
self.SaveFile()
def OnMenuSaveAs(self, event):
dlg = wxFileDialog(self, "Save doodle as...",
style=wxSAVE | wxOVERWRITE_PROMPT,
wildcard = self.wildcard)
if dlg.ShowModal() == wxID_OK:
filename = dlg.GetPath()
if not os.path.splitext(filename)[1]:
filename = filename + '.ddl'
self.filename = filename
self.SaveFile()
self.SetTitle(self.title + ' -- ' + self.filename)
dlg.Destroy()
def OnMenuClear(self, event):
self.doodle.SetLinesData([])
self.SetTitle(self.title)
def OnMenuExit(self, event):
self.Close()
def OnMenuAbout(self, event):
dlg = DoodleAbout(self)
dlg.ShowModal()
dlg.Destroy()
#----------------------------------------------------------------------
class ControlPanel(wxPanel):
"""
This class implements a very simple control panel for the DoodleWindow.
It creates buttons for each of the colours and thickneses supported by
the DoodleWindow, and event handlers to set the selected values. There is
also a little window that shows an example doodleLine in the selected
values. Nested sizers are used for layout.
"""
def __init__(self, parent, ID, doodle):
wxPanel.__init__(self, parent, ID, style=wxRAISED_BORDER)
numCols = 4
spacing = 4
# Make a grid of buttons for each colour. Attach each button
# event to self.OnSetColour. The button ID is the same as the
# key in the colour dictionary.
colours = doodle.menuColours
keys = colours.keys()
keys.sort()
cGrid = wxGridSizer(cols=numCols, hgap=2, vgap=2)
for k in keys:
bmp = self.MakeBitmap(wxNamedColour(colours[k]))
b = wxBitmapButton(self, k, bmp)
EVT_BUTTON(self, k, self.OnSetColour)
cGrid.Add(b, 0)
# Save the button size so we can use it for the number buttons
btnSize = b.GetSize()
# Make a grid of buttons for the thicknesses. Attach each button
# event to self.OnSetThickness. The button ID is the same as the
# thickness value.
tGrid = wxGridSizer(cols=numCols, hgap=2, vgap=2)
for x in range(1, doodle.maxThickness+1):
b = wxButton(self, x, str(x), size=btnSize)
EVT_BUTTON(self, x, self.OnSetThickness)
tGrid.Add(b, 0)
# Make a colour indicator window, it is registerd as a listener
# with the doodle window so it will be notified when the settings
# change
ci = ColourIndicator(self)
doodle.AddListener(ci)
doodle.Notify()
self.doodle = doodle
# Make a box sizer and put the two grids and the indicator
# window in it.
box = wxBoxSizer(wxVERTICAL)
box.Add(cGrid, 0, wxALL, spacing)
box.Add(tGrid, 0, wxALL, spacing)
box.Add(ci, 0, wxEXPAND|wxALL, spacing)
self.SetSizer(box)
self.SetAutoLayout(true)
# Resize this window so it is just large enough for the
# minimum requirements of the sizer.
box.Fit(self)
def MakeBitmap(self, colour):
"""
We can create a bitmap of whatever we want by simply selecting
it into a wxMemoryDC and drawing on it. In this case we just set
a background brush and clear the dc.
"""
bmp = wxEmptyBitmap(16,16)
dc = wxMemoryDC()
dc.SelectObject(bmp)
dc.SetBackground(wxBrush(colour))
dc.Clear()
dc.SelectObject(wxNullBitmap)
return bmp
def OnSetColour(self, event):
"""
Use the event ID to get the colour, set that colour in the doodle.
"""
colour = self.doodle.menuColours[event.GetId()]
self.doodle.SetColour(colour)
def OnSetThickness(self, event):
"""
Use the event ID to set the thickness in the doodle.
"""
self.doodle.SetThickness(event.GetId())
#----------------------------------------------------------------------
class ColourIndicator(wxWindow):
"""
An instance of this class is used on the ControlPanel to show
a sample of what the current doodle line will look like.
"""
def __init__(self, parent):
wxWindow.__init__(self, parent, -1, style=wxSUNKEN_BORDER)
self.SetBackgroundColour(wxWHITE)
self.SetSize(wxSize(-1, 40))
self.colour = self.thickness = None
EVT_PAINT(self, self.OnPaint)
def Update(self, colour, thickness):
"""
The doodle window calls this method any time the colour
or line thickness changes.
"""
self.colour = colour
self.thickness = thickness
self.Refresh() # generate a paint event
def OnPaint(self, event):
"""
This method is called when all or part of the window needs to be
redrawn.
"""
dc = wxPaintDC(self)
if self.colour:
sz = self.GetClientSize()
pen = wxPen(wxNamedColour(self.colour), self.thickness)
dc.BeginDrawing()
dc.SetPen(pen)
dc.DrawLine(10, sz.height/2, sz.width-10, sz.height/2)
dc.EndDrawing()
#----------------------------------------------------------------------
class DoodleAbout(wxDialog):
""" An about box that uses an HTML window """
text = '''
<html>
<body bgcolor="#ACAA60">
<center><table bgcolor="#455481" width="100%" cellspacing="0"
cellpadding="0" border="1">
<tr>
<td align="center"><h1>SuperDoodle</h1></td>
</tr>
</table>
</center>
<p><b>SuperDoodle</b> is a demonstration program for <b>wxPython</b> that
will hopefully teach you a thing or two. Just follow these simple
instructions: </p>
<p>
<ol>
<li><b>Read</b> the Source...
<li><b>Learn</b>...
<li><b>Do!</b>
</ol>
<p><b>SuperDoodle</b> and <b>wxPython</b> are brought to you by
<b>Robin Dunn</b> and <b>Total Control Software</b>, Copyright
&copy; 1997-2001.</p>
</body>
</html>
'''
def __init__(self, parent):
wxDialog.__init__(self, parent, -1, 'About SuperDoodle',
size=wxSize(420, 380))
from wxPython.html import wxHtmlWindow
html = wxHtmlWindow(self, -1)
html.SetPage(self.text)
button = wxButton(self, wxID_OK, "Okay")
# constraints for the html window
lc = wxLayoutConstraints()
lc.top.SameAs(self, wxTop, 5)
lc.left.SameAs(self, wxLeft, 5)
lc.bottom.SameAs(button, wxTop, 5)
lc.right.SameAs(self, wxRight, 5)
html.SetConstraints(lc)
# constraints for the button
lc = wxLayoutConstraints()
lc.bottom.SameAs(self, wxBottom, 5)
lc.centreX.SameAs(self, wxCentreX)
lc.width.AsIs()
lc.height.AsIs()
button.SetConstraints(lc)
self.SetAutoLayout(true)
self.Layout()
self.CentreOnParent(wxBOTH)
#----------------------------------------------------------------------
class DoodleApp(wxApp):
def OnInit(self):
frame = DoodleFrame(None)
frame.Show(true)
self.SetTopWindow(frame)
return true
#----------------------------------------------------------------------
if __name__ == '__main__':
app = DoodleApp(0)
app.MainLoop()

View File

@ -0,0 +1,689 @@
##############################################################################
#
# 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.
#
##############################################################################
import re, ST, STDOM
from string import split, join, replace, expandtabs, strip, find
StringType=type('')
ListType=type([])
class StructuredTextExample(ST.StructuredTextParagraph):
"""Represents a section of document with literal text, as for examples"""
def __init__(self, subs, **kw):
t=[]; a=t.append
for s in subs: a(s.getNodeValue())
apply(ST.StructuredTextParagraph.__init__,
(self, join(t,'\n\n'), ()),
kw)
def getColorizableTexts(self): return ()
def setColorizableTexts(self, src): pass # never color examples
class StructuredTextBullet(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextNumbered(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextDescriptionTitle(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextDescriptionBody(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextDescription(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
def __init__(self, title, src, subs, **kw):
apply(ST.StructuredTextParagraph.__init__, (self, src, subs), kw)
self._title=title
def getColorizableTexts(self): return self._title, self._src
def setColorizableTexts(self, src): self._title, self._src = src
def getChildren(self):
return (StructuredTextDescriptionTitle(self._title),
StructuredTextDescriptionBody(self._src, self._subs))
class StructuredTextSectionTitle(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextSection(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
def __init__(self, src, subs=None, **kw):
apply(ST.StructuredTextParagraph.__init__,
(self, StructuredTextSectionTitle(src), subs),
kw)
# a StructuredTextTable holds StructuredTextRows
class StructuredTextTable(ST.StructuredTextDocument):
"""
rows is a list of lists containing tuples, which
represent the columns/cells in each rows.
EX
rows = [[('row 1:column1',1)],[('row2:column1',1)]]
"""
def __init__(self, rows, src, subs, **kw):
apply(ST.StructuredTextDocument.__init__,(self,subs),kw)
self._rows = []
for row in rows:
if row:
self._rows.append(StructuredTextRow(row,kw))
def getRows(self):
return [self._rows]
def _getRows(self):
return self.getRows()
def getColorizableTexts(self):
"""
return a tuple where each item is a column/cell's
contents. The tuple, result, will be of this format.
("r1 col1", "r1=col2", "r2 col1", "r2 col2")
"""
#result = ()
result = []
for row in self._rows:
for column in row.getColumns()[0]:
#result = result[:] + (column.getColorizableTexts(),)
result.append(column.getColorizableTexts()[0])
return result
def setColorizableTexts(self,texts):
"""
texts is going to a tuple where each item is the
result of being mapped to the colortext function.
Need to insert the results appropriately into the
individual columns/cells
"""
for row_index in range(len(self._rows)):
for column_index in range(len(self._rows[row_index]._columns)):
self._rows[row_index]._columns[column_index].setColorizableTexts((texts[0],))
texts = texts[1:]
def _getColorizableTexts(self):
return self.getColorizableTexts()
def _setColorizableTexts(self):
return self.setColorizableTexts()
# StructuredTextRow holds StructuredTextColumns
class StructuredTextRow(ST.StructuredTextDocument):
def __init__(self,row,kw):
"""
row is a list of tuples, where each tuple is
the raw text for a cell/column and the span
of that cell/column".
EX
[('this is column one',1), ('this is column two',1)]
"""
apply(ST.StructuredTextDocument.__init__,(self,[]),kw)
self._columns = []
for column in row:
self._columns.append(StructuredTextColumn(column[0],column[1],kw))
def getColumns(self):
return [self._columns]
def _getColumns(self):
return [self._columns]
# this holds the raw text of a table cell
class StructuredTextColumn(ST.StructuredTextParagraph):
"""
StructuredTextColumn is a cell/column in a table.
This contains the actual text of a column and is
thus a StructuredTextParagraph. A StructuredTextColumn
also holds the span of its column
"""
def __init__(self,text,span,kw):
apply(ST.StructuredTextParagraph.__init__,(self,text,[]),kw)
self._span = span
def getSpan(self):
return self._span
def _getSpan(self):
return self._span
class StructuredTextMarkup(STDOM.Element):
def __init__(self, v, **kw):
self._value=v
self._attributes=kw.keys()
for k, v in kw.items(): setattr(self, k, v)
def getChildren(self, type=type, lt=type([])):
v=self._value
if type(v) is not lt: v=[v]
return v
def getColorizableTexts(self): return self._value,
def setColorizableTexts(self, v): self._value=v[0]
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, `self._value`)
class StructuredTextLiteral(StructuredTextMarkup):
def getColorizableTexts(self): return ()
def setColorizableTexts(self, v): pass
class StructuredTextEmphasis(StructuredTextMarkup): pass
class StructuredTextStrong(StructuredTextMarkup): pass
class StructuredTextInnerLink(StructuredTextMarkup): pass
class StructuredTextNamedLink(StructuredTextMarkup): pass
class StructuredTextUnderline(StructuredTextMarkup): pass
class StructuredTextLink(StructuredTextMarkup):
"A simple hyperlink"
class DocumentClass:
"""
Class instance calls [ex.=> x()] require a structured text
structure. Doc will then parse each paragraph in the structure
and will find the special structures within each paragraph.
Each special structure will be stored as an instance. Special
structures within another special structure are stored within
the 'top' structure
EX : '-underline this-' => would be turned into an underline
instance. '-underline **this**' would be stored as an underline
instance with a strong instance stored in its string
"""
paragraph_types = [
'doc_bullet',
'doc_numbered',
'doc_description',
'doc_header',
'doc_table',
]
text_types = [
'doc_href',
'doc_strong',
'doc_emphasize',
'doc_literal',
'doc_inner_link',
'doc_named_link',
'doc_underline',
]
def __call__(self, doc):
if type(doc) is type(''):
doc=ST.StructuredText(doc)
doc.setSubparagraphs(self.color_paragraphs(
doc.getSubparagraphs()))
else:
doc=ST.StructuredTextDocument(self.color_paragraphs(
doc.getSubparagraphs()))
return doc
def parse(self, raw_string, text_type,
type=type, st=type(''), lt=type([])):
"""
Parse accepts a raw_string, an expr to test the raw_string,
and the raw_string's subparagraphs.
Parse will continue to search through raw_string until
all instances of expr in raw_string are found.
If no instances of expr are found, raw_string is returned.
Otherwise a list of substrings and instances is returned
"""
tmp = [] # the list to be returned if raw_string is split
append=tmp.append
if type(text_type) is st: text_type=getattr(self, text_type)
while 1:
t = text_type(raw_string)
if not t: break
#an instance of expr was found
t, start, end = t
if start: append(raw_string[0:start])
tt=type(t)
if tt is st:
# if we get a string back, add it to text to be parsed
raw_string = t+raw_string[end:len(raw_string)]
else:
if tt is lt:
# is we get a list, append it's elements
tmp[len(tmp):]=t
else:
# normal case, an object
append(t)
raw_string = raw_string[end:len(raw_string)]
if not tmp: return raw_string # nothing found
if raw_string: append(raw_string)
elif len(tmp)==1: return tmp[0]
return tmp
def color_text(self, str, types=None):
"""Search the paragraph for each special structure
"""
if types is None: types=self.text_types
for text_type in types:
if type(str) is StringType:
str = self.parse(str, text_type)
elif type(str) is ListType:
r=[]; a=r.append
for s in str:
if type(s) is StringType:
s=self.parse(s, text_type)
if type(s) is ListType: r[len(r):]=s
else: a(s)
else:
s.setColorizableTexts(
map(self.color_text,
s.getColorizableTexts()
))
a(s)
str=r
else:
r=[]; a=r.append; color=self.color_text
for s in str.getColorizableTexts():
color(s, (text_type,))
a(s)
str.setColorizableTexts(r)
return str
def color_paragraphs(self, raw_paragraphs,
type=type, sequence_types=(type([]), type(())),
st=type('')):
result=[]
for paragraph in raw_paragraphs:
if paragraph.getNodeName() != 'StructuredTextParagraph':
result.append(paragraph)
continue
for pt in self.paragraph_types:
if type(pt) is st:
# grab the corresponding function
pt=getattr(self, pt)
# evaluate the paragraph
r=pt(paragraph)
if r:
if type(r) not in sequence_types:
r=r,
new_paragraphs=r
for paragraph in new_paragraphs:
paragraph.setSubparagraphs(self.color_paragraphs(paragraph.getSubparagraphs()))
break
else:
new_paragraphs=ST.StructuredTextParagraph(paragraph.getColorizableTexts()[0],
self.color_paragraphs(paragraph.getSubparagraphs()),
indent=paragraph.indent),
# color the inline StructuredText types
# for each StructuredTextParagraph
for paragraph in new_paragraphs:
paragraph.setColorizableTexts(
map(self.color_text,
paragraph.getColorizableTexts()
))
result.append(paragraph)
return result
def doc_table(self,paragraph, expr = re.compile('(\s*)([||]+)').match):
text = paragraph.getColorizableTexts()[0]
m = expr(text)
if not (m):
return None
rows = []
# initial split
for row in split(text,"\n"):
rows.append(row)
# clean up the rows
for index in range(len(rows)):
tmp = []
rows[index] = strip(rows[index])
l = len(rows[index])-2
result = split(rows[index][:l],"||")
for text in result:
if text:
tmp.append(text)
tmp.append('')
else:
tmp.append(text)
rows[index] = tmp
# remove trailing '''s
for index in range(len(rows)):
l = len(rows[index])-1
rows[index] = rows[index][:l]
result = []
for row in rows:
cspan = 0
tmp = []
for item in row:
if item:
tmp.append(item,cspan)
cspan = 0
else:
cspan = cspan + 1
result.append(tmp)
subs = paragraph.getSubparagraphs()
indent=paragraph.indent
return StructuredTextTable(result,text,subs,indent=paragraph.indent)
def doc_bullet(self, paragraph, expr = re.compile('\s*[-*o]\s+').match):
top=paragraph.getColorizableTexts()[0]
m=expr(top)
if not m:
return None
subs=paragraph.getSubparagraphs()
if top[-2:]=='::':
subs=[StructuredTextExample(subs)]
top=top[:-1]
return StructuredTextBullet(top[m.span()[1]:], subs,
indent=paragraph.indent,
bullet=top[:m.span()[1]]
)
def doc_numbered(
self, paragraph,
expr = re.compile('(\s*[a-zA-Z]+\.)|(\s*[0-9]+\.)|(\s*[0-9]+\s+)').match):
# This is the old expression. It had a nasty habit
# of grabbing paragraphs that began with a single
# letter word even if there was no following period.
#expr = re.compile('\s*'
# '(([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.)*'
# '([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.?'
# '\s+').match):
top=paragraph.getColorizableTexts()[0]
m=expr(top)
if not m: return None
subs=paragraph.getSubparagraphs()
if top[-2:]=='::':
subs=[StructuredTextExample(subs)]
top=top[:-1]
return StructuredTextNumbered(top[m.span()[1]:], subs,
indent=paragraph.indent,
number=top[:m.span()[1]])
def doc_description(
self, paragraph,
delim = re.compile('\s+--\s+').search,
nb=re.compile(r'[^\0- ]').search,
):
top=paragraph.getColorizableTexts()[0]
d=delim(top)
if not d: return None
start, end = d.span()
title=top[:start]
if find(title, '\n') >= 0: return None
if not nb(title): return None
d=top[start:end]
top=top[end:]
subs=paragraph.getSubparagraphs()
if top[-2:]=='::':
subs=[StructuredTextExample(subs)]
top=top[:-1]
return StructuredTextDescription(
title, top, subs,
indent=paragraph.indent,
delim=d)
def doc_header(self, paragraph,
expr = re.compile('[ a-zA-Z0-9.:/,-_*<>\?\'\"]+').match
):
subs=paragraph.getSubparagraphs()
if not subs: return None
top=paragraph.getColorizableTexts()[0]
if not strip(top): return None
if top[-2:]=='::':
subs=StructuredTextExample(subs)
if strip(top)=='::': return subs
return ST.StructuredTextParagraph(top[:-1],
[subs],
indent=paragraph.indent,
level=paragraph.level)
if find(top,'\n') >= 0: return None
return StructuredTextSection(top, subs, indent=paragraph.indent, level=paragraph.level)
def doc_literal(
self, s,
expr=re.compile(
"(?:\s|^)'" # open
"([^ \t\n\r\f\v']|[^ \t\n\r\f\v'][^\n']*[^ \t\n\r\f\v'])" # contents
"'(?:\s|[,.;:!?]|$)" # close
).search):
r=expr(s)
if r:
start, end = r.span(1)
return (StructuredTextLiteral(s[start:end]), start-1, end+1)
else:
return None
def doc_emphasize(
self, s,
expr = re.compile('\s*\*([ \na-zA-Z0-9.:/;,\'\"\?]+)\*(?!\*|-)').search
):
r=expr(s)
if r:
start, end = r.span(1)
return (StructuredTextEmphasis(s[start:end]), start-1, end+1)
else:
return None
def doc_inner_link(self,
s,
expr1 = re.compile("\.\.\s*").search,
expr2 = re.compile("\[[a-zA-Z0-9]+\]").search):
# make sure we dont grab a named link
if expr2(s) and expr1(s):
start1,end1 = expr1(s).span()
start2,end2 = expr2(s).span()
if end1 == start2:
# uh-oh, looks like a named link
return None
else:
# the .. is somewhere else, ignore it
return (StructuredTextInnerLink(s[start2+1,end2-1],start2,end2))
return None
elif expr2(s) and not expr1(s):
start,end = expr2(s).span()
return (StructuredTextInnerLink(s[start+1:end-1]),start,end)
return None
def doc_named_link(self,
s,
expr=re.compile("(\.\.\s)(\[[a-zA-Z0-9]+\])").search):
result = expr(s)
if result:
start,end = result.span(2)
a,b = result.span(1)
str = strip(s[a:b]) + s[start:end]
str = s[start+1:end-1]
st,en = result.span()
return (StructuredTextNamedLink(str),st,en)
#return (StructuredTextNamedLink(s[st:en]),st,en)
return None
def doc_underline(self,
s,
expr=re.compile("\_([a-zA-Z0-9\s\.,\?\/]+)\_").search):
result = expr(s)
if result:
start,end = result.span(1)
st,e = result.span()
return (StructuredTextUnderline(s[start:end]),st,e)
else:
return None
def doc_strong(self,
s,
expr = re.compile('\s*\*\*([ \na-zA-Z0-9.:/;\-,!\?\'\"]+)\*\*').search
):
r=expr(s)
if r:
start, end = r.span(1)
return (StructuredTextStrong(s[start:end]), start-2, end+2)
else:
return None
def doc_href(
self, s,
expr1 = re.compile("(\"[ a-zA-Z0-9\n\-\.\,\;\(\)\/\:\/]+\")(:)([a-zA-Z0-9\:\/\.\~\-]+)([,]*\s*)").search,
expr2 = re.compile('(\"[ a-zA-Z0-9\n\-\.\:\;\(\)\/]+\")([,]+\s+)([a-zA-Z0-9\@\.\,\?\!\/\:\;\-\#]+)(\s*)').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)
if r:
# need to grab the href part and the
# beginning part
start,e = r.span(1)
name = s[start:e]
name = replace(name,'"','',2)
#start = start + 1
st,end = r.span(3)
if punctuation(s[end-1:end]):
end = end -1
link = s[st:end]
#end = end - 1
# name is the href title, link is the target
# of the href
return (StructuredTextLink(name, href=link),
start, end)
#return (StructuredTextLink(s[start:end], href=s[start:end]),
# start, end)
else:
return None

View File

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

View File

@ -0,0 +1,777 @@
##############################################################################
#
# 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.
#
##############################################################################
import re, ST, STDOM
from string import split, join, replace, expandtabs, strip, find, rstrip
StringType=type('')
ListType=type([])
class StructuredTextExample(ST.StructuredTextParagraph):
"""Represents a section of document with literal text, as for examples"""
def __init__(self, subs, **kw):
t=[]; a=t.append
for s in subs: a(s.getNodeValue())
apply(ST.StructuredTextParagraph.__init__,
(self, join(t,'\n\n'), ()),
kw)
def getColorizableTexts(self): return ()
def setColorizableTexts(self, src): pass # never color examples
class StructuredTextBullet(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextNumbered(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextDescriptionTitle(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextDescriptionBody(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextDescription(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
def __init__(self, title, src, subs, **kw):
apply(ST.StructuredTextParagraph.__init__, (self, src, subs), kw)
self._title=title
def getColorizableTexts(self): return self._title, self._src
def setColorizableTexts(self, src): self._title, self._src = src
def getChildren(self):
return (StructuredTextDescriptionTitle(self._title),
StructuredTextDescriptionBody(self._src, self._subs))
class StructuredTextSectionTitle(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextSection(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
def __init__(self, src, subs=None, **kw):
apply(ST.StructuredTextParagraph.__init__,
(self, StructuredTextSectionTitle(src), subs),
kw)
# a StructuredTextTable holds StructuredTextRows
class StructuredTextTable(ST.StructuredTextDocument):
"""
rows is a list of lists containing tuples, which
represent the columns/cells in each rows.
EX
rows = [[('row 1:column1',1)],[('row2:column1',1)]]
"""
def __init__(self, rows, src, subs, **kw):
apply(ST.StructuredTextDocument.__init__,(self,subs),kw)
self._rows = []
for row in rows:
if row:
self._rows.append(StructuredTextRow(row,kw))
def getRows(self):
return [self._rows]
def _getRows(self):
return self.getRows()
def getColumns(self):
result = []
for row in self._rows:
result.append(row.getColumns())
return result
def _getColumns(self):
return self.getColumns()
def setColumns(self,columns):
for index in range(len(self._rows)):
self._rows[index].setColumns(columns[index])
def _setColumns(self,columns):
return self.setColumns(columns)
def getColorizableTexts(self):
"""
return a tuple where each item is a column/cell's
contents. The tuple, result, will be of this format.
("r1 col1", "r1=col2", "r2 col1", "r2 col2")
"""
result = []
for row in self._rows:
for column in row.getColumns()[0]:
result.append(column.getColorizableTexts()[0])
return result
def setColorizableTexts(self,texts):
"""
texts is going to a tuple where each item is the
result of being mapped to the colortext function.
Need to insert the results appropriately into the
individual columns/cells
"""
for row_index in range(len(self._rows)):
for column_index in range(len(self._rows[row_index]._columns)):
self._rows[row_index]._columns[column_index].setColorizableTexts((texts[0],))
texts = texts[1:]
def _getColorizableTexts(self):
return self.getColorizableTexts()
def _setColorizableTexts(self):
return self.setColorizableTexts()
# StructuredTextRow holds StructuredTextColumns
class StructuredTextRow(ST.StructuredTextDocument):
def __init__(self,row,kw):
"""
row is a list of tuples, where each tuple is
the raw text for a cell/column and the span
of that cell/column".
EX
[('this is column one',1), ('this is column two',1)]
"""
apply(ST.StructuredTextDocument.__init__,(self,[]),kw)
self._columns = []
for column in row:
self._columns.append(StructuredTextColumn(column[0],column[1],kw))
def getColumns(self):
return [self._columns]
def _getColumns(self):
return [self._columns]
def setColumns(self,columns):
self._columns = columns
def _setColumns(self,columns):
return self.setColumns(columns)
# this holds the text of a table cell
class StructuredTextColumn(ST.StructuredTextParagraph):
"""
StructuredTextColumn is a cell/column in a table.
A cell can hold multiple paragraphs. The cell
is either classified as a StructuredTextTableHeader
or StructuredTextTableData.
"""
def __init__(self,text,span,kw):
# print "StructuredTextColumn", text, span
apply(ST.StructuredTextParagraph.__init__,(self,text,[]),kw)
self._span = span
def getSpan(self):
return self._span
def _getSpan(self):
return self._span
class StructuredTextTableHeader(ST.StructuredTextDocument): pass
class StructuredTextTableData(ST.StructuredTextDocument): pass
class StructuredTextMarkup(STDOM.Element):
def __init__(self, v, **kw):
self._value=v
self._attributes=kw.keys()
for k, v in kw.items(): setattr(self, k, v)
def getChildren(self, type=type, lt=type([])):
v=self._value
if type(v) is not lt: v=[v]
return v
def getColorizableTexts(self): return self._value,
def setColorizableTexts(self, v): self._value=v[0]
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, `self._value`)
class StructuredTextLiteral(StructuredTextMarkup):
def getColorizableTexts(self): return ()
def setColorizableTexts(self, v): pass
class StructuredTextEmphasis(StructuredTextMarkup): pass
class StructuredTextStrong(StructuredTextMarkup): pass
class StructuredTextInnerLink(StructuredTextMarkup): pass
class StructuredTextNamedLink(StructuredTextMarkup): pass
class StructuredTextUnderline(StructuredTextMarkup): pass
class StructuredTextSGML(StructuredTextMarkup): pass
class StructuredTextLink(StructuredTextMarkup): pass
class DocumentClass:
"""
Class instance calls [ex.=> x()] require a structured text
structure. Doc will then parse each paragraph in the structure
and will find the special structures within each paragraph.
Each special structure will be stored as an instance. Special
structures within another special structure are stored within
the 'top' structure
EX : '-underline this-' => would be turned into an underline
instance. '-underline **this**' would be stored as an underline
instance with a strong instance stored in its string
"""
#'doc_table',
paragraph_types = [
'doc_bullet',
'doc_numbered',
'doc_description',
'doc_header',
'doc_table',
]
#'doc_inner_link',
#'doc_named_link',
#'doc_underline',
text_types = [
'doc_href',
'doc_strong',
'doc_emphasize',
'doc_literal',
'doc_sgml'
]
def __call__(self, doc):
if type(doc) is type(''):
doc=ST.StructuredText(doc)
doc.setSubparagraphs(self.color_paragraphs(
doc.getSubparagraphs()))
else:
doc=ST.StructuredTextDocument(self.color_paragraphs(
doc.getSubparagraphs()))
return doc
def parse(self, raw_string, text_type,
type=type, st=type(''), lt=type([])):
"""
Parse accepts a raw_string, an expr to test the raw_string,
and the raw_string's subparagraphs.
Parse will continue to search through raw_string until
all instances of expr in raw_string are found.
If no instances of expr are found, raw_string is returned.
Otherwise a list of substrings and instances is returned
"""
tmp = [] # the list to be returned if raw_string is split
append=tmp.append
if type(text_type) is st: text_type=getattr(self, text_type)
while 1:
t = text_type(raw_string)
if not t: break
#an instance of expr was found
t, start, end = t
if start: append(raw_string[0:start])
tt=type(t)
if tt is st:
# if we get a string back, add it to text to be parsed
raw_string = t+raw_string[end:len(raw_string)]
else:
if tt is lt:
# is we get a list, append it's elements
tmp[len(tmp):]=t
else:
# normal case, an object
append(t)
raw_string = raw_string[end:len(raw_string)]
if not tmp: return raw_string # nothing found
if raw_string: append(raw_string)
elif len(tmp)==1: return tmp[0]
return tmp
def color_text(self, str, types=None):
"""Search the paragraph for each special structure
"""
if types is None: types=self.text_types
for text_type in types:
if type(str) is StringType:
str = self.parse(str, text_type)
elif type(str) is ListType:
r=[]; a=r.append
for s in str:
if type(s) is StringType:
s=self.parse(s, text_type)
if type(s) is ListType: r[len(r):]=s
else: a(s)
else:
s.setColorizableTexts(
map(self.color_text,
s.getColorizableTexts()
))
a(s)
str=r
else:
r=[]; a=r.append; color=self.color_text
for s in str.getColorizableTexts():
color(s, (text_type,))
a(s)
str.setColorizableTexts(r)
return str
def color_paragraphs(self, raw_paragraphs,
type=type, sequence_types=(type([]), type(())),
st=type('')):
result=[]
for paragraph in raw_paragraphs:
#print type(paragraph)
if paragraph.getNodeName() != 'StructuredTextParagraph':
result.append(paragraph)
continue
for pt in self.paragraph_types:
if type(pt) is st:
# grab the corresponding function
pt=getattr(self, pt)
# evaluate the paragraph
r=pt(paragraph)
if r:
if type(r) not in sequence_types:
r=r,
new_paragraphs=r
for paragraph in new_paragraphs:
paragraph.setSubparagraphs(self.color_paragraphs(paragraph.getSubparagraphs()))
break
else:
new_paragraphs=ST.StructuredTextParagraph(paragraph.getColorizableTexts()[0],
self.color_paragraphs(paragraph.getSubparagraphs()),
indent=paragraph.indent),
# color the inline StructuredText types
# for each StructuredTextParagraph
for paragraph in new_paragraphs:
if paragraph.getNodeName() is "StructuredTextTable":
#print "we have a table"
cells = paragraph.getColumns()
text = paragraph.getColorizableTexts()
text = map(ST.StructuredText,text)
text = map(self.__call__,text)
#for index in range(len(text)):
# text[index].setColorizableTexts(map(self.color_text,text[index].getColorizableTexts()))
paragraph.setColorizableTexts(text)
paragraph.setColorizableTexts(
map(self.color_text,
paragraph.getColorizableTexts()
))
result.append(paragraph)
return result
def doc_table(self, paragraph, expr = re.compile('\s*\|[-]+\|').match):
text = paragraph.getColorizableTexts()[0]
m = expr(text)
subs = paragraph.getSubparagraphs()
if not (m):
return None
rows = []
rows = split(text,'\n')
spans = []
ROWS = []
COLS = []
TDdivider = re.compile("[\-]+").match
THdivider = re.compile("[\=]+").match
# find where the column markers are located
col = re.compile('\|').search
text = strip(text)
rows = split(text,'\n')
for row in range(len(rows)):
rows[row] = strip(rows[row])
for row in rows:
tmp = strip(row)
tmp = row[1:len(tmp)-1] # remove leading and trailing |
offset = 0
if col(tmp):
while col(tmp):
start,end = col(tmp).span()
if not start+offset in spans:
spans.append(start + offset)
COLS.append((tmp[0:start],start+offset))
tmp = " " + tmp[end:]
offset = offset + (start)
if not offset+len(tmp) in spans:
spans.append(offset+len(tmp))
COLS.append((tmp,offset+len(tmp)))
ROWS.append(COLS)
COLS = []
spans.sort()
ROWS = ROWS[1:len(ROWS)]
# find each column span
cols = []
tmp = []
for row in ROWS:
for c in row:
tmp.append(c[1])
cols.append(tmp)
tmp = []
cur = 1 # the current column span
tmp = []
C = [] # holds the span of each cell
for col in cols:
for span in spans:
if not span in col:
cur = cur + 1
else:
tmp.append(cur)
cur = 1
C.append(tmp)
tmp = []
# make rows contain the cell's text and the span
# of that cell
for index in range(len(C)):
for i in range(len(C[index])):
ROWS[index][i] = (ROWS[index][i][0],C[index][i])
rows = ROWS
# now munge the table cells together
ROWS = []
COLS = []
for row in rows:
for index in range(len(row)):
if not COLS:
COLS = range(len(row))
for i in range(len(COLS)):
COLS[i] = ["",1]
if TDdivider(row[index][0]) or THdivider(row[index][0]):
ROWS.append(COLS)
COLS = []
else:
COLS[index][0] = COLS[index][0] + rstrip(row[index][0]) + "\n"
COLS[index][1] = row[index][1]
return StructuredTextTable(ROWS,text,subs,indent=paragraph.indent)
def doc_bullet(self, paragraph, expr = re.compile('\s*[-*o]\s+').match):
top=paragraph.getColorizableTexts()[0]
m=expr(top)
if not m:
return None
subs=paragraph.getSubparagraphs()
if top[-2:]=='::':
subs=[StructuredTextExample(subs)]
top=top[:-1]
return StructuredTextBullet(top[m.span()[1]:], subs,
indent=paragraph.indent,
bullet=top[:m.span()[1]]
)
def doc_numbered(
self, paragraph,
expr = re.compile('(\s*[a-zA-Z]+\.)|(\s*[0-9]+\.)|(\s*[0-9]+\s+)').match):
# This is the old expression. It had a nasty habit
# of grabbing paragraphs that began with a single
# letter word even if there was no following period.
#expr = re.compile('\s*'
# '(([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.)*'
# '([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.?'
# '\s+').match):
top=paragraph.getColorizableTexts()[0]
m=expr(top)
if not m: return None
subs=paragraph.getSubparagraphs()
if top[-2:]=='::':
subs=[StructuredTextExample(subs)]
top=top[:-1]
return StructuredTextNumbered(top[m.span()[1]:], subs,
indent=paragraph.indent,
number=top[:m.span()[1]])
def doc_description(
self, paragraph,
delim = re.compile('\s+--\s+').search,
nb=re.compile(r'[^\0- ]').search,
):
top=paragraph.getColorizableTexts()[0]
d=delim(top)
if not d: return None
start, end = d.span()
title=top[:start]
if find(title, '\n') >= 0: return None
if not nb(title): return None
d=top[start:end]
top=top[end:]
subs=paragraph.getSubparagraphs()
if top[-2:]=='::':
subs=[StructuredTextExample(subs)]
top=top[:-1]
return StructuredTextDescription(
title, top, subs,
indent=paragraph.indent,
delim=d)
def doc_header(self, paragraph,
expr = re.compile('[ a-zA-Z0-9.:/,-_*<>\?\'\"]+').match
):
subs=paragraph.getSubparagraphs()
if not subs: return None
top=paragraph.getColorizableTexts()[0]
if not strip(top): return None
if top[-2:]=='::':
subs=StructuredTextExample(subs)
if strip(top)=='::': return subs
return ST.StructuredTextParagraph(
top[:-1], [subs], indent=paragraph.indent)
if find(top,'\n') >= 0: return None
return StructuredTextSection(top, subs, indent=paragraph.indent)
def doc_literal(
self, s,
expr=re.compile(
"(?:\s|^)'" # open
"([^ \t\n\r\f\v']|[^ \t\n\r\f\v'][^\n']*[^ \t\n\r\f\v'])" # contents
"'(?:\s|[,.;:!?]|$)" # close
).search):
r=expr(s)
if r:
start, end = r.span(1)
return (StructuredTextLiteral(s[start:end]), start-1, end+1)
else:
return None
def doc_emphasize(
self, s,
expr = re.compile('\s*\*([ \na-zA-Z0-9.:/;,\'\"\?]+)\*(?!\*|-)').search
):
r=expr(s)
if r:
start, end = r.span(1)
return (StructuredTextEmphasis(s[start:end]), start-1, end+1)
else:
return None
def doc_inner_link(self,
s,
expr1 = re.compile("\.\.\s*").search,
expr2 = re.compile("\[[a-zA-Z0-9]+\]").search):
# make sure we dont grab a named link
if expr2(s) and expr1(s):
start1,end1 = expr1(s).span()
start2,end2 = expr2(s).span()
if end1 == start2:
# uh-oh, looks like a named link
return None
else:
# the .. is somewhere else, ignore it
return (StructuredTextInnerLink(s[start2+1,end2-1],start2,end2))
return None
elif expr2(s) and not expr1(s):
start,end = expr2(s).span()
return (StructuredTextInnerLink(s[start+1:end-1]),start,end)
return None
def doc_named_link(self,
s,
expr=re.compile("(\.\.\s)(\[[a-zA-Z0-9]+\])").search):
result = expr(s)
if result:
start,end = result.span(2)
a,b = result.span(1)
str = strip(s[a:b]) + s[start:end]
st,en = result.span()
return (StructuredTextNamedLink(str),st,en)
#return (StructuredTextNamedLink(s[st:en]),st,en)
return None
def doc_underline(self,
s,
expr=re.compile("\_([a-zA-Z0-9\s\.,\?]+)\_").search):
result = expr(s)
if result:
start,end = result.span(1)
st,e = result.span()
return (StructuredTextUnderline(s[start:end]),st,e)
else:
return None
def doc_strong(self,
s,
expr = re.compile('\s*\*\*([ \na-zA-Z0-9.:/;\-,!\?\'\"]+)\*\*').search
):
r=expr(s)
if r:
start, end = r.span(1)
return (StructuredTextStrong(s[start:end]), start-2, end+2)
else:
return None
def doc_href(
self, s,
expr1 = re.compile("(\"[ a-zA-Z0-9\n\-\.\,\;\(\)\/\:\/]+\")(:)([a-zA-Z0-9\:\/\.\~\-]+)([,]*\s*)").search,
expr2 = re.compile('(\"[ a-zA-Z0-9\n\-\.\:\;\(\)\/]+\")([,]+\s+)([a-zA-Z0-9\@\.\,\?\!\/\:\;\-\#]+)(\s*)').search):
punctuation = re.compile("[\,\.\?\!\;]+").match
r=expr1(s) or expr2(s)
if r:
# need to grab the href part and the
# beginning part
start,e = r.span(1)
name = s[start:e]
name = replace(name,'"','',2)
#start = start + 1
st,end = r.span(3)
if punctuation(s[end-1:end]):
end = end -1
link = s[st:end]
#end = end - 1
# name is the href title, link is the target
# of the href
return (StructuredTextLink(name, href=link),
start, end)
#return (StructuredTextLink(s[start:end], href=s[start:end]),
# start, end)
else:
return None
def doc_sgml(self,s,expr=re.compile("\<[a-zA-Z0-9\.\=\'\"\:\/\-\#\+\s]+\>").search):
"""
SGML text is ignored and outputed as-is
"""
r = expr(s)
if r:
start,end = r.span()
text = s[start:end]
return (StructuredTextSGML(text),start,end)

View File

@ -0,0 +1,134 @@
##############################################################################
#
# 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.
#
##############################################################################
import re, ST, STDOM
from string import split, join, replace, expandtabs, strip, find
from DocumentClass import *
class StructuredTextImage(StructuredTextMarkup):
"A simple embedded image"
class DocumentWithImages(DocumentClass):
"""
"""
text_types = [
'doc_img',
] + DocumentClass.text_types
def doc_img(
self, s,
expr1=re.compile('\"([ _a-zA-Z0-9*.:/;,\-\n\~]+)\":img:([a-zA-Z0-9\-.:/;,\n\~]+)').search,
expr2=re.compile('\"([ _a-zA-Z0-9*.:/;,\-\n\~]+)\":img:([a-zA-Z0-9\-.:/;,\n\~]+):([a-zA-Z0-9\-.:/;,\n\~]+)').search
):
r = expr2(s)
if r:
startt, endt = r.span(1)
startk, endk = r.span(2)
starth, endh = r.span(3)
start, end = r.span()
return (StructuredTextImage(s[startt:endt], href=s[starth:endh], key=s[startk:endk]),
start, end)
else:
r=expr1(s)
if r:
startt, endt = r.span(1)
starth, endh = r.span(2)
start, end = r.span()
return (StructuredTextImage(s[startt:endt], href=s[starth:endh]),
start, end)
return None

View File

@ -0,0 +1,308 @@
##############################################################################
#
# 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.
#
##############################################################################
from string import join, split, find
import re, sys, ST
class HTMLClass:
element_types={
'#text': '_text',
'StructuredTextDocument': 'document',
'StructuredTextParagraph': 'paragraph',
'StructuredTextExample': 'example',
'StructuredTextBullet': 'bullet',
'StructuredTextNumbered': 'numbered',
'StructuredTextDescription': 'description',
'StructuredTextDescriptionTitle': 'descriptionTitle',
'StructuredTextDescriptionBody': 'descriptionBody',
'StructuredTextSection': 'section',
'StructuredTextSectionTitle': 'sectionTitle',
'StructuredTextLiteral': 'literal',
'StructuredTextEmphasis': 'emphasis',
'StructuredTextStrong': 'strong',
'StructuredTextLink': 'link',
'StructuredTextXref': 'xref',
'StructuredTextInnerLink':'innerLink',
'StructuredTextNamedLink':'namedLink',
'StructuredTextUnderline':'underline',
'StructuredTextTable':'table',
'StructuredTextSGML':'sgml',
}
def dispatch(self, doc, level, output):
getattr(self, self.element_types[doc.getNodeName()])(doc, level, output)
def __call__(self, doc, level=1):
r=[]
self.dispatch(doc, level-1, r.append)
return join(r,'')
def _text(self, doc, level, output):
output(doc.getNodeValue())
def document(self, doc, level, output):
output('<html>\n')
children=doc.getChildNodes()
if (children and
children[0].getNodeName() == 'StructuredTextSection'):
output('<head>\n<title>%s</title>\n</head>\n' %
children[0].getChildNodes()[0].getNodeValue())
output('<body>\n')
for c in children:
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</body>\n')
output('</html>\n')
def section(self, doc, level, output):
children=doc.getChildNodes()
for c in children:
getattr(self, self.element_types[c.getNodeName()])(c, level+1, output)
def sectionTitle(self, doc, level, output):
output('<h%d>' % (level))
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</h%d>\n' % (level))
def description(self, doc, level, output):
p=doc.getPreviousSibling()
if p is None or p.getNodeName() is not doc.getNodeName():
output('<dl>\n')
for c in doc.getChildNodes():
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('</dl>\n')
def descriptionTitle(self, doc, level, output):
output('<dt>')
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</dt>\n')
def descriptionBody(self, doc, level, output):
output('<dd>')
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</dd>\n')
def bullet(self, doc, level, output):
p=doc.getPreviousSibling()
if p is None or p.getNodeName() is not doc.getNodeName():
output('<ul>\n')
output('<li>')
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
n=doc.getNextSibling()
output('</li>\n')
if n is None or n.getNodeName() is not doc.getNodeName():
output('</ul>\n')
def numbered(self, doc, level, output):
p=doc.getPreviousSibling()
if p is None or p.getNodeName() is not doc.getNodeName():
output('<ol>\n')
output('<li>')
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
n=doc.getNextSibling()
output('</li>\n')
if n is None or n.getNodeName() is not doc.getNodeName():
output('</ol>\n')
def example(self, doc, level, output):
i=0
for c in doc.getChildNodes():
if i==0:
output('<pre>')
output(html_quote(c.getNodeValue()))
output('</pre>\n')
else:
getattr(self, self.element_types[c.getNodeName()])(
c, level, output)
def paragraph(self, doc, level, output):
i=0
output('<p>')
for c in doc.getChildNodes():
if c.getNodeName() in ['StructuredTextParagraph']:
getattr(self, self.element_types[c.getNodeName()])(
c, level, output)
else:
getattr(self, self.element_types[c.getNodeName()])(
c, level, output)
output('</p>')
def link(self, doc, level, output):
output('<a href="%s">' % doc.href)
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</a>')
def emphasis(self, doc, level, output):
output('<em>')
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</em>')
def literal(self, doc, level, output):
output('<code>')
for c in doc.getChildNodes():
output(html_quote(c.getNodeValue()))
output('</code>')
def strong(self, doc, level, output):
output('<strong>')
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</strong>')
def underline(self, doc, level, output):
output("<u>")
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output("</u>")
def innerLink(self, doc, level, output):
output('<a href="#');
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('">[')
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output(']</a>')
def namedLink(self, doc, level, output):
output('<a name="')
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('">[')
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output(']</a>')
def sgml(self,doc,level,output):
for c in doc.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
def table(self,doc,level,output):
"""
A StructuredTextTable holds StructuredTextRow(s) which
holds StructuredTextColumn(s). A StructuredTextColumn
is a type of StructuredTextParagraph and thus holds
the actual data.
"""
output("<table border=1 cellpadding=2>\n")
for row in doc.getRows()[0]:
output("<tr>\n")
for column in row.getColumns()[0]:
str = "<td colspan=%s>" % column.getSpan()
output(str)
#for c in doc.getChildNodes():
# getattr(self, self.element_types[c.getNodeName()])(c, level, output)
for c in column.getChildNodes():
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output("</td>\n")
output("</tr>\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

@ -0,0 +1,133 @@
##############################################################################
#
# 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.
#
##############################################################################
from string import join, split, find
import re, sys, ST
import time
from HTMLClass import HTMLClass
ets = HTMLClass.element_types
ets.update({'StructuredTextImage': 'image'})
class HTMLWithImages(HTMLClass):
element_types = ets
def document(self, doc, level, output):
output('<html>\n')
children=doc.getChildNodes()
if (children and
children[0].getNodeName() == 'StructuredTextSection'):
output('<head>\n<title>%s</title>\n</head>\n' %
children[0].getChildNodes()[0].getNodeValue())
output('<body bgcolor="#FFFFFF">\n')
for c in children:
getattr(self, self.element_types[c.getNodeName()])(c, level, output)
output('</body>\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):
if hasattr(doc, 'key'):
output('<a name="%s"></a>\n<img src="%s" alt="%s">' % (doc.key, doc.href, doc.getNodeValue()))
else:
output('<img src="%s" alt="%s">' % (doc.href, doc.getNodeValue()))
def xref(self, doc, level, output):
val = doc.getNodeValue()
output('<a href="#%s">%s</a>' % (val, val) )

View File

@ -0,0 +1,170 @@
##############################################################################
#
# 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

@ -0,0 +1,278 @@
import re, STDOM
from string import split, join, replace, expandtabs, strip, find
#####################################################################
# Updated functions #
#####################################################################
def indention(str,front = re.compile("^\s+").match):
"""
Convert all tabs to the appropriate number of spaces.
Find the number of leading spaces. If none, return 0
"""
if front(str):
start,end = front(str).span()
return end-start-1
else:
return 0 # no leading spaces
def insert(struct, top, level):
"""
find what will be the parant paragraph of
a sentence and return that paragraph's
sub-paragraphs. The new paragraph will be
appended to those sub-paragraphs
"""
#print "struct", struct, top-1
if not top-1 in range(len(struct)):
return None
run = struct[top-1]
i = 0
while i+1 < level:
run = run.getSubparagraphs()[len(run.getSubparagraphs())-1]
i = i + 1
#print "parent for level ", level, " was => ", run.getColorizableTexts()
return run.getSubparagraphs()
def display(struct):
"""
runs through the structure and prints out
the paragraphs. If the insertion works
correctly, display's results should mimic
the orignal paragraphs.
"""
if struct.getColorizableTexts():
print join(struct.getColorizableTexts()),"\n"
if struct.getSubparagraphs():
for x in struct.getSubparagraphs():
display(x)
def display2(struct):
"""
runs through the structure and prints out
the paragraphs. If the insertion works
correctly, display's results should mimic
the orignal paragraphs.
"""
if struct.getNodeValue():
print struct.getNodeValue(),"\n"
if struct.getSubparagraphs():
for x in struct.getSubparagraphs():
display(x)
def findlevel(levels,indent):
"""
remove all level information of levels
with a greater level of indentation.
Then return which level should insert this
paragraph
"""
keys = levels.keys()
for key in keys:
if levels[key] > indent:
del(levels[key])
keys = levels.keys()
if not(keys):
return 0
else:
for key in keys:
if levels[key] == indent:
return key
highest = 0
for key in keys:
if key > highest:
highest = key
return highest-1
#####################################################################
# Golly, the capitalization of this function always makes me think it's a class
def StructuredText(paragraphs, paragraph_delimiter=re.compile('\n\s*\n')):
"""
StructuredText accepts paragraphs, which is a list of
lines to be parsed. StructuredText creates a structure
which mimics the structure of the paragraphs.
Structure => [paragraph,[sub-paragraphs]]
"""
currentlevel = 0
currentindent = 0
levels = {0:0}
level = 0 # which header are we under
struct = [] # the structure to be returned
run = struct
paragraphs = filter(
strip,
paragraph_delimiter.split(expandtabs('\n\n'+paragraphs+'\n\n'))
)
if not paragraphs: return []
ind = [] # structure based on indention levels
for paragraph in paragraphs:
ind.append([indention(paragraph), paragraph])
currentindent = indention(paragraphs[0])
levels[0] = currentindent
#############################################################
# updated #
#############################################################
for indent,paragraph in ind :
if indent == 0:
level = level + 1
currentlevel = 0
currentindent = 0
levels = {0:0}
struct.append(StructuredTextParagraph(paragraph, indent=indent, level=currentlevel))
elif indent > currentindent:
currentlevel = currentlevel + 1
currentindent = indent
levels[currentlevel] = indent
run = insert(struct,level,currentlevel)
run.append(StructuredTextParagraph(paragraph, indent=indent, level=currentlevel))
elif indent < currentindent:
result = findlevel(levels,indent)
if result > 0:
currentlevel = result
currentindent = indent
run = insert(struct,level,currentlevel)
run.append(StructuredTextParagraph(paragraph, indent=indent, level=currentlevel))
else:
if insert(struct,level,currentlevel):
run = insert(struct,level,currentlevel)
else:
run = struct
currentindet = indent
run.append(StructuredTextParagraph(paragraph, indent=indent, level=currentlevel))
return StructuredTextDocument(struct)
Basic = StructuredText
class StructuredTextParagraph(STDOM.Element):
indent=0
def __init__(self, src, subs=None, **kw):
if subs is None: subs=[]
self._src=src
self._subs=list(subs)
self._attributes=kw.keys()
for k, v in kw.items(): setattr(self, k, v)
def getChildren(self, type=type, lt=type([])):
src=self._src
if type(src) is not lt: src=[src]
return src+self._subs
def getAttribute(self, name):
return getattr(self, name, None)
def getAttributeNode(self, name):
if hasattr(self, name):
return STDOM.Attr(name, getattr(self, name))
def getAttributes(self):
d={}
for a in self._attributes:
d[a]=getattr(self, a, '')
return STDOM.NamedNodeMap(d)
def getSubparagraphs(self):
return self._subs
def setSubparagraphs(self, subs):
self._subs=subs
def getColorizableTexts(self):
return (self._src,)
def setColorizableTexts(self, src):
self._src=src[0]
def __repr__(self):
r=[]; a=r.append
a((' '*(self.indent or 0))+
('%s(' % self.__class__.__name__)
+str(self._src)+', ['
)
for p in self._subs: a(`p`)
a((' '*(self.indent or 0))+'])')
return join(r,'\n')
"""
create aliases for all above functions in the pythony way.
"""
def _get_Children(self, type=type, lt=type([])):
return self.getChildren(type,lt)
def _get_Attribute(self, name):
return self.getAttribute(name)
def _get_AttributeNode(self, name):
return self.getAttributeNode(name)
def _get_Attributes(self):
return self.getAttributes()
def _get_Subparagraphs(self):
return self.getSubparagraphs()
def _set_Subparagraphs(self, subs):
return self.setSubparagraphs(subs)
def _get_ColorizableTexts(self):
return self.getColorizableTexts()
def _set_ColorizableTexts(self, src):
return self.setColorizableTexts(src)
class StructuredTextDocument(StructuredTextParagraph):
"""
A StructuredTextDocument holds StructuredTextParagraphs
as its subparagraphs.
"""
_attributes=()
def __init__(self, subs=None, **kw):
apply(StructuredTextParagraph.__init__,
(self, '', subs),
kw)
def getChildren(self):
return self._subs
def getColorizableTexts(self):
return ()
def setColorizableTexts(self, src):
pass
def __repr__(self):
r=[]; a=r.append
a('%s([' % self.__class__.__name__)
for p in self._subs: a(`p`+',')
a('])')
return join(r,'\n')
"""
create aliases for all above functions in the pythony way.
"""
def _get_Children(self):
return self.getChildren()
def _get_ColorizableTexts(self):
return self.getColorizableTexts()
def _set_ColorizableTexts(self, src):
return self.setColorizableTexts(src)

View File

@ -0,0 +1,736 @@
##############################################################################
#
# 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.
#
##############################################################################
"""
DOM implementation in StructuredText : Read-Only methods
All standard Zope objects support DOM to a limited extent.
"""
import string
# Node type codes
# ---------------
ELEMENT_NODE = 1
ATTRIBUTE_NODE = 2
TEXT_NODE = 3
CDATA_SECTION_NODE = 4
ENTITY_REFERENCE_NODE = 5
ENTITY_NODE = 6
PROCESSING_INSTRUCTION_NODE = 7
COMMENT_NODE = 8
DOCUMENT_NODE = 9
DOCUMENT_TYPE_NODE = 10
DOCUMENT_FRAGMENT_NODE = 11
NOTATION_NODE = 12
# Exception codes
# ---------------
INDEX_SIZE_ERR = 1
DOMSTRING_SIZE_ERR = 2
HIERARCHY_REQUEST_ERR = 3
WRONG_DOCUMENT_ERR = 4
INVALID_CHARACTER_ERR = 5
NO_DATA_ALLOWED_ERR = 6
NO_MODIFICATION_ALLOWED_ERR = 7
NOT_FOUND_ERR = 8
NOT_SUPPORTED_ERR = 9
INUSE_ATTRIBUTE_ERR = 10
# Exceptions
# ----------
class DOMException(Exception):
pass
class IndexSizeException(DOMException):
code = INDEX_SIZE_ERR
class DOMStringSizeException(DOMException):
code = DOMSTRING_SIZE_ERR
class HierarchyRequestException(DOMException):
code = HIERARCHY_REQUEST_ERR
class WrongDocumentException(DOMException):
code = WRONG_DOCUMENT_ERR
class InvalidCharacterException(DOMException):
code = INVALID_CHARACTER_ERR
class NoDataAllowedException(DOMException):
code = NO_DATA_ALLOWED_ERR
class NoModificationAllowedException(DOMException):
code = NO_MODIFICATION_ALLOWED_ERR
class NotFoundException(DOMException):
code = NOT_FOUND_ERR
class NotSupportedException(DOMException):
code = NOT_SUPPORTED_ERR
class InUseAttributeException(DOMException):
code = INUSE_ATTRIBUTE_ERR
# Node classes
# ------------
class ParentNode:
"""
A node that can have children, or, more precisely, that implements
the child access methods of the DOM.
"""
def getChildNodes(self, type=type, st=type('')):
"""
Returns a NodeList that contains all children of this node.
If there are no children, this is a empty NodeList
"""
r=[]
for n in self.getChildren():
if type(n) is st: n=TextNode(n)
r.append(n.__of__(self))
return NodeList(r)
def getFirstChild(self, type=type, st=type('')):
"""
The first child of this node. If there is no such node
this returns None
"""
children = self.getChildren()
if not children:
return None
n=chidren[0]
if type(n) is st:
n=TextNode(n)
return n.__of__(self)
def getLastChild(self, type=type, st=type('')):
"""
The last child of this node. If there is no such node
this returns None.
"""
children = self.getChildren()
if not children: return None
n=chidren[-1]
if type(n) is st: n=TextNode(n)
return n.__of__(self)
"""
create aliases for all above functions in the pythony way.
"""
def _get_ChildNodes(self, type=type, st=type('')):
return self.getChildNodes(type,st)
def _get_FirstChild(self, type=type, st=type('')):
return self.getFirstChild(type,st)
def _get_LastChild(self, type=type, st=type('')):
return self.getLastChild(type,st)
class NodeWrapper(ParentNode):
"""
This is an acquisition-like wrapper that provides parent access for
DOM sans circular references!
"""
def __init__(self, aq_self, aq_parent):
self.aq_self=aq_self
self.aq_parent=aq_parent
def __getattr__(self, name):
return getattr(self.aq_self, name)
def getParentNode(self):
"""
The parent of this node. All nodes except Document
DocumentFragment and Attr may have a parent
"""
return self.aq_parent
def _getDOMIndex(self, children, getattr=getattr):
i=0
self=self.aq_self
for child in children:
if getattr(child, 'aq_self', child) is self:
self._DOMIndex=i
return i
i=i+1
return None
def getPreviousSibling(self,
type=type,
st=type(''),
getattr=getattr,
None=None):
"""
The node immediately preceding this node. If
there is no such node, this returns None.
"""
children = self.aq_parent.getChildren()
if not children:
return None
index=getattr(self, '_DOMIndex', None)
if index is None:
index=self._getDOMIndex(children)
if index is None: return None
index=index-1
if index < 0: return None
try: n=children[index]
except IndexError: return None
else:
if type(n) is st:
n=TextNode(n)
n._DOMIndex=index
return n.__of__(self)
def getNextSibling(self, type=type, st=type('')):
"""
The node immediately preceding this node. If
there is no such node, this returns None.
"""
children = self.aq_parent.getChildren()
if not children:
return None
index=getattr(self, '_DOMIndex', None)
if index is None:
index=self._getDOMIndex(children)
if index is None:
return None
index=index+1
try: n=children[index]
except IndexError:
return None
else:
if type(n) is st:
n=TextNode(n)
n._DOMIndex=index
return n.__of__(self)
def getOwnerDocument(self):
"""
The Document object associated with this node, if any.
"""
return self.aq_parent.getOwnerDocument()
"""
create aliases for all above functions in the pythony way.
"""
def _get_ParentNode(self):
return self.getParentNode()
def _get_DOMIndex(self, children, getattr=getattr):
return self._getDOMIndex(children,getattr)
def _get_PreviousSibling(self,
type=type,
st=type(''),
getattr=getattr,
None=None):
return self.getPreviousSibling(type,st,getattr,None)
def _get_NextSibling(self, type=type, st=type('')):
return self.getNextSibling(type,st)
def _get_OwnerDocument(self):
return self.getOwnerDocument()
class Node(ParentNode):
"""
Node Interface
"""
# Get a DOM wrapper with a parent link
def __of__(self, parent):
return NodeWrapper(self, parent)
# DOM attributes
# --------------
def getNodeName(self):
"""
The name of this node, depending on its type
"""
def getNodeValue(self):
"""
The value of this node, depending on its type
"""
return None
def getParentNode(self):
"""
The parent of this node. All nodes except Document
DocumentFragment and Attr may have a parent
"""
def getChildren(self):
"""
Get a Python sequence of children
"""
return ()
def getPreviousSibling(self,
type=type,
st=type(''),
getattr=getattr,
None=None):
"""
The node immediately preceding this node. If
there is no such node, this returns None.
"""
def getNextSibling(self, type=type, st=type('')):
"""
The node immediately preceding this node. If
there is no such node, this returns None.
"""
def getAttributes(self):
"""
Returns a NamedNodeMap containing the attributes
of this node (if it is an element) or None otherwise.
"""
return None
def getOwnerDocument(self):
"""
The Document object associated with this node, if any.
"""
# DOM Methods
# -----------
def hasChildNodes(self):
"""
Returns true if the node has any children, false
if it doesn't.
"""
return len(self.getChildren())
"""
create aliases for all above functions in the pythony way.
"""
def _get_NodeName(self):
return self.getNodeName()
def _get_NodeValue(self):
return self.getNodeValue()
def _get_ParentNode(self):
return self.getParentNode()
def _get_Children(self):
return self.getChildren()
def _get_PreviousSibling(self,
type=type,
st=type(''),
getattr=getattr,
None=None):
return self.getPreviousSibling(type,st,getattr,None)
def _get_NextSibling(self, type=type, st=type('')):
return self.getNextSibling()
def _get_Attributes(self):
return self.getAttributes()
def _get_OwnerDocument(self):
return self.getOwnerDocument()
def _has_ChildNodes(self):
return self.hasChildNodes()
class TextNode(Node):
def __init__(self, str): self._value=str
def getNodeType(self):
return TEXT_NODE
def getNodeName(self):
return '#text'
def getNodeValue(self):
return self._value
"""
create aliases for all above functions in the pythony way.
"""
def _get_NodeType(self):
return self.getNodeType()
def _get_NodeName(self):
return self.getNodeName()
def _get_NodeValue(self):
return self.getNodeValue()
class Element(Node):
"""
Element interface
"""
# Element Attributes
# ------------------
def getTagName(self):
"""The name of the element"""
return self.__class__.__name__
def getNodeName(self):
"""The name of this node, depending on its type"""
return self.__class__.__name__
def getNodeType(self):
"""A code representing the type of the node."""
return ELEMENT_NODE
def getNodeValue(self, type=type, st=type('')):
r=[]
for c in self.getChildren():
if type(c) is not st:
c=c.getNodeValue()
r.append(c)
return string.join(r,'')
def getParentNode(self):
"""
The parent of this node. All nodes except Document
DocumentFragment and Attr may have a parent
"""
# Element Methods
# ---------------
_attributes=()
def getAttribute(self, name): return getattr(self, name, None)
def getAttributeNode(self, name):
if hasattr(self, name):
return Attr(name, getattr(self, name))
def getAttributes(self):
d={}
for a in self._attributes:
d[a]=getattr(self, a, '')
return NamedNodeMap(d)
def getAttribute(self, name):
"""Retrieves an attribute value by name."""
return None
def getAttributeNode(self, name):
""" Retrieves an Attr node by name or None if
there is no such attribute. """
return None
def getElementsByTagName(self, tagname):
"""
Returns a NodeList of all the Elements with a given tag
name in the order in which they would be encountered in a
preorder traversal of the Document tree. Parameter: tagname
The name of the tag to match (* = all tags). Return Value: A new
NodeList object containing all the matched Elements.
"""
nodeList = []
for child in self.getChildren():
if (child.getNodeType()==ELEMENT_NODE and \
child.getTagName()==tagname or tagname== '*'):
nodeList.append(child)
if hasattr(child, 'getElementsByTagName'):
n1 = child.getElementsByTagName(tagname)
nodeList = nodeList + n1._data
return NodeList(nodeList)
"""
create aliases for all above functions in the pythony way.
"""
def _get_TagName(self):
return self.getTagName()
def _get_NodeName(self):
return self.getNodeName()
def _get_NodeType(self):
return self.getNodeType()
def _get_NodeValue(self, type=type, st=type('')):
return self.GetNodeValue(type,st)
def _get_ParentNode(self):
return self.getParentNode()
def _get_Attribute(self, name):
return self.getAttribute(name)
def _get_AttributeNode(self, name):
return self.getAttributeNode(name)
def _get_Attributes(self):
return self.getAttributes()
def _get_Attribute(self, name):
return self.getAttribute(name)
def _get_AttributeNode(self, name):
return self.getAttributeNode(name)
def _get_ElementsByTagName(self, tagname):
return self.getElementsByTagName(tagname)
class NodeList:
"""
NodeList interface - Provides the abstraction of an ordered
collection of nodes.
Python extensions: can use sequence-style 'len', 'getitem', and
'for..in' constructs.
"""
def __init__(self,list=None):
self._data = list or []
def __getitem__(self, index, type=type, st=type('')):
return self._data[index]
def __getslice__(self, i, j):
return self._data[i:j]
def item(self, index):
"""
Returns the index-th item in the collection
"""
try: return self._data[index]
except IndexError: return None
def getLength(self):
"""
The length of the NodeList
"""
return len(self._data)
__len__=getLength
"""
create aliases for all above functions in the pythony way.
"""
def _get_Length(self):
return self.getLength()
class NamedNodeMap:
"""
NamedNodeMap interface - Is used to represent collections
of nodes that can be accessed by name. NamedNodeMaps are not
maintained in any particular order.
Python extensions: can use sequence-style 'len', 'getitem', and
'for..in' constructs, and mapping-style 'getitem'.
"""
def __init__(self, data=None):
if data is None:
data = {}
self._data = data
def item(self, index):
"""
Returns the index-th item in the map
"""
try: return self._data.values()[index]
except IndexError: return None
def __getitem__(self, key):
if type(key)==type(1):
return self._data.values()[key]
else:
return self._data[key]
def getLength(self):
"""
The length of the NodeList
"""
return len(self._data)
__len__ = getLength
def getNamedItem(self, name):
"""
Retrieves a node specified by name. Parameters:
name Name of a node to retrieve. Return Value A Node (of any
type) with the specified name, or None if the specified name
did not identify any node in the map.
"""
if self._data.has_key(name):
return self._data[name]
return None
"""
create aliases for all above functions in the pythony way.
"""
def _get_Length(self):
return self.getLength()
def _get_NamedItem(self, name):
return self.getNamedItem(name)
class Attr(Node):
"""
Attr interface - The Attr interface represents an attriubte in an
Element object. Attr objects inherit the Node Interface
"""
def __init__(self, name, value, specified=1):
self.name = name
self.value = value
self.specified = specified
def getNodeName(self):
"""
The name of this node, depending on its type
"""
return self.name
def getName(self):
"""
Returns the name of this attribute.
"""
return self.name
def getNodeValue(self):
"""
The value of this node, depending on its type
"""
return self.value
def getNodeType(self):
"""
A code representing the type of the node.
"""
return ATTRIBUTE_NODE
def getSpecified(self):
"""
If this attribute was explicitly given a value in the
original document, this is true; otherwise, it is false.
"""
return self.specified
"""
create aliases for all above functions in the pythony way.
"""
def _get_NodeName(self):
return self.getNodeName()
def _get_Name(self):
return self.getName()
def _get_NodeValue(self):
return self.getNodeValue()
def _get_NodeType(self):
return self.getNodeType()
def _get_Specified(self):
return self.getSpecified()

View File

@ -0,0 +1,116 @@
Using Structured Text
The goal of StructuredText is to make it possible to express
structured text using a relatively simple plain text format. Simple
structures, like bullets or headings are indicated through
conventions that are natural, for some definition of
"natural". Hierarchical structures are indicated through
indentation. The use of indentation to express hierarchical
structure is inspired by the Python programming language.
Use of StructuredText consists of one to three logical steps. In the
first step, a text string is converted to a network of objects using
the 'StructuredText.Basic' facility, as in the following
example::
raw=open("mydocument.txt").read()
import StructuredText
st=StructuredText.Basic(raw)
The output of 'StructuredText.Basic' is simply a
StructuredTextDocumemt object containing StructuredTextParagraph
objects arranged in a hierarchy. Paragraphs are delimited by strings
of two or more whitespace characters beginning and ending with
newline characters. Hierarchy is indicated by indentation. The
indentation of a paragraph is the minimum number of leading spaces
in a line containing non-white-space characters after converting tab
characters to spaces (assuming a tab stop every eight characters).
StructuredTextNode objects support the read-only subset of the
Document Object Model (DOM) API. It should be possible to process
'StructuredTextNode' hierarchies using XML tools such as XSLT.
The second step in using StructuredText is to apply additional
structuring rules based on text content. A variety of differentText
rules can be used. Typically, these are used to implement a
structured text language for producing documents, but any sort of
structured text language could be implemented in the second
step. For example, it is possible to use StructuredText to implement
structured text formats for representing structured data. The second
step, which could consist of multiple processing steps, is
performed by processing, or "coloring", the hierarchy of generic
StructuredTextParagraph objects into a network of more specialized
objects. Typically, the objects produced should also implement the DOM
API to allow processing with XML tools.
A document processor is provided to convert a StructuredTextDocument
object containing only StructuredStructuredTextParagraph objects
into a StructuredTextDocument object containing a richer collection
of objects such as bullets, headings, emphasis, and so on using
hints in the text. Hints are selected based on conventions of the
sort typically seen in electronic mail or news-group postings. It
should be noted, however, that these conventions are somewhat
culturally dependent, fortunately, the document processor is easily
customized to implement alternative rules. Here's an example of
using the DOC processor to convert the output of the previous example::
doc=StructuredText.Document(st)
The final step is to process the colored networks produced from the
second step to produce additional outputs. The final step could be
performed by Python programs, or by XML tools. A Python outputter is
provided for the document processor output that produces Hypertext Markup
Language (HTML) text::
html=StructuredText.HTML(doc)
Customizing the document processor
The document processor is driven by two tables. The first table,
named 'paragraph_types', is a sequence of callable objects or method
names for coloring paragraphs. If a table entry is a string, then it
is the name of a method of the document processor to be used. For
each input paragraph, the objects in the table are called until one
returns a value (not 'None'). The value returned replaces the
original input paragraph in the output. If none of the objects in
the paragraph types table return a value, then a copy of the
original paragraph is used. The new object returned by calling a
paragraph type should implement the ReadOnlyDOM,
StructuredTextColorizable, and StructuredTextSubparagraphContainer
interfaces. See the 'Document.py' source file for examples.
A paragraph type may return a list or tuple of replacement
paragraphs, this allowing a paragraph to be split into multiple
paragraphs.
The second table, 'text_types', is a sequence of callable objects or
method names for coloring text. The callable objects in this table
are used in sequence to transform the input text into new text or
objects. The callable objects are passed a string and return
nothing ('None') or a three-element tuple consisting of:
- a replacement object,
- a starting position, and
- an ending position
The text from the starting position is (logically) replaced with the
replacement object. The replacement object is typically an object
that implements that implements the ReadOnlyDOM, and
StructuredTextColorizable interfaces. The replacement object can
also be a string or a list of strings or objects. Replacement is
done from beginning to end and text after the replacement ending
position will be passed to the character type objects for processing.
Example: adding wiki links
We want to add support for Wiki links. A Wiki link is a string of
text containing mixed-case letters, such that at least two of the
letters are upper case and such that the first letter is upper case.

View File

@ -0,0 +1,833 @@
#! /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 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):
text = gsub(
'[\0\n].. \[\([-_0-9_a-zA-Z-]+\)\]',
'\n <a name="\\1">[\\1]</a>',
text)
text = gsub(
'\([\0- ,]\)\[\([0-9_a-zA-Z-]+\)\]\([\0- ,.:]\)',
'\\1<a href="#\\2">[\\2]</a>\\3',
text)
text = gsub(
'\([\0- ,]\)\[\([^]]+\)\.html\]\([\0- ,.:]\)',
'\\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:],'tw')
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 s[:2]=='#!':
s=ts_regex.sub('^#![^\n]+','',s)
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

@ -0,0 +1,158 @@
#!/usr/bin/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.
#
##############################################################################
from Html import HTML
from string import split
from ST import DOC
import re
"""
This is the new structured text type.
"""
class Zwiki_Title:
def __init__(self,str=''):
self.expr1 = re.compile('([A-Z]+[A-Z]+[a-zA-Z]*)').search
self.expr2 = re.compile('([A-Z]+[a-z]+[A-Z]+[a-zA-Z]*)').search
self.str = [str]
self.typ = "Zwiki_Title"
def type(self):
return '%s' % self.typ
def string(self):
return self.str
def __getitem__(self,index):
return self.str[index]
def __call__(self,raw_string,subs):
"""
The raw_string is checked to see if it matches the rules
for this structured text expression. If the raw_string does,
it is parsed for the sub-string which matches and a doc_inner_link
instance is returned whose string is the matching substring.
If raw_string does not match, nothing is returned.
"""
if self.expr1(raw_string):
start,end = self.expr1(raw_string).span()
result = Zwiki_Title(raw_string[start:end])
result.start,result.end = self.expr1(raw_string).span()
return result
elif self.expr2(raw_string):
start,end = self.expr2(raw_string).span()
result = Zwiki_Title(raw_string[start:end])
result.start,result.end = self.expr2(raw_string).span()
return result
else:
return None
def span(self):
return self.start,self.end
class Zwiki_doc(DOC):
def __init__(self):
DOC.__init__(self)
"""
Add the new type to self.types
"""
self.types.append(Zwiki_Title())
class Zwiki_parser(HTML):
def __init__(self):
HTML.__init__(self)
self.types["Zwiki_Title"] = self.zwiki_title
def zwiki_title(self,object):
result = ""
for x in object.string():
result = result + x
result = "<a href=%s>%s</a>" % (result,result)
#result = "<dtml-wikiname %s>" % result
self.string = self.string + result

View File

@ -0,0 +1,112 @@
##############################################################################
#
# 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.
#
##############################################################################
import HTMLClass, DocumentClass
import ClassicDocumentClass
from StructuredText import html_with_references, HTML
from ST import Basic
import DocBookClass
import HTMLWithImages
import DocumentWithImages
ClassicHTML=HTML
HTMLNG=HTMLClass.HTMLClass()
def HTML(src, level=0, type=type, StringType=type('')):
if type(src) is StringType:
return ClassicHTML(src, level)
return HTMLNG(src, level)
Classic=ClassicDocumentClass.DocumentClass()
Document=DocumentClass.DocumentClass()
DocumentWithImages=DocumentWithImages.DocumentWithImages()
HTMLWithImages=HTMLWithImages.HTMLWithImages()
DocBookBook=DocBookClass.DocBookBook
DocBookChapter=DocBookClass.DocBookChapter()
DocBookChapterWithFigures=DocBookClass.DocBookChapterWithFigures()
DocBookArticle=DocBookClass.DocBookArticle()

View File

@ -0,0 +1,215 @@
##############################################################################
#
# 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

@ -0,0 +1,201 @@
#!/usr/bin/env python
#----------------------------------------------------------------------
import sys, os
import StructuredText
from wxPython.wx import *
USE_WXHTML = 1
if not USE_WXHTML:
try: # try to load the IE ActiveX control
from wxPython.lib.activexwrapper import MakeActiveXClass
import win32com.client.gencache
browserModule = win32com.client.gencache.EnsureModule(
"{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}", 0, 1, 1)
except:
USE_WXHTML = 1
if not USE_WXHTML:
BrowserClass = MakeActiveXClass(browserModule.WebBrowser)
class MyHtmlWindow(BrowserClass):
def SetPage(self, html):
import tempfile
filename = tempfile.mktemp('.html')
f = open(filename, 'w')
f.write(html)
f.close()
self.Navigate(os.path.abspath(filename))
self.filename = filename
def OnDocumentComplete(self, pDisp=None, URL=None):
os.unlink(self.filename)
else:
from wxPython.html import *
MyHtmlWindow = wxHtmlWindow
class StxFrame(wxFrame):
title = "StxViewer"
def __init__(self, stxFile):
wxFrame.__init__(self, None, -1, self.title, size=(650, 700),
style=wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE)
##self.CreateStatusBar()
menu = wxMenu()
menu.Append(10, "&Open\tCtrl-O", "Open a Structured Text file")
EVT_MENU(self, 10, self.OnOpen)
menu.Append(20, "&Close", "Close the current file")
EVT_MENU(self, 20, self.OnClose)
menu.Append(30, "&Save\tCtrl-S", "Save the current file")
EVT_MENU(self, 30, self.OnSave)
menu.Append(40, "Save &as", "Save the current file to a new name")
EVT_MENU(self, 40, self.OnSaveAs)
menu.Append(45, "Save as &html", "Save the current file as HTML")
EVT_MENU(self, 45, self.OnSaveAsHTML)
menu.AppendSeparator()
menu.Append(50, "&Refresh\tCtrl-R", "Reload the file from disk")
EVT_MENU(self, 50, self.OnRefresh)
menu.AppendSeparator()
menu.Append(60, "E&xit\tCtrl-X", "Close the application")
EVT_MENU(self, 60, self.OnExit)
menuBar = wxMenuBar()
menuBar.Append(menu, "&File")
self.SetMenuBar(menuBar)
nb = wxNotebook(self, -1)
EVT_NOTEBOOK_PAGE_CHANGED(self, -1, self.OnPageChanged)
self.htmlWin = MyHtmlWindow(nb, -1)
nb.AddPage(self.htmlWin, "View")
self.editWin = wxTextCtrl(nb, -1, "", style=wxTE_MULTILINE)
self.editWin.SetFont(wxFont(10, wxTELETYPE, wxNORMAL, wxNORMAL))
nb.AddPage(self.editWin, "Edit")
self.viewHtml = wxTextCtrl(nb, -1, "", style=wxTE_MULTILINE|wxTE_READONLY)
self.viewHtml.SetFont(wxFont(10, wxTELETYPE, wxNORMAL, wxNORMAL))
nb.AddPage(self.viewHtml, "HTML")
self.LoadStxFile(stxFile)
def LoadStxFile(self, stxFile):
self.file = stxFile
if stxFile is not None:
text = open(stxFile).read()
self.SetTitle(self.title + ': ' + stxFile)
else:
text = ""
self.SetTitle(self.title)
self.LoadStxText(text)
def LoadStxText(self, text):
# Old ST
html = str(StructuredText.html_with_references(text))
# NG Version
#st = StructuredText.Basic(text)
#doc = StructuredText.Document(st)
#html = StructuredText.HTML(doc)
self.htmlWin.SetPage(html)
self.editWin.SetValue(text)
self.viewHtml.SetValue(html)
self.html = html
def OnPageChanged(self, evt):
if evt.GetOldSelection() == 1: # if it was on the edit page
text = self.editWin.GetValue()
self.LoadStxText(text)
def OnOpen(self, evt):
dlg = wxFileDialog(self, defaultDir=os.getcwd(),
wildcard = "STX files (*.stx)|*.stx|"
"Text files (*.txt)|*.txt|"
"All files (*.*)|*.*",
style=wxOPEN)
if dlg.ShowModal() == wxID_OK:
self.LoadStxFile(dlg.GetPath())
dlg.Destroy()
def OnClose(self, evt):
self.LoadStxFile(None)
def OnSave(self, evt):
if not self.file:
self.OnSaveAs(evt)
else:
text = self.editWin.GetValue()
open(self.file, 'w').write(text)
self.LoadStxFile(self.file)
def OnSaveAs(self, evt):
dlg = wxFileDialog(self, "Save as...", defaultDir=os.getcwd(),
wildcard = "STX files (*.stx)|*.stx|"
"Text files (*.txt)|*.txt|"
"All files (*.*)|*.*",
style=wxSAVE)
if dlg.ShowModal() == wxID_OK:
file = dlg.GetPath()
text = self.editWin.GetValue()
open(file, 'w').write(text)
self.LoadStxFile(file)
dlg.Destroy()
def OnSaveAsHTML(self, evt):
dlg = wxFileDialog(self, "Save as...", defaultDir=os.getcwd(),
wildcard = "HTML files (*.html)|*.html|"
"All files (*.*)|*.*",
style=wxSAVE)
if dlg.ShowModal() == wxID_OK:
file = dlg.GetPath()
text = self.editWin.GetValue()
self.LoadStxText(text)
open(file, 'w').write(self.html)
dlg.Destroy()
def OnRefresh(self, evt):
self.LoadStxFile(self.file)
def OnExit(self, evt):
self.Close(true)
app = wxPySimpleApp()
wxInitAllImageHandlers()
if len(sys.argv) > 1:
filename = sys.argv[1]
else:
filename = None
frame = StxFrame(filename)
frame.Show(true)
app.MainLoop()

View File

@ -0,0 +1,127 @@
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. If the sequence is
made up of lower-case i's and v's, a lower-case roman-numeral list is
generated. If the sequence is made up of upper-case I's and V's, an
upper-case roman-numeral list is generated. If the sequence is made
up of other lower case letters (typically a,b,c) a lowercase alphabetic
list is generated. If the sequence is made of of other upper case
letters (typically, A,B,C) an uppercase alphabetic list is generated.
If the sequence is something else (typically, 1,2,3), a arabic-numeral
list is generated.
- 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>

View File

@ -0,0 +1,3 @@
This sample comes from an IBM developerWorks article at
http://www-106.ibm.com/developerworks/library/l-wxpy/index.html

View File

@ -0,0 +1,286 @@
#!/bin/python
import sys, os
from wxPython.wx import *
from string import *
# Process the command line. Not much to do;
# just get the name of the project file if it's given. Simple.
projfile = 'Unnamed'
if len(sys.argv) > 1:
projfile = sys.argv[1]
def MsgBox (window, string):
dlg=wxMessageDialog(window, string, 'wxProject', wxOK)
dlg.ShowModal()
dlg.Destroy()
class main_window(wxFrame):
def __init__(self, parent, id, title):
wxFrame.__init__(self, parent, -1, title, size = (500, 500),
style=wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE)
# ------------------------------------------------------------------------------------
# Set up menu bar for the program.
# ------------------------------------------------------------------------------------
self.mainmenu = wxMenuBar() # Create menu bar.
mainwindow = self
menu=wxMenu() # Make a menu (will be the Project menu)
exitID=wxNewId() # Make a new ID for a menu entry.
menu.Append(exitID, '&Open', 'Open project') # Name the ID by adding it to the menu.
EVT_MENU(self, exitID, self.OnProjectOpen) # Create and assign a menu event.
exitID=wxNewId()
menu.Append(exitID, '&New', 'New project')
EVT_MENU(self, exitID, self.OnProjectNew)
exitID=wxNewId()
menu.Append(exitID, 'E&xit', 'Exit program')
EVT_MENU(self, exitID, self.OnProjectExit)
self.mainmenu.Append (menu, '&Project') # Add the project menu to the menu bar.
menu=wxMenu() # Make a menu (will be the File menu)
exitID=wxNewId()
menu.Append(exitID, '&Add', 'Add file to project')
EVT_MENU(self, exitID, self.OnFileAdd)
exitID=wxNewId()
menu.Append(exitID, '&Remove', 'Remove file from project')
EVT_MENU(self, exitID, self.OnFileRemove)
exitID=wxNewId()
menu.Append(exitID, '&Open', 'Open file for editing')
EVT_MENU(self, exitID, self.OnFileOpen)
exitID=wxNewId()
menu.Append(exitID, '&Save', 'Save file')
EVT_MENU(self, exitID, self.OnFileSave)
self.mainmenu.Append (menu, '&File') # Add the file menu to the menu bar.
self.SetMenuBar (self.mainmenu) # Attach the menu bar to the window.
# ------------------------------------------------------------------------------------
# Create the splitter window.
# ------------------------------------------------------------------------------------
splitter = wxSplitterWindow (self, -1, style=wxNO_3D|wxSP_3D)
splitter.SetMinimumPaneSize (1)
# ------------------------------------------------------------------------------------
# Create the tree on the left.
# ------------------------------------------------------------------------------------
tID = wxNewId()
self.tree = wxTreeCtrl (splitter, tID, style=wxTR_HAS_BUTTONS |
wxTR_EDIT_LABELS |
wxTR_HAS_VARIABLE_ROW_HEIGHT)
EVT_TREE_BEGIN_LABEL_EDIT(self.tree, tID, self.OnTreeLabelEdit)
EVT_TREE_END_LABEL_EDIT(self.tree, tID, self.OnTreeLabelEditEnd)
EVT_TREE_ITEM_ACTIVATED(self.tree, tID, self.OnTreeItemActivated)
# ------------------------------------------------------------------------------------
# Create the editor on the right.
# ------------------------------------------------------------------------------------
self.editor = wxTextCtrl(splitter, -1, style=wxTE_MULTILINE)
self.editor.Enable (0)
# ------------------------------------------------------------------------------------
# Install the tree and the editor.
# ------------------------------------------------------------------------------------
splitter.SplitVertically (self.tree, self.editor)
splitter.SetSashPosition (180, true)
self.Show(true)
# Some global state variables.
self.projectdirty = false
# ----------------------------------------------------------------------------------------
# Some nice little handlers.
# ----------------------------------------------------------------------------------------
def project_open(self, project_file):
try:
input = open (project_file, 'r')
self.tree.DeleteAllItems()
self.project_file = project_file
name = replace (input.readline(), "\n", "")
self.SetTitle (name)
self.root = self.tree.AddRoot(name)
self.activeitem = self.root
for line in input.readlines():
self.tree.AppendItem (self.root, replace(line, "\n", ""))
input.close
self.tree.Expand (self.root)
self.editor.Clear()
self.editor.Enable (false)
self.projectdirty = false
except IOError:
pass
def project_save(self):
try:
output = open (self.project_file, 'w+')
output.write (self.tree.GetItemText (self.root) + "\n")
count = self.tree.GetChildrenCount (self.root)
iter = 0
child = ''
for i in range(count):
if i == 0:
(child,iter) = self.tree.GetFirstChild(self.root,iter)
else:
(child,iter) = self.tree.GetNextChild(self.root,iter)
output.write (self.tree.GetItemText(child) + "\n")
output.close()
self.projectdirty = false
except IOError:
dlg_m = wxMessageDialog (self, 'There was an error saving the project file.',
'Error!', wxOK)
dlg_m.ShowModal()
dlg_m.Destroy()
# ----------------------------------------------------------------------------------------
# Event handlers from here on out.
# ----------------------------------------------------------------------------------------
def OnProjectOpen(self, event):
open_it = true
if self.projectdirty:
dlg=wxMessageDialog(self, 'The project has been changed. Save?', 'wxProject',
wxYES_NO | wxCANCEL)
result = dlg.ShowModal()
if result == wxID_YES:
self.project_save()
if result == wxID_CANCEL:
open_it = false
dlg.Destroy()
if open_it:
dlg = wxFileDialog(self, "Choose a project to open", ".", "", "*.wxp", wxOPEN)
if dlg.ShowModal() == wxID_OK:
self.project_open(dlg.GetPath())
dlg.Destroy()
def OnProjectNew(self, event):
open_it = true
if self.projectdirty:
dlg=wxMessageDialog(self, 'The project has been changed. Save?', 'wxProject',
wxYES_NO | wxCANCEL)
result = dlg.ShowModal()
if result == wxID_YES:
self.project_save()
if result == wxID_CANCEL:
open_it = false
dlg.Destroy()
if open_it:
dlg = wxTextEntryDialog (self, "Name for new project:", "New Project",
"New project", wxOK | wxCANCEL)
if dlg.ShowModal() == wxID_OK:
newproj = dlg.GetValue()
dlg.Destroy()
dlg = wxFileDialog (self, "Place to store new project", ".", "", "*.wxp",
wxSAVE)
if dlg.ShowModal() == wxID_OK:
try:
proj = open (dlg.GetPath(), 'w')
proj.write (newproj + "\n")
proj.close()
self.project_open (dlg.GetPath())
except IOError:
dlg_m = wxMessageDialog (self,
'There was an error saving the new project file.',
'Error!', wxOK)
dlg_m.ShowModal()
dlg_m.Destroy()
dlg.Destroy()
def OnProjectExit(self, event):
close = true
if self.projectdirty:
dlg=wxMessageDialog(self, 'The project has been changed. Save?', 'wxProject',
wxYES_NO | wxCANCEL)
result = dlg.ShowModal()
if result == wxID_YES:
self.project_save()
if result == wxID_CANCEL:
close = false
dlg.Destroy()
if close:
self.Close()
def OnFileAdd(self, event):
dlg = wxFileDialog (self, "Choose a file to add", ".", "", "*.*", wxOPEN)
if dlg.ShowModal() == wxID_OK:
path = os.path.split(dlg.GetPath())
self.tree.AppendItem (self.root, path[1])
self.tree.Expand (self.root)
self.project_save()
def OnFileRemove(self, event):
item = self.tree.GetSelection()
if item != self.root:
self.tree.Delete (item)
self.project_save()
def OnFileOpen(self, event):
item = self.tree.GetSelection()
def OnFileSave(self, event):
if self.activeitem != self.root:
self.editor.SaveFile (self.tree.GetItemText (self.activeitem))
def OnTreeLabelEdit(self, event):
item=event.GetItem()
if item != self.root:
event.Veto()
def OnTreeLabelEditEnd(self, event):
self.projectdirty = true
def OnTreeItemActivated(self, event):
go_ahead = true
if self.activeitem != self.root:
if self.editor.IsModified():
dlg=wxMessageDialog(self, 'The edited file has changed. Save it?',
'wxProject', wxYES_NO | wxCANCEL)
result = dlg.ShowModal()
if result == wxID_YES:
self.editor.SaveFile (self.tree.GetItemText (self.activeitem))
if result == wxID_CANCEL:
go_ahead = false
dlg.Destroy()
if go_ahead:
self.tree.SetItemBold (self.activeitem, 0)
if go_ahead:
item=event.GetItem()
self.activeitem = item
if item != self.root:
self.tree.SetItemBold (item, 1)
self.editor.Enable (1)
self.editor.LoadFile (self.tree.GetItemText(item))
self.editor.SetInsertionPoint (0)
self.editor.SetFocus()
else:
self.editor.Clear()
self.editor.Enable (0)
class App(wxApp):
def OnInit(self):
frame = main_window(None, -1, "wxProject - " + projfile)
self.SetTopWindow(frame)
if (projfile != 'Unnamed'):
frame.project_open (projfile)
return true
app = App(0)
app.MainLoop()