diff -ur moin-1.5.0/MoinMoin/formatter/base.py moin-1.5.0-formatter-patch/MoinMoin/formatter/base.py
--- moin-1.5.0/MoinMoin/formatter/base.py	2005-11-18 14:16:35.000000000 -0500
+++ moin-1.5.0-formatter-patch/MoinMoin/formatter/base.py	2006-01-16 12:08:36.000000000 -0500
@@ -135,23 +135,20 @@
     def line_anchordef(self, lineno):
         return ""
 
-    def anchorlink(self, on, name='', id=None):
+    def anchorlink(self, on, name='', **kw):
         return ""
 
     def line_anchorlink(self, on, lineno=0):
         return ""
 
-    def image(self, **kw):
+    def image(self, src=None, **kw):
         """ Take HTML <IMG> tag attributes in `attr`.
 
         Attribute names have to be lowercase!
         """
-        attrstr = u''
-        for attr, value in kw.items():
-            if attr=='html_class':
-                attr='class'
-            attrstr = attrstr + u' %s="%s"' % (attr, wikiutil.escape(value))
-        return u'<img%s>' % attrstr
+        if src:
+            return '[Image:%s]' % src
+        return '[Image]'
 
     def smiley(self, text):
         return text
@@ -161,7 +158,7 @@
 
     # Text and Text Attributes ########################################### 
     
-    def text(self, text):
+    def text(self, text, **kw):
         if not self._highlight_re:
             return self._text(text)
             
@@ -185,37 +182,37 @@
     def _text(self, text):
         raise NotImplementedError
 
-    def strong(self, on):
+    def strong(self, on, **kw):
         raise NotImplementedError
 
-    def emphasis(self, on):
+    def emphasis(self, on, **kw):
         raise NotImplementedError
 
-    def underline(self, on):
+    def underline(self, on, **kw):
         raise NotImplementedError
 
-    def highlight(self, on):
+    def highlight(self, on, **kw):
         raise NotImplementedError
 
-    def sup(self, on):
+    def sup(self, on, **kw):
         raise NotImplementedError
 
-    def sub(self, on):
+    def sub(self, on, **kw):
         raise NotImplementedError
 
-    def strike(self, on):
+    def strike(self, on, **kw):
         raise NotImplementedError
 
     def code(self, on, **kw):
         raise NotImplementedError
 
-    def preformatted(self, on):
+    def preformatted(self, on, **kw):
         self.in_pre = on != 0
 
-    def small(self, on):
+    def small(self, on, **kw):
         raise NotImplementedError
 
-    def big(self, on):
+    def big(self, on, **kw):
         raise NotImplementedError
 
     # special markup for syntax highlighting #############################
@@ -234,10 +231,10 @@
     def linebreak(self, preformatted=1):
         raise NotImplementedError
 
-    def paragraph(self, on):
+    def paragraph(self, on, **kw):
         self.in_p = (on != 0)
 
-    def rule(self, size=0):
+    def rule(self, size=0, **kw):
         raise NotImplementedError
 
     def icon(self, type):
@@ -245,22 +242,22 @@
 
     # Lists ##############################################################
 
-    def number_list(self, on, type=None, start=None):
+    def number_list(self, on, type=None, start=None, **kw):
         raise NotImplementedError
 
-    def bullet_list(self, on):
+    def bullet_list(self, on, **kw):
         raise NotImplementedError
 
     def listitem(self, on, **kw):
         raise NotImplementedError
 
-    def definition_list(self, on):
+    def definition_list(self, on, **kw):
         raise NotImplementedError
 
-    def definition_term(self, on, compact=0):
+    def definition_term(self, on, compact=0, **kw):
         raise NotImplementedError
 
-    def definition_desc(self, on):
+    def definition_desc(self, on, **kw):
         raise NotImplementedError
 
     def heading(self, on, depth, **kw):
@@ -268,13 +265,13 @@
 
     # Tables #############################################################
     
-    def table(self, on, attrs={}):
+    def table(self, on, attrs={}, **kw):
         raise NotImplementedError
 
-    def table_row(self, on, attrs={}):
+    def table_row(self, on, attrs={}, **kw):
         raise NotImplementedError
 
-    def table_cell(self, on, attrs={}):
+    def table_cell(self, on, attrs={}, **kw):
         raise NotImplementedError
 
     # Dynamic stuff / Plugins ############################################
@@ -342,7 +339,7 @@
 
         return self.text(f.getvalue())
 
