Index: genshi/core.py
===================================================================
--- genshi/core.py	(revision 557)
+++ genshi/core.py	(working copy)
@@ -62,6 +62,7 @@
     START_CDATA = StreamEventKind('START_CDATA') #: start CDATA section
     END_CDATA = StreamEventKind('END_CDATA') #: end CDATA section
     PI = StreamEventKind('PI') #: processing instruction
+    XML_DECL = StreamEventKind('XML_DECL') #: XML declaration PI
     COMMENT = StreamEventKind('COMMENT') #: comment
 
     def __init__(self, events):
@@ -214,6 +215,7 @@
 START_CDATA = Stream.START_CDATA
 END_CDATA = Stream.END_CDATA
 PI = Stream.PI
+XML_DECL = Stream.XML_DECL
 COMMENT = Stream.COMMENT
 
 def _ensure(stream):
Index: genshi/tests/input.py
===================================================================
--- genshi/tests/input.py	(revision 557)
+++ genshi/tests/input.py	(working copy)
@@ -71,7 +71,7 @@
         <div>\xf6</div>
         """.encode('iso-8859-1')
         events = list(XMLParser(StringIO(text)))
-        kind, data, pos = events[1]
+        kind, data, pos = events[2]
         self.assertEqual(Stream.TEXT, kind)
         self.assertEqual(u'\xf6', data)
 
@@ -181,6 +181,33 @@
         self.assertEqual(u'php', target)
         self.assertEqual(u'echo "Foobar"', data)
 
+    def test_xmldecl(self):
+        text = '<?xml version="1.0" ?><root />'
+        events = list(XMLParser(StringIO(text)))
+        kind, (version, encoding, standalone), pos = events[0]
+        self.assertEqual(Stream.XML_DECL, kind)
+        self.assertEqual(u'1.0', version)
+        self.assertEqual(None, encoding)
+        self.assertEqual(-1, standalone)
+
+    def test_xmldecl_encoding(self):
+        text = '<?xml version="1.0" encoding="utf-8" ?><root />'
+        events = list(XMLParser(StringIO(text)))
+        kind, (version, encoding, standalone), pos = events[0]
+        self.assertEqual(Stream.XML_DECL, kind)
+        self.assertEqual(u'1.0', version)
+        self.assertEqual(u'utf-8', encoding)
+        self.assertEqual(-1, standalone)
+
+    def test_xmldecl_standalone(self):
+        text = '<?xml version="1.0" standalone="yes" ?><root />'
+        events = list(XMLParser(StringIO(text)))
+        kind, (version, encoding, standalone), pos = events[0]
+        self.assertEqual(Stream.XML_DECL, kind)
+        self.assertEqual(u'1.0', version)
+        self.assertEqual(None, encoding)
+        self.assertEqual(1, standalone)
+
     def test_processing_instruction_trailing_qmark(self):
         text = '<?php echo "Foobar" ??>'
         events = list(HTMLParser(StringIO(text)))
Index: genshi/input.py
===================================================================
--- genshi/input.py	(revision 557)
+++ genshi/input.py	(working copy)
@@ -27,7 +27,7 @@
 
 from genshi.core import Attrs, QName, Stream, stripentities
 from genshi.core import DOCTYPE, START, END, START_NS, END_NS, TEXT, \
-                        START_CDATA, END_CDATA, PI, COMMENT
+                        START_CDATA, END_CDATA, PI, XML_DECL, COMMENT
 
 __all__ = ['ET', 'ParseError', 'XMLParser', 'XML', 'HTMLParser', 'HTML']
 __docformat__ = 'restructuredtext en'
@@ -123,6 +123,7 @@
         parser.StartCdataSectionHandler = self._handle_start_cdata
         parser.EndCdataSectionHandler = self._handle_end_cdata
         parser.ProcessingInstructionHandler = self._handle_pi
+        parser.XmlDeclHandler = self._handle_xml_decl
         parser.CommentHandler = self._handle_comment
 
         # Tell Expat that we'll handle non-XML entities ourselves
@@ -234,6 +235,9 @@
     def _handle_pi(self, target, data):
         self._enqueue(PI, (target, data))
 
+    def _handle_xml_decl(self, version, encoding, standalone):
+        self._enqueue(XML_DECL, (version, encoding, standalone))
+
     def _handle_comment(self, text):
         self._enqueue(COMMENT, text)
 
Index: genshi/output.py
===================================================================
--- genshi/output.py	(revision 557)
+++ genshi/output.py	(working copy)
@@ -24,7 +24,8 @@
 
 from genshi.core import escape, Attrs, Markup, Namespace, QName, StreamEventKind
 from genshi.core import DOCTYPE, START, END, START_NS, END_NS, TEXT, \
-                        START_CDATA, END_CDATA, PI, COMMENT, XML_NAMESPACE
+                        START_CDATA, END_CDATA, PI, XML_DECL, COMMENT, \
+                        XML_NAMESPACE
 
 __all__ = ['DocType', 'XMLSerializer', 'XHTMLSerializer', 'HTMLSerializer',
            'TextSerializer']
@@ -139,6 +140,17 @@
             elif kind is PI:
                 yield Markup('<?%s %s?>' % data)
 
+            elif kind is XML_DECL:
+                version, encoding, standalone = data
+                buf = ['<?xml version="%s"' % version]
+                if encoding:
+                    buf.append(' encoding="%s"' % encoding)
+                if standalone != -1:
+                    standalone = standalone and 'yes' or 'no'
+                    buf.append(' standalone="%s"' % standalone)
+                buf.append('?>\n')
+                yield Markup(u''.join(buf))
+
 
 class XHTMLSerializer(XMLSerializer):
     """Produces XHTML text from an event stream.
Index: genshi/template/tests/markup.py
===================================================================
--- genshi/template/tests/markup.py	(revision 557)
+++ genshi/template/tests/markup.py	(working copy)
@@ -183,7 +183,7 @@
         <div xmlns:py="http://genshi.edgewall.org/">
           \xf6
         </div>""".encode('iso-8859-1'), encoding='iso-8859-1')
-        self.assertEqual(u"""<div>
+        self.assertEqual(u"""<?xml version="1.0" encoding="iso-8859-1"?>\n<div>
           \xf6
         </div>""", unicode(tmpl.generate()))
 
@@ -228,7 +228,7 @@
           <Size py:if="0" xmlns:t="test">Size</Size>
           <Item/>
         </Test>""")
-        self.assertEqual("""<Test>
+        self.assertEqual("""<?xml version="1.0"?>\n<Test>
           
           <Item/>
         </Test>""", str(tmpl.generate()))
