changeset 96:fa08aef181a2 trunk

Add an XHTML serialization method. Now really need to get rid of some code duplication in the `markup.output` module.
author cmlenz
date Fri, 21 Jul 2006 11:39:32 +0000
parents 2fe86a99947f
children ff19219485cc
files markup/core.py markup/output.py
diffstat 2 files changed, 87 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/markup/core.py
+++ b/markup/core.py
@@ -68,7 +68,8 @@
         """Return a string representation of the stream.
         
         @param method: determines how the stream is serialized; can be either
-                       'xml' or 'html', or a custom `Serializer` subclass
+                       "xml", "xhtml", or "html", or a custom `Serializer`
+                       subclass
         @param encoding: how the output string should be encoded; if set to
                          `None`, this method returns a `unicode` object
 
@@ -99,7 +100,8 @@
         string.
         
         @param method: determines how the stream is serialized; can be either
-                       'xml' or 'html', or a custom `Serializer` subclass
+                       "xml", "xhtml", or "html", or a custom `Serializer`
+                       subclass
         @param filters: list of filters to apply to the stream before
                         serialization. The default is to apply whitespace
                         reduction using `markup.filters.WhitespaceFilter`.
@@ -108,8 +110,9 @@
         from markup import output
         cls = method
         if isinstance(method, basestring):
-            cls = {'xml': output.XMLSerializer,
-                   'html': output.HTMLSerializer}[method]
+            cls = {'xml':   output.XMLSerializer,
+                   'xhtml': output.XHTMLSerializer,
+                   'html':  output.HTMLSerializer}[method]
         else:
             assert issubclass(cls, output.Serializer)
         serializer = cls(**kwargs)
--- a/markup/output.py
+++ b/markup/output.py
@@ -155,13 +155,13 @@
                 yield Markup('<!--%s-->' % data)
 
 
-class HTMLSerializer(Serializer):
-    """Produces HTML text from an event stream.
+class XHTMLSerializer(XMLSerializer):
+    """Produces XHTML text from an event stream.
     
     >>> from markup.builder import tag
     >>> elem = tag.div(tag.a(href='foo'), tag.br, tag.hr(noshade=True))
-    >>> print ''.join(HTMLSerializer().serialize(elem.generate()))
-    <div><a href="foo"></a><br><hr noshade></div>
+    >>> print ''.join(XHTMLSerializer().serialize(elem.generate()))
+    <div><a href="foo"></a><br /><hr noshade="noshade" /></div>
     """
 
     NAMESPACE = Namespace('http://www.w3.org/1999/xhtml')
@@ -173,17 +173,6 @@
                                 'defer', 'disabled', 'ismap', 'multiple',
                                 'nohref', 'noresize', 'noshade', 'nowrap'])
 
-    def __init__(self, doctype=None):
-        """Initialize the HTML serializer.
-        
-        @param doctype: a `(name, pubid, sysid)` tuple that represents the
-            DOCTYPE declaration that should be included at the top of the
-            generated output
-        """
-        self.preamble = []
-        if doctype:
-            self.preamble.append((DOCTYPE, doctype, (None, -1, -1)))
-
     def serialize(self, stream):
         have_doctype = False
         ns_mapping = {}
@@ -215,6 +204,82 @@
                 if tag.namespace and tag not in self.NAMESPACE:
                     continue # not in the HTML namespace, so don't emit
                 buf = ['<', tag.localname]
+
+                for attr, value in attrib:
+                    if attr.namespace and attr not in self.NAMESPACE:
+                        continue # not in the HTML namespace, so don't emit
+                    if attr.localname in self._BOOLEAN_ATTRS:
+                        if value:
+                            buf.append(' %s="%s"' % (attr.localname, attr.localname))
+                    else:
+                        buf.append(' %s="%s"' % (attr.localname, escape(value)))
+
+                if tag.localname in self._EMPTY_ELEMS:
+                    kind, data, pos = stream.next()
+                    if kind is END:
+                        buf.append(' />')
+                    else:
+                        buf.append('>')
+                        stream.pushback((kind, data, pos))
+                else:
+                    buf.append('>')
+
+                yield Markup(''.join(buf))
+
+            elif kind is END:
+                tag = data
+                if tag.namespace and tag not in self.NAMESPACE:
+                    continue # not in the HTML namespace, so don't emit
+                yield Markup('</%s>' % tag.localname)
+
+            elif kind is TEXT:
+                yield escape(data, quotes=False)
+
+            elif kind is COMMENT:
+                yield Markup('<!--%s-->' % data)
+
+
+class HTMLSerializer(XHTMLSerializer):
+    """Produces HTML text from an event stream.
+    
+    >>> from markup.builder import tag
+    >>> elem = tag.div(tag.a(href='foo'), tag.br, tag.hr(noshade=True))
+    >>> print ''.join(HTMLSerializer().serialize(elem.generate()))
+    <div><a href="foo"></a><br><hr noshade></div>
+    """
+
+    def serialize(self, stream):
+        have_doctype = False
+        ns_mapping = {}
+
+        stream = _PushbackIterator(chain(self.preamble, stream))
+        for kind, data, pos in stream:
+
+            if kind is DOCTYPE:
+                if not have_doctype:
+                    name, pubid, sysid = data
+                    buf = ['<!DOCTYPE %s']
+                    if pubid:
+                        buf.append(' PUBLIC "%s"')
+                    elif sysid:
+                        buf.append(' SYSTEM')
+                    if sysid:
+                        buf.append(' "%s"')
+                    buf.append('>\n')
+                    yield Markup(''.join(buf), *filter(None, data))
+                    have_doctype = True
+
+            elif kind is START_NS:
+                prefix, uri = data
+                if uri not in ns_mapping:
+                    ns_mapping[uri] = prefix
+
+            elif kind is START:
+                tag, attrib = data
+                if tag.namespace and tag not in self.NAMESPACE:
+                    continue # not in the HTML namespace, so don't emit
+                buf = ['<', tag.localname]
+
                 for attr, value in attrib:
                     if attr.namespace and attr not in self.NAMESPACE:
                         continue # not in the HTML namespace, so don't emit
Copyright (C) 2012-2017 Edgewall Software