-    def escapedText(self, on):
+    def escapedText(self, on, **kw):
         """ This allows emitting text as-is, anything special will
             be escaped (at least in HTML, some text output format
             would possibly do nothing here)
diff -ur moin-1.5.0/MoinMoin/formatter/dom_xml.py moin-1.5.0-formatter-patch/MoinMoin/formatter/dom_xml.py
--- moin-1.5.0/MoinMoin/formatter/dom_xml.py	2005-11-18 14:16:35.000000000 -0500
+++ moin-1.5.0-formatter-patch/MoinMoin/formatter/dom_xml.py	2006-01-16 12:09:17.000000000 -0500
@@ -253,7 +253,7 @@
         kw['type'] = 'inline'
         return self._add_tag('attachment', **kw)
 
-    def rule(self, size=0):
+    def rule(self, size=0, **kw):
         return self._add_tag('hr', size=str(size))
 
     def icon(self, type):
@@ -262,41 +262,41 @@
     def smiley(self, type):
         return self._add_tag('smiley', type=type)
 
-    def strong(self, on):
+    def strong(self, on, **kw):
         return self._set_tag('b', on)
 
-    def emphasis(self, on):
+    def emphasis(self, on, **kw):
         return self._set_tag('em', on)
 
-    def highlight(self, on):
+    def highlight(self, on, **kw):
         return self._set_tag('highlight', on)
 
-    def number_list(self, on, type=None, start=None):
+    def number_list(self, on, type=None, start=None, **kw):
         return self._set_tag('ol', on, type=type, start=start)
 
-    def bullet_list(self, on):
+    def bullet_list(self, on, **kw):
         return self._set_tag('ul', on)
 
     def listitem(self, on, **kw):
         return self._set_tag('li', on)
 
-    def sup(self, on):
+    def sup(self, on, **kw):
         return self._set_tag('sup', on)
 
-    def sub(self, on):
+    def sub(self, on, **kw):
         return self._set_tag('sub', on)
 
-    def strike(self, on):
+    def strike(self, on, **kw):
         return self._set_tag('strike', on)
 
     def code(self, on, **kw):
         return self._set_tag('code', on)
 
-    def preformatted(self, on):
+    def preformatted(self, on, **kw):
         self.in_pre = on != 0
         return self._set_tag('pre', on)
 
-    def paragraph(self, on):
+    def paragraph(self, on, **kw):
         FormatterBase.paragraph(self, on)
         return self._set_tag('p', on)
 
@@ -315,31 +315,28 @@
             result[str(name)] = value
         return result
 
-    def table(self, on, attrs={}):
+    def table(self, on, attrs={}, **kw):
         return self._set_tag('table', on, **self._check_attrs(attrs))
         
-    def table_row(self, on, attrs={}):
+    def table_row(self, on, attrs={}, **kw):
         return self._set_tag('tr', on, **self._check_attrs(attrs))
 
-    def table_cell(self, on, attrs={}):
+    def table_cell(self, on, attrs={}, **kw):
         return self._set_tag('td', on, **self._check_attrs(attrs))
 
     def anchordef(self, name):
         return self._add_tag('anchor', name=name)
 
-    def anchorlink(self, on, name, id=None):
-        kw = {}
-        if id:
-            kw['id'] = str(id)
+    def anchorlink(self, on, name, **kw):
         return self.url(on, "#" + name, **kw)
 
-    def underline(self, on):
+    def underline(self, on, **kw):
         return self._set_tag('u', on)
 
-    def definition_list(self, on):
+    def definition_list(self, on, **kw):
         return self._set_tag('dl', on)
 
-    def definition_term(self, on, compact=0):
+    def definition_term(self, on, compact=0, **kw):
         # XXX may be not correct
         # self._langAttr() missing
         if compact and on:
@@ -347,24 +344,26 @@
         else:
             return self._set_tag('dt', on)            
 
-    def definition_desc(self, on):
+    def definition_desc(self, on, **kw):
         # self._langAttr() missing
         return self._set_tag('dd', on)
 
-    def image(self, **kw):
+    def image(self, src=None, **kw):
         """ Take HTML <IMG> tag attributes in `attr`.
 
             Attribute names have to be lowercase!
         """
+        if src:
+            kw['src']=src
         return self._add_tag('img', **kw)
 
-    def escapedText(self, text):
+    def escapedText(self, text, **kw):
         return wikiutil.escape(text)
 
-    def small(self, on):
+    def small(self, on, **kw):
         return self._set_tag('small', on)
 
-    def big(self, on):
+    def big(self, on, **kw):
         return self._set_tag('big', on)
 
     def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
diff -ur moin-1.5.0/MoinMoin/formatter/text_gedit.py moin-1.5.0-formatter-patch/MoinMoin/formatter/text_gedit.py
--- moin-1.5.0/MoinMoin/formatter/text_gedit.py	2005-12-04 07:37:54.000000000 -0500
+++ moin-1.5.0-formatter-patch/MoinMoin/formatter/text_gedit.py	2006-01-16 12:06:31.000000000 -0500
@@ -140,13 +140,13 @@
 
     # Change nesting: sub lists are no longer within the <li> tags
     
-    def number_list(self, on, type=None, start=None):
+    def number_list(self, on, type=None, start=None, **kw):
         li = ""
         if self._in_li: # close <li>
             li = self.listitem(False)
         return li + text_html.Formatter.number_list(self, on, type, start)
 
-    def bullet_list(self, on):
+    def bullet_list(self, on, **kw):
         li = ""
         if self._in_li: # close <li>
             li = self.listitem(False)
@@ -197,7 +197,7 @@
         attrs = text_html.Formatter._checkTableAttr(self, attrs, prefix)
         return self._style_to_attributes(attrs)
 
-    def table(self, on, attrs=None):
+    def table(self, on, attrs=None, **kw):
         """ Create table
 
         @param on: start table
@@ -221,29 +221,31 @@
 
         return ''.join(result)    
 
-    def comment(self, text):
+    def comment(self, text, **kw):
         text = text.rstrip() # workaround for growing amount of blanks at EOL
         return self.preformatted(1, attr={'class': 'comment'}) + text + self.preformatted(0)
 
-    def underline(self, on):
+    def underline(self, on, **kw):
         tag = 'u'
         if on:
             return self.open(tag)
         return self.close(tag)
                     
-    def strong(self, on):
+    def strong(self, on, **kw):
         tag = 'b'
         if on:
             return self.open(tag)
         return self.close(tag)
 
-    def emphasis(self, on):
+    def emphasis(self, on, **kw):
         tag = 'i'
         if on:
             return self.open(tag)
         return self.close(tag)
 
-    def code(self, on, css=None):
+    def code(self, on, css=None, **kw):
+        if not css and kw.has_key('css_class'):
+            css=kw['css_class']
         tag = 'tt'
         # Maybe we don't need this, because we have tt will be in inlineStack.
         self._in_code = on
diff -ur moin-1.5.0/MoinMoin/formatter/text_html.py moin-1.5.0-formatter-patch/MoinMoin/formatter/text_html.py
--- moin-1.5.0/MoinMoin/formatter/text_html.py	2005-12-04 07:37:54.000000000 -0500
+++ moin-1.5.0-formatter-patch/MoinMoin/formatter/text_html.py	2006-01-16 15:04:44.000000000 -0500
@@ -11,6 +11,103 @@
 from MoinMoin.Page import Page
 from MoinMoin.action import AttachFile
 
+def repair_attribute_name( name ):
+    """Takes an attribute name and tries to make it HTML correct.
+
+    This function takes an attribute name as a string, as it may be
+    passed in to a formatting method using a keyword-argument syntax,
+    and tries to convert it into the real HTML attribute name.  This is
+    necessary because some HTML attributes are in fact Python reserved
+    words (like "for"), and also to help with backwards compatibility
+    with older versions of MoinMoin where different names may have
+    been used (like "content_id" or "css").
+
+    Returns a tuple of (namespace, attribute).
+
+    The default namespace is always assumed to be "html", unless the
+    input string contains a colon or a double-underscore (in which
+    case the first such occurance is assumed to separate the namespace
+    prefix from name.  So for example to get the HTML attribute "for"
+    (on a <label> element), you can pass in the string "html__for".
+
+    (In actuality we only deal with namespace prefixes, not any real
+    namespace URI...we only care about the syntax not the meanings)
+    """
+
+    # Handle any namespaces (just in case someday we support XHTML)
+    if ':' in name:
+        ns, name = name.split(':',1)
+    elif '__' in name:
+        ns, name = name.split('__',1)
+    elif name == 'xmlns':
+        ns = ''
+    else:
+        ns = 'html'
+
+    if ns == 'html':
+        # We have an HTML attribute, fix according to DTD
+        if name == 'content_type': # MIME type such as in <a> and <link> elements
+            name =  'type'
+        elif name == 'content_id': # moin historical convention
+            name =  'id'
+        elif name == 'http_equiv': # underscore to hyphen
+            name =  'http-equiv'
+        elif name == 'z_index': # underscore to hyphen
+            name = 'z-index'
+        elif name in ('css_class','css'): # to avoid python word 'class'
+            name = 'class'
+        elif name.startswith('on'): # event handler hook
+            name = name.lower()
+    return (ns, name)
+
+
+# These are standard HTML attributes which are typically used without any
+# value; e.g., as boolean flags indicated by their presence.
+
+html_attribute_flags = ['compact','disabled','ismap','nohref','noresize','noshade',
+                        'nowrap','readonly','selected','wrap']
+
+
+def extend_attribute_dictionary( attributedict, ns, name, value ):
+    """Add a new attribute to an attribute dictionary, merging values where possible.
+
+    The attributedict must be a dictionary with tuple-keys of the form:
+    (namespace, attrname).
+
+    The given ns, name, and value will be added to the dictionary.  It
+    will replace the old value if it existed, except for a few special
+    cases where the values are logically merged instead (CSS class
+    names and style rules).
+
+    As a special case, if value is None (not just ''), then the
+    attribute is actually deleted from the dictionary.
+    """
+
+    key = (ns, name)
+    if attributedict.has_key(key):
+        if value is None:
+            del attributedict[key]
+            return
+        elif ns == 'html':
+            if name == 'class':
+                # CSS classes are appended by space-separated list
+                classlist = attributedict[key]
+                classlist += ' ' + value
+                value = classlist
+            elif name == 'style':
+                # CSS styles are appended by semicolon-separated rules list
+                stylelist = attributedict[key]
+                stylelist += '; ' + value
+                value = stylelist
+            elif name in html_attribute_flags:
+                # All attributes must have a value. According to XHTML those
+                # traditionally used as flags should have their value set to
+                # the same as the attribute name.
+                value = name
+    attributedict[key] = value
+    return
+
+
 class Formatter(FormatterBase):
     """
         Send HTML data.
@@ -67,35 +164,128 @@
                 # lang is inherited from content div
                 return {}
 
-        attr = {'lang': lang, 'dir': i18n.getDirection(lang),}
+        attr = {'xml:lang': lang, 'lang': lang, 'dir': i18n.getDirection(lang),}
         return attr
 
-    def formatAttributes(self, attr=None):
-        """ Return formatted attributes string
+    # These are all the standard HTML attributes that can be on any element.
+    _common_attrs = ['accesskey','class','dir','disabled','id','lang','style','tabindex','title']
+
+    def formatAttributes(self, attr=None, allowed_attrs=None, **kw):
+        """ Return HTML attributes formatted as a single string.
 
         @param attr: dict containing keys and values
-        @rtype: string ?
+        @param allowed_attrs: A list of allowable attribute names
+        @param **kw: other arbitrary attributes expressed as keyword arguments.
+        @rtype: string
         @return: formated attributes or empty string
+
+        The attributes and their values can either be given in the 'attr'
+        dictionary, or as extra keyword arguments.  They are both
+        merged together.  See the function repair_attribute_name() for
+        special notes on how to name attributes.
+
+        Setting a value to None rather than a string (or string
+        coercible) will remove that attribute from the list.
+        
+        If the list of allowed_attrs is provided, then an error is
+        raised if an HTML attribute is encountered that is not in that
+        list (or is not a common attribute which is always allowed or
+        is not in another XML namespace using the double-underscore
+        syntax).
         """
+
+        # Merge the attr dict and kw dict into a single attributes dictionary
+        # (repairing any attribute names, extracting namespaces, and merging
+        # some values like css classes).
+        attributes = {} # dict of key=(namespace,name): value=attribute_value
         if attr:
-            attr = [' %s="%s"' % (k, v) for k, v in attr.items()]           
-            return ''.join(attr)
+            for a,v in attr.items():
+                a_ns, a_name = repair_attribute_name(a)
+                extend_attribute_dictionary( attributes, a_ns, a_name, v )
+        if kw:
+            for a,v in kw.items():
+                a_ns, a_name = repair_attribute_name(a)
+                extend_attribute_dictionary( attributes, a_ns, a_name, v )
+
+        # Add title attribute if missing, but it has an alt.
+        if attributes.has_key(('html','alt')) and not attributes.has_key(('html','title')):
+            attributes[('html','title')] = attributes[('html','alt')]
+
+        # Force both lang and xml:lang to be present and identical if either exists.
+        # The lang takes precedence over xml:lang if both exist.
+        if attributes.has_key(('html','lang')):
+            attributes[('xml','lang')] = attributes[('html','lang')]
+        elif attributes.has_key(('xml','lang')):
+            attributes[('html','lang')] = attributes[('xml','lang')]
+
+        # Check all the HTML attributes to see if they are known and allowed.  Ignore
+        # attributes if in non-HTML namespaces.
+        if allowed_attrs:
+            for name in [key[1] for key in attributes.keys() if key[0] == 'html']:
+                if name in self._common_attrs or name in allowed_attrs:
+                    pass
+                elif name.startswith('on'):
+                    pass  # Too many event handlers to enumerate, just let them all pass.
+                else:
+                    # Unknown or unallowed attribute.
+                    err = 'Illegal HTML attribute "%s" passed to formatter' % name
+                    raise ValueError(err)
+
+        # Finally, format them all as a single string.
+        if attributes:
+            # Construct a formatted string containing all attributes
+            # with their values escaped.  Any html:* namespace
+            # attributes drop the namespace prefix.  We build this by
+            # separating the attributes into three categories:
+            #
+            #  * Those without any namespace (should only be xmlns attributes)
+            #  * Those in the HTML namespace (we drop the html: prefix for these)
+            #  * Those in any other non-HTML namespace, including xml:
+
+            xmlnslist = ['%s="%s"' % (k[1], wikiutil.escape(v,1))
+                         for k, v in attributes.items() if not k[0]]
+            htmllist = ['%s="%s"' % (k[1], wikiutil.escape(v,1))
+                        for k, v in attributes.items() if k[0] == 'html']
+            otherlist = ['%s:%s="%s"' % (k[0], k[1], wikiutil.escape(v,1))
+                         for k, v in attributes.items() if k[0] and k[0] != 'html']
+
+            # Join all these lists together in a space-separated string.  Also
+            # prefix the whole thing with a space too.
+            htmllist.sort()
+            otherlist.sort()
+            all = [''] + xmlnslist + htmllist + otherlist
+            return ' ' + ' '.join(all)
         return ''
 
     # TODO: use set when we require Python 2.3
     # TODO: The list is not complete, add missing from dtd
-    _blocks = 'p div pre table tr td ol ul dl li dt dd h1 h2 h3 h4 h5 h6 hr form'
+    _blocks = 'p div pre table tbody thead tfoot tr th td ol ul dl li dt dd h1 h2 h3 h4 h5 h6 hr form'
     _blocks = dict(zip(_blocks.split(), [1] * len(_blocks)))
 
-    def open(self, tag, newline=False, attr=None):
+    # These are the HTML elements which are typically only used with
+    # an opening tag without a separate closing tag.  We do not
+    # include 'script' or 'style' because sometimes they do have
+    # content, and also IE has a parsing bug with those two elements (only)
+    # when they don't have a closing tag even if valid XHTML.
+
+    _self_closing_tags = ['base','br','frame','hr','img','input','isindex','link','meta','param']
+
+    def open(self, tag, newline=False, attr=None, allowed_attrs=None, **kw):
         """ Open a tag with optional attributes
         
         @param tag: html tag, string
-        @param newline: render tag on a separate line
-        @parm attr: dict with tag attributes
+        @param newline: render tag so following data is on a separate line
+        @param attr: dict with tag attributes
+        @param allowed_attrs: list of allowed attributes for this element
+        @param kw: arbitrary attributes and values
         @rtype: string ?
-        @return: open tag with attributes
+        @return: open tag with attributes as a string
         """
+        self_close = ''
+        if tag in self._self_closing_tags:
+            # Don't expect a closing tag later on.
+            self_close = ' /'
+
         if tag in self._blocks:
             # Block elements
             result = []
@@ -106,25 +296,33 @@
                 attributes.update(attr)
             
             # Format
-            attributes = self.formatAttributes(attributes)
-            result.append('<%s%s>' % (tag, attributes))
+            attributes = self.formatAttributes(attributes, allowed_attrs=allowed_attrs, **kw)
+            result.append('<%s%s%s>' % (tag, attributes, self_close))
             if newline:
                 result.append('\n')
             return ''.join(result)
         else:
             # Inline elements
             # Add to inlineStack
-            self._inlineStack.append(tag)
+            if not self_close:
+                # Only push on stack if we expect a close-tag later
+                self._inlineStack.append(tag)
             # Format
-            return '<%s%s>' % (tag, self.formatAttributes(attr))
-       
+            return '<%s%s%s>' % (tag,
+                                 self.formatAttributes(attr, allowed_attrs, **kw),
+                                 self_close)
+
     def close(self, tag, newline=False):
         """ Close tag
 
         @param tag: html tag, string
-        @rtype: string ?
-        @return: closing tag
+        @param newline: render tag so following data is on a separate line
+        @rtype: string
+        @return: closing tag as a string
         """
+        if tag in self._self_closing_tags:
+            # This tag was already closed
+            return ''
         if tag in self._blocks:
             # Block elements
             # Close all tags in inline stack
@@ -137,7 +335,9 @@
             # Format with newline
             if newline:
                 result.append('\n')
-            result.append('</%s>\n' % (tag))
+            result.append('</%s>' % (tag))
+            if newline:
+                result.append('\n')
             return ''.join(result)            
         else:
             # Inline elements 
@@ -150,7 +350,7 @@
 
     # Public methods ###################################################
 
-    def startContent(self, content_id='content', **kwargs):
+    def startContent(self, content_id='content', newline=True, **kw):
         """ Start page content div """
 
         # Setup id
@@ -163,11 +363,12 @@
         # Use the content language
         attr = self.langAttr(self.request.content_lang)
         attr['id'] = content_id
-        result.append(self.open('div', newline=1, attr=attr))
+        result.append(self.open('div', newline=newline, attr=attr,
+                                allowed_attrs=['align'], **kw))
         result.append(self.anchordef(aid))
         return ''.join(result)
         
-    def endContent(self):
+    def endContent(self,newline=True):
         """ Close page content div """
 
         # Setup id
@@ -182,7 +383,7 @@
 
         result = []
         result.append(self.anchordef(aid))
-        result.append(self.close('div', newline=1))
+        result.append(self.close('div', newline=newline))
         return ''.join(result) 
 
     def lang(self, on, lang_name):
@@ -205,7 +406,7 @@
     def sysmsg(self, on, **kw):
         tag = 'div'
         if on:
-            return self.open(tag, attr={'class': 'message'})
+            return self.open(tag, attr={'class': 'message'}, **kw)
         return self.close(tag)
     
     # Links ##############################################################
@@ -259,41 +460,96 @@
             # unescaped=1 was changed to 0 to make interwiki links with pages with umlauts (or other non-ascii) work
 
     def url(self, on, url=None, css=None, **kw):
-        """
+        """ Inserts an <a> element.
+
+            Call once with on=1 to start the link, and again with on=0
+            to end it (no other arguments are needed when on==0).
+
             Keyword params:
-                title - <a> title attribute
-                attrs -  just include those <a> attrs "as is"
+                url - the URL to link to; will go through Wiki URL mapping.
+                type - icon type: one of "www" or "mailto" to use that icon
+                css - a space-separated list of CSS classes
+                attrs -  just include this string verbatim inside
+                         the <a> element; can be used for arbitrary attrs
         """
+        if not on:
+            return self.close('a')
+        attrs = self.langAttr()
+
+        # Handle the URL mapping
+        if url is None and kw.has_key('href'):
+            url = kw['href']
+            del kw['href']
         if url is not None:
             url = wikiutil.mapURL(self.request, url)
-        title = kw.get('title', None)
-        attrs = kw.get('attrs', None)
+            attrs['href'] = url
+
+        if css:
+            attrs['class'] = css
+        
+        if kw.has_key('type'): # Icon type
+            icon_type = kw['type']
+            del kw['type']
+        else:
+            icon_type = None
+
+        if kw.has_key('attrs'):
+            # for backwards compatibility, raw pre-formated attribute string
+            extra_attrs = kw['attrs']
+            del kw['attrs']
+        else:
+            extra_attrs = None
+
+        # create link
         if on:
-            str = '<a'
-            if css: 
-                str = '%s class="%s"' % (str, css)
-            if title:
-                str = '%s title="%s"' % (str, title)
-            if attrs:
-                str = '%s %s' % (str, attrs)
-            str = '%s href="%s">' % (str, wikiutil.escape(url, 1))
+            str = self.open('a', attr=attrs, **kw)
+            if extra_attrs:
+                # insert this into the tag (messy)
+                if str[-2:] == '/>':
+                    str = '%s %s />' % (str[:-2], extra_attrs)
+                else:
+                    str = '%s %s>' % (str[:-1], extra_attrs)
         else:
-            str = '</a>'
+            str = self.close('a')
+
+        if icon_type == 'www':
+             str = '%s%s ' % (str, self.icon("www"))
+        elif icon_type == 'mailto':
+             str = '%s%s ' % (str, self.icon('mailto'))
         return str
 
     def anchordef(self, id):
-        #return '<a id="%s"></a>' % (id, ) # this breaks PRE sections for IE
-        # do not add a \n here, it breaks pre sections with line_anchordef
-        return '<span id="%s" class="anchor"></span>' % (id, )
+        """Inserts an element with an id attribute, used as an anchor
+        for link references.  We use <span></span> rather than <span/>
+        for browser portability.
+        """
+        return '<span class="anchor" id="%s"></span>' % wikiutil.escape(id, 1)
 
     def line_anchordef(self, lineno):
         return self.anchordef("line-%d" % lineno)
 
-    def anchorlink(self, on, name='', id=None):
-        extra = ''
-        if id:
-            extra = ' id="%s"' % id
-        return ['<a href="#%s"%s>' % (name, extra), '</a>'][not on]
+    def anchorlink(self, on, name='', **kw):
+        """Inserts an <a> link pointing to an anchor within the same
+        document.  Call once with on=1 to start the link, and a
+        second time with on=0 to end it.  No other arguments are
+        needed on the second call.
+
+        The name argument should be the same as the id provided to the
+        anchordef() method, or some other elment.  The id argument, if
+        provided, is instead the id of this link itself and not of the
+        element the link references.
+        """
+
+        attrs = self.langAttr()
+        if name:
+            attrs['href'] = '#%s' % name
+        if kw.has_key('href'):
+            del kw['href']
+        if on:
+            str = self.open('a', attr=attrs, **kw)
+        else:
+            str = self.close('a')
+        return str
 
     def line_anchorlink(self, on, lineno=0):
         return self.anchorlink(on, name="line-%d" % lineno)
@@ -415,81 +671,138 @@
 
     # Inline ###########################################################
         
-    def strong(self, on):
+    def strong(self, on, **kw):
+        """Creates an HTML <strong> element.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
+
         tag = 'strong'
         if on:
-            return self.open(tag)
+            return self.open(tag, allowed_attrs=[], **kw)
         return self.close(tag)
 
-    def emphasis(self, on):
+    def emphasis(self, on, **kw):
+        """Creates an HTML <em> element.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
+
         tag = 'em'
         if on:
-            return self.open(tag)
+            return self.open(tag, allowed_attrs=[], **kw)
         return self.close(tag)
 
-    def underline(self, on):
+    def underline(self, on, **kw):
+        """Creates a text span for underlining (css class "u").
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'span'
         if on:
-            return self.open(tag, attr={'class': 'u'})
+            return self.open(tag, attr={'class': 'u'}, allowed_attrs=[], **kw)
         return self.close(tag)
 
-    def highlight(self, on):
+    def highlight(self, on, **kw):
+        """Creates a text span for highlighting (css class "highlight").
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'strong'
         if on:
-            return self.open(tag, attr={'class': 'highlight'})
+            return self.open(tag, attr={'class': 'highlight'}, allowed_attrs=[], **kw)
         return self.close(tag)
 
-    def sup(self, on):
+    def sup(self, on, **kw):
+        """Creates a <sup> element for superscript text.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'sup'
         if on:
-            return self.open(tag)
+            return self.open(tag, allowed_attrs=[], **kw)
         return self.close(tag)
 
-    def sub(self, on):
+    def sub(self, on, **kw):
+        """Creates a <sub> element for subscript text.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'sub'
         if on:
-            return self.open(tag)
+            return self.open(tag, allowed_attrs=[], **kw)
         return self.close(tag)
 
-    def strike(self, on):
+    def strike(self, on, **kw):
+        """Creates a <strike> element for line-through (strikeout) text.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'strike'
         if on:
-            return self.open(tag)
+            return self.open(tag, allowed_attrs=[], **kw)
         return self.close(tag)
 
     def code(self, on, **kw):
+        """Creates a <tt> element for inline code text.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'tt'
         # Maybe we don't need this, because we have tt will be in inlineStack.
         self._in_code = on        
         if on:
-            return self.open(tag)
+            return self.open(tag, allowed_attrs=[], **kw)
         return self.close(tag)
         
-    def small(self, on):
+    def small(self, on, **kw):
+        """Creates a <small> element for smaller font.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'small'
         if on:
-            return self.open(tag)
+            return self.open(tag, allowed_attrs=[], **kw)
         return self.close(tag)
 
-    def big(self, on):
+    def big(self, on, **kw):
+        """Creates a <big> element for larger font.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'big'
         if on:
-            return self.open(tag)
+            return self.open(tag, allowed_attrs=[], **kw)
         return self.close(tag)
 
 
     # Block elements ####################################################
 
-    def preformatted(self, on, attr=None):
+    def preformatted(self, on, **kw):
+        """Creates a preformatted text region, with a <pre> element.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         FormatterBase.preformatted(self, on)
         tag = 'pre'
         if on:
-            return self.open(tag, newline=1, attr=attr)
+            return self.open(tag, newline=1, **kw)
         return self.close(tag)
                 
     # Use by code area
     _toggleLineNumbersScript = """
-<script type="text/JavaScript">
+<script type="text/javascript">
 function isnumbered(obj) {
   return obj.childNodes.length && obj.firstChild.childNodes.length && obj.firstChild.firstChild.className == 'LineNumber';
 }
@@ -541,6 +854,19 @@
 """
     
     def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
+        """Creates a formatted code region, with line numbering.
+
+        This region is formatted as a <div> with a <pre> inside it.  The
+        code_id argument is assigned to the 'id' of the div element, and
+        must be unique within the document.  The show, start, and step are
+        used for line numbering.
+
+        Note this is not like most formatter methods, it can not take any
+        extra keyword arguments.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         res = []
         ci = self.request.makeUniqueID('CA-%s_%03d' % (code_id, self._code_area_num))
         if on:
@@ -562,7 +888,7 @@
             if self._code_area_state[1] >= 0:
                 toggleLineNumbersLink = r'''
 <script type="text/javascript">
-document.write('<a href="#" onClick="return togglenumber(\'%s\', %d, %d);" \
+document.write('<a href="#" onclick="return togglenumber(\'%s\', %d, %d);" \
                 class="codenumbers">Toggle line numbers<\/a>');
 </script>
 ''' % (self._code_area_state[0], self._code_area_state[2], self._code_area_state[3])
@@ -603,11 +929,21 @@
     # Paragraphs, Lines, Rules ###########################################
     
     def linebreak(self, preformatted=1):
+        """Creates a line break in the HTML output.
+        
+        If preformatted is true a <br> element is inserted, otherwise
+        the linebreak will only be visible in the HTML source.
+        """
         if self._in_code_area:
             preformatted = 1
-        return ['\n', '<br>\n'][not preformatted]
+        return ['\n', '<br />\n'][not preformatted]
         
-    def paragraph(self, on):
+    def paragraph(self, on, **kw):
+        """Creates a paragraph with a <p> element.
+        
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         if self._terse:
             return ''
         FormatterBase.paragraph(self, on)
@@ -615,14 +951,20 @@
             self._in_li = self._in_li + 1
         tag = 'p'
         if on:
-            return self.open(tag)
-        return self.close(tag)
+            return self.open(tag,**kw)
+        return self.close(tag) + '\n'
             
-    def rule(self, size=None):
-        if size:
+    def rule(self, size=None, **kw):
+        """Creates a horizontal rule with an <hr> element.
+
+        If size is a number in the range [1..6], the CSS class of the rule
+        is set to 'hr1' through 'hr6'.  The intent is that the larger the
+        size number the thicker or bolder the rule will be.
+        """
+        if size and 1 <= size <= 6:
             # Add hr class: hr1 - hr6
-            return self.open('hr', newline=1, attr={'class': 'hr%d' % size})
-        return self.open('hr', newline=1)
+            return self.open('hr', newline=1, attr={'class': 'hr%d' % size}, **kw)
+        return self.open('hr', newline=1, **kw)
                 
     def icon(self, type):
         return self.request.theme.make_icon(type)
@@ -634,9 +976,35 @@
             href = self.request.theme.img_url(img)
         return self.image(src=href, alt=text, width=str(w), height=str(h))
 
+    def image(self, src=None, **kw):
+        """Creates an inline image with an <img> element.
+
+        The src argument must be the URL to the image file.
+        """
+        if src:
+            kw['src']=src
+        return self.open('img',**kw)
+
     # Lists ##############################################################
 
-    def number_list(self, on, type=None, start=None):
+    _list_stack=[]
+    def _indent_list(self):
+        if not self._list_stack:
+            return ''
+        return '\n' + '  '*len(self._list_stack)
+
+    def number_list(self, on, type=None, start=None, **kw):
+        """Creates an HTML ordered list, <ol> element.
+
+        The 'type' if specified can be any legal numbered
+        list-style-type, such as 'decimal','lower-roman', etc.
+
+        The 'start' argument if specified gives the numeric value of
+        the first list item (default is 1).
+
+        Call once with on=1 to start the list, and a second time
+        with on=0 to end it.
+        """
         tag = 'ol'
         if on:
             attr = {}
@@ -644,49 +1012,80 @@
                 attr['type'] = type
             if start is not None:
                 attr['start'] = start
-            return self.open(tag, newline=1, attr=attr)
-        return self.close(tag)
+            tagstr = self.open(tag, newline=0, attr=attr, **kw)
+            self._list_stack.append(tag)
+            return self._indent_list() + tagstr + '\n'
+        else:
+            self._list_stack.pop()
+            tagstr = self.close(tag)
+            return self._indent_list() + tagstr
     
-    def bullet_list(self, on):
+    def bullet_list(self, on, **kw):
+        """Creates an HTML ordered list, <ol> element.
+
+        The 'type' if specified can be any legal unnumbered
+        list-style-type, such as 'disc','square', etc.
+
+        Call once with on=1 to start the list, and a second time
+        with on=0 to end it.
+        """
         tag = 'ul'
         if on:
-            return self.open(tag, newline=1)
-        return self.close(tag)
+            tagstr = self.open(tag, newline=0, **kw)
+            self._list_stack.append(tag)
+            return self._indent_list() + tagstr + '\n'
+        else:
+            self._list_stack.pop()
+            tagstr = self.close(tag)
+            return self._indent_list() + tagstr
            
     def listitem(self, on, **kw):
-        """ List item inherit its lang from the list. """
+        """Adds a list item, <li> element, to a previously opened
+        bullet or number list.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'li'
         self._in_li = on != 0
         if on:
-            attr = {}
-            css_class = kw.get('css_class', None)
-            if css_class:
-                attr['class'] = css_class
-            style = kw.get('style', None)
-            if style:
-                attr['style'] = style
-            return self.open(tag, attr=attr)
-        return self.close(tag)
+            return '  ' + self.open(tag, **kw)
+        return self.close(tag) + '\n'
+
+    def definition_list(self, on, **kw):
+        """Creates an HTML definition list, <dl> element.
 
-    def definition_list(self, on):
+        Call once with on=1 to start the list, and a second time
+        with on=0 to end it.
+        """
         tag = 'dl'
         if on:
-            return self.open(tag, newline=1)
+            return self.open(tag, newline=1, **kw)
         return self.close(tag)
 
-    def definition_term(self, on):
+    def definition_term(self, on, **kw):
+        """Adds a new term to a definition list, HTML element <dt>.
+
+        Call once with on=1 to start the term, and a second time
+        with on=0 to end it.
+        """
         tag = 'dt'
         if on:
-            return self.open(tag)
-        return self.close(tag)
+            return self.open(tag, **kw)
+        return self.close(tag) + '\n'
         
-    def definition_desc(self, on):
+    def definition_desc(self, on, **kw):
+        """Gives the definition to a definition item, HTML element <dd>.
+
+        Call once with on=1 to start the definition, and a second time
+        with on=0 to end it.
+        """
         tag = 'dd'
         if on:
-            return self.open(tag)
-        return self.close(tag)
+            return '  ' + self.open(tag, **kw)
+        return self.close(tag) + '\n'
 
-    def heading(self, on, depth, id = None, **kw):
+    def heading(self, on, depth, **kw):
         # remember depth of first heading, and adapt counting depth accordingly
         if not self._base_depth:
             self._base_depth = depth
@@ -722,11 +1121,8 @@
             number = '.'.join(map(str, self.request._fmt_hd_counters[self._show_section_numbers-1:]))
             if number: number += ". "
 
-        attr = {}
-        if id:
-            attr['id'] = id
         # Add space before heading, easier to check source code
-        result = '\n' + self.open('h%d' % heading_depth, attr=attr)
+        result = '\n' + self.open('h%d' % heading_depth, **kw)
 
         # TODO: convert this to readable code
         if self.request.user.show_topbottom:
@@ -795,7 +1191,7 @@
         return result
 
 
-    def table(self, on, attrs=None):
+    def table(self, on, attrs=None, **kw):
         """ Create table
 
         @param on: start table
@@ -814,36 +1210,53 @@
                 attrs = {}
             else:
                 attrs = self._checkTableAttr(attrs, 'table')
-            result.append(self.open('table', newline=1, attr=attrs))
+            result.append(self.open('table', newline=1, attr=attrs,
+                                    allowed_attrs=self._allowed_table_attrs['table'],
+                                    **kw))
+            result.append(self.open('tbody', newline=1))
         else:
-            # Close table then div
+            # Close tbody, table, and then div
+            result.append(self.close('tbody'))
             result.append(self.close('table'))
             result.append(self.close('div'))
 
         return ''.join(result)    
     
-    def table_row(self, on, attrs=None):
+    def table_row(self, on, attrs=None, **kw):
         tag = 'tr'
         if on:
             if not attrs:
                 attrs = {}
             else:
                 attrs = self._checkTableAttr(attrs, 'row')
-            return self.open(tag, newline=1, attr=attrs)
-        return self.close(tag)
+            return self.open(tag, newline=1, attr=attrs,
+                             allowed_attrs=self._allowed_table_attrs['row'],
+                             **kw)
+        return self.close(tag) + '\n'
     
-    def table_cell(self, on, attrs=None):
+    def table_cell(self, on, attrs=None, **kw):
         tag = 'td'
         if on:
             if not attrs:
                 attrs = {}
             else:
                 attrs = self._checkTableAttr(attrs, '')
-            return self.open(tag, newline=1, attr=attrs)
-        return self.close(tag)
-
-    def escapedText(self, text):
-        return wikiutil.escape(text)
+            return '  ' + self.open(tag, attr=attrs,
+                             allowed_attrs=self._allowed_table_attrs[''],
+                             **kw)
+        return self.close(tag) + '\n'
+
+    def text(self, text, **kw):
+        txt = FormatterBase.text( self, text, **kw )
+        if kw:
+            return self.open('span',**kw) + txt + self.close('span')
+        return txt
+
+    def escapedText(self, text, **kw):
+        txt = wikiutil.escape(text)
+        if kw:
+            return self.open('span',**kw) + txt + self.close('span')
+        return txt
 
     def rawHTML(self, markup):
         return markup
Only in moin-1.5.0-formatter-patch/MoinMoin/formatter: text_html.pyc
diff -ur moin-1.5.0/MoinMoin/formatter/text_plain.py moin-1.5.0-formatter-patch/MoinMoin/formatter/text_plain.py
--- moin-1.5.0/MoinMoin/formatter/text_plain.py	2005-11-18 14:24:24.000000000 -0500
+++ moin-1.5.0-formatter-patch/MoinMoin/formatter/text_plain.py	2006-01-16 12:06:32.000000000 -0500
@@ -79,27 +79,27 @@
     def attachment_drawing(self, url, text, **kw):
         return "[drawing:%s]" % text
     
-    def text(self, text):
+    def text(self, text, **kw):
         self._did_para = 0
         if self._text is not None:
             self._text.append(text)
         return text
 
-    def rule(self, size=0):
+    def rule(self, size=0, **kw):
         size = min(size, 10)
         ch = u"---~=*+#####"[size]
         return (ch * 79) + u'\n'
 
-    def strong(self, on):
+    def strong(self, on, **kw):
         return u'*'
 
-    def emphasis(self, on):
+    def emphasis(self, on, **kw):
         return u'/'
 
-    def highlight(self, on):
+    def highlight(self, on, **kw):
         return u''
 
-    def number_list(self, on, type=None, start=None):
+    def number_list(self, on, type=None, start=None, **kw):
         if on:
             self._in_list = 1
             return [u'\n', u'\n\n'][not self._did_para]
@@ -110,7 +110,7 @@
                 return u'\n'
         return u''
 
-    def bullet_list(self, on):
+    def bullet_list(self, on, **kw):
         if on:
             self._in_list = -1
             return [u'\n', u'\n\n'][not self._did_para]
@@ -136,20 +136,20 @@
             self._did_para = 1
             return u'\n'
         
-    def sup(self, on):
+    def sup(self, on, **kw):
         return u'^'
 
-    def sub(self, on):
+    def sub(self, on, **kw):
         return u'_'
 
-    def strike(self, on):
+    def strike(self, on, **kw):
         return u'__'
 
     def code(self, on, **kw):
         #return [unichr(0x60), unichr(0xb4)][not on]
         return u"'" # avoid high-ascii
 
-    def preformatted(self, on):
+    def preformatted(self, on, **kw):
         FormatterBase.preformatted(self, on)
         snip = u'---%<'
         snip = snip + (u'-' * (78 - len(snip)))
@@ -158,10 +158,10 @@
         else:
             return snip + u'\n'
 
-    def small(self, on):
+    def small(self, on, **kw):
         return u''
 
-    def big(self, on):
+    def big(self, on, **kw):
         return u''
 
     def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
@@ -191,7 +191,7 @@
     def code_token(self, on, tok_type):
         return ""
 
-    def paragraph(self, on):
+    def paragraph(self, on, **kw):
         FormatterBase.paragraph(self, on)
         if self._did_para:
             on = 0
@@ -212,33 +212,34 @@
             self._text = None
             return result
 
-    def table(self, on, attrs={}):
+    def table(self, on, attrs={}, **kw):
         return u''
 
-    def table_row(self, on, attrs={}):
+    def table_row(self, on, attrs={}, **kw):
         return u''
 
-    def table_cell(self, on, attrs={}):
+    def table_cell(self, on, attrs={}, **kw):
         return u''
 
-    def underline(self, on):
+    def underline(self, on, **kw):
         return u'_'
 
-    def definition_list(self, on):
+    def definition_list(self, on, **kw):
         return u''
 
-    def definition_term(self, on, compact=0):
+    def definition_term(self, on, compact=0, **kw):
         result = u''
         if not compact: result = result + u'\n'
         if not on: result = result + u':\n'
         return result
 
-    def definition_desc(self, on):
+    def definition_desc(self, on, **kw):
         return [u'    ', u'\n'][not on]
 
-    def image(self, **kw):
-        if kw.has_key(u'alt'):
-            return kw[u'alt']
+    def image(self, src=None, **kw):
+        for a in (u'alt',u'title'):
+            if kw.has_key(a):
+                return kw[a]
         return u''
 
     def lang(self, on, lang_name):
diff -ur moin-1.5.0/MoinMoin/formatter/text_xml.py moin-1.5.0-formatter-patch/MoinMoin/formatter/text_xml.py
--- moin-1.5.0/MoinMoin/formatter/text_xml.py	2005-12-01 20:30:07.000000000 -0500
+++ moin-1.5.0-formatter-patch/MoinMoin/formatter/text_xml.py	2006-01-16 12:06:32.000000000 -0500
@@ -77,12 +77,12 @@
         return '<attachmentdrawing href="%s">%s</attachmentdrawing>' % (
             url, text)
 
-    def text(self, text):
+    def text(self, text, **kw):
         if self.in_pre:
             return text.replace(']]>', ']]>]]&gt;<![CDATA[')
         return self._escape(text)
 
-    def rule(self, size=0):
+    def rule(self, size=0, **kw):
         return "\n<br/>%s<br/>\n" % ("-"*78,) # <hr/> not supported in stylebook
         if size:
             return '<hr size="%d"/>\n' % (size,)
@@ -92,21 +92,21 @@
     def icon(self, type):
         return '<icon type="%s" />' % type            
 
-    def strong(self, on):
+    def strong(self, on, **kw):
         return ['<strong>', '</strong>'][not on]
 
-    def emphasis(self, on):
+    def emphasis(self, on, **kw):
         return ['<em>', '</em>'][not on]
 
-    def highlight(self, on):
+    def highlight(self, on, **kw):
         return ['<strong>', '</strong>'][not on]
 
-    def number_list(self, on, type=None, start=None):
+    def number_list(self, on, type=None, start=None, **kw):
         result = ''
         if self.in_p: result = self.paragraph(0)
         return result + ['<ol>', '</ol>\n'][not on]
 
-    def bullet_list(self, on):
+    def bullet_list(self, on, **kw):
         result = ''
         if self.in_p: result = self.paragraph(0)
         return result + ['<ul>', '</ul>\n'][not on]
@@ -117,22 +117,22 @@
     def code(self, on, **kw):
         return ['<code>', '</code>'][not on]
 
-    def sup(self, on):
+    def sup(self, on, **kw):
         return ['<sup>', '</sup>'][not on]
 
-    def sub(self, on):
+    def sub(self, on, **kw):
         return ['<sub>', '</sub>'][not on]
 
-    def strike(self, on):
+    def strike(self, on, **kw):
         return ['<strike>', '</strike>'][not on]
 
-    def preformatted(self, on):
+    def preformatted(self, on, **kw):
         FormatterBase.preformatted(self, on)
         result = ''
         if self.in_p: result = self.paragraph(0)
         return result + ['<source><![CDATA[', ']]></source>'][not on]
 
-    def paragraph(self, on):
+    def paragraph(self, on, **kw):
         FormatterBase.paragraph(self, on)
         return ['<p>', '</p>\n'][not on]
 
@@ -160,41 +160,42 @@
 
         return result + '<s%d%s title="' % (depth, id_text)
 
-    def table(self, on, attrs={}):
+    def table(self, on, attrs={}, **kw):
         return ['<table>', '</table>'][not on]
 
-    def table_row(self, on, attrs={}):
+    def table_row(self, on, attrs={}, **kw):
         return ['<tr>', '</tr>'][not on]
 
-    def table_cell(self, on, attrs={}):
+    def table_cell(self, on, attrs={}, **kw):
         return ['<td>', '</td>'][not on]
 
     def anchordef(self, id):
         return '<anchor id="%s"/>' % id
 
-    def anchorlink(self, on, name='', id=None):
+    def anchorlink(self, on, name='', **kw):
+        id = kw.get('id',None)
         extra = ''
         if id:
             extra = ' id="%s"' % id
         return ('<link anchor="%s"%s>' % (name, extra) ,'</link>') [not on]
 
-    def underline(self, on):
+    def underline(self, on, **kw):
         return self.strong(on) # no underline in StyleBook
 
-    def definition_list(self, on):
+    def definition_list(self, on, **kw):
         result = ''
         if self.in_p: result = self.paragraph(0)
         return result + ['<gloss>', '</gloss>'][not on]
 
-    def definition_term(self, on, compact=0):
+    def definition_term(self, on, compact=0, **kw):
         return ['<label>', '</label>'][not on]
 
-    def definition_desc(self, on):
+    def definition_desc(self, on, **kw):
         return ['<item>', '</item>'][not on]
 
-    def image(self, **kw):
+    def image(self, src=None, **kw):
         valid_attrs = ['src', 'width', 'height', 'alt']
-        attrs = {}
+        attrs = {'src':src}
         for key, value in kw.items():
             if key in valid_attrs:
                 attrs[key] = value
diff -ur moin-1.5.0/MoinMoin/formatter/xml_docbook.py moin-1.5.0-formatter-patch/MoinMoin/formatter/xml_docbook.py
--- moin-1.5.0/MoinMoin/formatter/xml_docbook.py	2005-12-01 20:30:07.000000000 -0500
+++ moin-1.5.0-formatter-patch/MoinMoin/formatter/xml_docbook.py	2006-01-16 12:06:33.000000000 -0500
@@ -132,7 +132,7 @@
     def endDocument(self):
         return self.outputFormatter.getFooter()
 
-    def text(self, text):
+    def text(self, text, **kw):
         if text == "\\n":
             srcText = "\n"
         else:
@@ -183,7 +183,7 @@
 
         return ""
 
-    def paragraph(self, on):
+    def paragraph(self, on, **kw):
         FormatterBase.paragraph(self, on)
         if on:
             para = self.doc.createElement("para")
@@ -269,40 +269,40 @@
 
         return self._handleNode(name, on, attributes)
 
-    def strong(self, on):
+    def strong(self, on, **kw):
         return self._handleFormatting("emphasis", on, (('role','strong'), ))
 
-    def emphasis(self, on):
+    def emphasis(self, on, **kw):
         return self._handleFormatting("emphasis", on)
 
-    def underline(self, on):
+    def underline(self, on, **kw):
         return self._handleFormatting("emphasis", on, (('role','underline'), ))
 
-    def highlight(self, on):
+    def highlight(self, on, **kw):
         return self._handleFormatting("emphasis", on, (('role','highlight'), ))
 
-    def sup(self, on):
+    def sup(self, on, **kw):
         return self._handleFormatting("superscript", on)
 
-    def sub(self, on):
+    def sub(self, on, **kw):
         return self._handleFormatting("subscript", on)
 
-    def strike(self, on):
+    def strike(self, on, **kw):
         # does not yield <strike> using the HTML XSLT files here ...
         # but seems to be correct
         return self._handleFormatting("emphasis", on,
                                       (('role','strikethrough'), ))
 
-    def code(self, on, **kwargs):
+    def code(self, on, **kw):
         return self._handleFormatting("code", on)
 
-    def preformatted(self, on):
+    def preformatted(self, on, **kw):
         return self._handleFormatting("screen", on)
 
 
 ### Lists ###########################################################
 
-    def number_list(self, on, type=None, start=None):
+    def number_list(self, on, type=None, start=None, **kw):
         docbook_ol_types = {'1': "arabic", 
                             'a': "loweralpha", 
                             'A': "upperalpha",
@@ -316,16 +316,16 @@
 
         return self._handleNode('orderedlist', on, attrs)
 
-    def bullet_list(self, on):
+    def bullet_list(self, on, **kw):
         return self._handleNode("itemizedlist", on)
 
-    def definition_list(self, on):
+    def definition_list(self, on, **kw):
         return self._handleNode("glosslist", on)        
 
     '''When on is false, we back out just on level. This is
        ok because we know definition_desc gets called, and we
        back out two levels there'''
-    def definition_term(self, on, compact=0):
+    def definition_term(self, on, compact=0, **kw):
         if on:
             entry=self.doc.createElement('glossentry')
             term=self.doc.createElement('glossterm')
@@ -337,7 +337,7 @@
         return ""
    
     '''We backout two levels when 'on' is false, to leave the glossentry stuff'''
-    def definition_desc(self, on):
+    def definition_desc(self, on, **kw):
         if on:
             return self._handleNode("glossdef", on)
         else:
@@ -381,7 +381,8 @@
         self._handleNode("ulink", False)
         return ""
 
-    def anchorlink(self, on, name='', id=None):
+    def anchorlink(self, on, name='', **kw):
+        id = kw.get('id',None)
         attrs = []
         if name != '':
             attrs.append(('endterm', name))
@@ -438,7 +439,9 @@
 
 
 ### Images and Smileys ##############################################
-    def image(self, **kw):
+    def image(self, src=None, **kw):
+        if src:
+            kw['src']=src
         media = self.doc.createElement('inlinemediaobject')
 
         imagewrap = self.doc.createElement('imageobject')
@@ -477,7 +480,7 @@
 
     #FIXME: We should copy code from text_html.py for attr handling
 
-    def table(self, on, attrs=None):
+    def table(self, on, attrs=None, **kw):
         sanitized_attrs = []
         if attrs and attrs.has_key('id'):
             sanitized_attrs[id] = attrs['id']
@@ -489,14 +492,14 @@
         self._handleNode("tbody", on)
         return ""
     
-    def table_row(self, on, attrs=None):
+    def table_row(self, on, attrs=None, **kw):
         self.table_current_row_cells = 0
         sanitized_attrs = []
         if attrs and attrs.has_key('id'):
             sanitized_attrs[id] = attrs['id']
         return self._handleNode("row", on, sanitized_attrs)
 
-    def table_cell(self, on, attrs=None):
+    def table_cell(self, on, attrs=None, **kw):
         # Finish row definition
         sanitized_attrs = []
         if attrs and attrs.has_key('id'):
@@ -572,11 +575,11 @@
         return u""
 
 ### Not supported ###################################################
-    def rule(self, size = 0):
+    def rule(self, size = 0, **kw):
         return ""
 
-    def small(self, on):
+    def small(self, on, **kw):
         return ""
 
-    def big(self, on):
+    def big(self, on, **kw):
         return ""
