]> git.saurik.com Git - wxWidgets.git/commitdiff
Eliminated redundant wxRichTextAnchoredObject class; refactored XML I/O code
authorJulian Smart <julian@anthemion.co.uk>
Wed, 6 Oct 2010 20:22:03 +0000 (20:22 +0000)
committerJulian Smart <julian@anthemion.co.uk>
Wed, 6 Oct 2010 20:22:03 +0000 (20:22 +0000)
so that objects can stream themselves; added a wxXmlDocument-based method
of writing XML, though this turned out to be much slower than writing directly
so the direct approach is retained and is the default (can be changed with wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT).
Loading and saving new attributes implemented. Added custom properties to objects.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@65779 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/richtext/richtextbuffer.h
include/wx/richtext/richtextxml.h
samples/richtext/richtext.cpp
src/richtext/richtextbuffer.cpp
src/richtext/richtextxml.cpp

index c4bbb91781dd4594d350b5f691e002bf928429a4..cd131df4e990b1db923088f8f55a61fb7695fc31 100644 (file)
 #include "wx/image.h"
 #include "wx/cmdproc.h"
 #include "wx/txtstrm.h"
+#include "wx/variant.h"
 
 #if wxUSE_DATAOBJ
 #include "wx/dataobj.h"
 #endif
 
 // Compatibility
+//#define wxRichTextAttr wxTextAttr
 #define wxTextAttrEx wxTextAttr
 
 // Setting wxRICHTEXT_USE_OWN_CARET to 1 implements a
 // don't use for now
 #define wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING 0
 
+// The following two symbols determine whether an output implementation
+// is present. To switch the relevant one on, set wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT in
+// richtextxml.cpp. By default, the faster direct output implementation is used.
+
+// Include the wxXmlDocument implementation for output
+#define wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT 1
+
+// Include the faster, direct implementation for output
+#define wxRICHTEXT_HAVE_DIRECT_OUTPUT 1
+
 /*!
  * Special characters
  */
@@ -123,8 +135,9 @@ class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextListStyleDefinition;
 class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextEvent;
 class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextRenderer;
 class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextBuffer;
-class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextAnchoredObject;
-class wxRichTextFloatCollector;
+class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextXMLHandler;
+class WXDLLIMPEXP_FWD_XML      wxXmlNode;
+class                          wxRichTextFloatCollector;
 
 /*!
  * Flags determining the available space, passed to Layout
@@ -298,6 +311,7 @@ public:
     void SetValueMM(float value) { m_value = (int) ((value * 10.0) + 0.5); m_flags |= wxTEXT_ATTR_VALUE_PRESENT; }
     void SetValue(int value) { m_value = value; m_flags |= wxTEXT_ATTR_VALUE_PRESENT; }
     void SetValue(int value, wxTextAttrDimensionFlags flags) { m_value = value; m_flags = flags; }
+    void SetValue(const wxTextAttrDimension& dim) { (*this) = dim; }
     
     wxTextAttrUnits GetUnits() const { return (wxTextAttrUnits) (m_flags & wxTEXT_ATTR_UNITS_MASK); }
     void SetUnits(wxTextAttrUnits units) { m_flags &= ~wxTEXT_ATTR_UNITS_MASK; m_flags |= units; }
@@ -308,29 +322,45 @@ public:
     bool IsPresent() const { return (m_flags & wxTEXT_ATTR_VALUE_PRESENT) != 0; }
     void SetPresent(bool b) { m_flags &= ~wxTEXT_ATTR_VALUE_PRESENT_MASK; m_flags |= (b ? wxTEXT_ATTR_VALUE_PRESENT : 0); }
     
+    wxTextAttrDimensionFlags GetFlags() const { return m_flags; }
+    void SetFlags(wxTextAttrDimensionFlags flags) { m_flags = flags; }
+    
     int                         m_value;
     wxTextAttrDimensionFlags    m_flags;
 };
 
-class WXDLLIMPEXP_RICHTEXT wxTextBoxAttrDimensions
+// A class for left, right, top and bottom dimensions
+class WXDLLIMPEXP_RICHTEXT wxTextAttrDimensions
 {
 public:
     void Reset() { m_left.Reset(); m_top.Reset(); m_right.Reset(); m_bottom.Reset(); }
     
-    bool operator==(const wxTextBoxAttrDimensions& dims) const { return m_left == dims.m_left && m_top == dims.m_top && m_right == dims.m_right && m_bottom == dims.m_bottom; }
+    bool operator==(const wxTextAttrDimensions& dims) const { return m_left == dims.m_left && m_top == dims.m_top && m_right == dims.m_right && m_bottom == dims.m_bottom; }
     
     // Partial equality test
-    bool EqPartial(const wxTextBoxAttrDimensions& dims) const;
+    bool EqPartial(const wxTextAttrDimensions& dims) const;
 
     // Apply border to 'this', but not if the same as compareWith
-    bool Apply(const wxTextBoxAttrDimensions& dims, const wxTextBoxAttrDimensions* compareWith = NULL);
+    bool Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith = NULL);
 
     // Collects the attributes that are common to a range of content, building up a note of
     // which attributes are absent in some objects and which clash in some objects.
-    void CollectCommonAttributes(const wxTextBoxAttrDimensions& attr, wxTextBoxAttrDimensions& clashingAttr, wxTextBoxAttrDimensions& absentAttr);
+    void CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr);
 
     // Remove specified attributes from this object
-    bool RemoveStyle(const wxTextBoxAttrDimensions& attr);
+    bool RemoveStyle(const wxTextAttrDimensions& attr);
+
+    const wxTextAttrDimension& GetLeft() const { return m_left; }
+    wxTextAttrDimension& GetLeft() { return m_left; }
+
+    const wxTextAttrDimension& GetRight() const { return m_right; }
+    wxTextAttrDimension& GetRight() { return m_right; }
+
+    const wxTextAttrDimension& GetTop() const { return m_top; }
+    wxTextAttrDimension& GetTop() { return m_top; }
+
+    const wxTextAttrDimension& GetBottom() const { return m_bottom; }
+    wxTextAttrDimension& GetBottom() { return m_bottom; }
 
     wxTextAttrDimension         m_left;
     wxTextAttrDimension         m_top;
@@ -338,8 +368,29 @@ public:
     wxTextAttrDimension         m_bottom;
 };
 
+// A class to make it easier to convert dimensions
+class WXDLLIMPEXP_RICHTEXT wxTextAttrDimensionConverter
+{
+public:
+    wxTextAttrDimensionConverter(wxDC& dc, double scale = 1.0, const wxSize& parentSize = wxDefaultSize)
+    { m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize; }
+
+    wxTextAttrDimensionConverter(int ppi, double scale = 1.0, const wxSize& parentSize = wxDefaultSize)
+    { m_ppi = ppi; m_scale = scale; m_parentSize = parentSize; }
+    
+    int GetPixels(const wxTextAttrDimension& dim, int direction = wxHORIZONTAL) const;
+    int GetTenthsMM(const wxTextAttrDimension& dim) const;
+
+    int ConvertTenthsMMToPixels(int units) const;
+    int ConvertPixelsToTenthsMM(int pixels) const;
+
+    int     m_ppi;
+    double  m_scale;
+    wxSize  m_parentSize;
+};
+
 // Border styles
-enum wxTextBoxAttrBorderStyle
+enum wxTextAttrBorderStyle
 {
     wxTEXT_BOX_ATTR_BORDER_NONE             = 0,
     wxTEXT_BOX_ATTR_BORDER_SOLID            = 1,
@@ -353,12 +404,20 @@ enum wxTextBoxAttrBorderStyle
 };
 
 // Border style presence flags
-enum wxTextBoxAttrBorderFlags
+enum wxTextAttrBorderFlags
 {
     wxTEXT_BOX_ATTR_BORDER_STYLE            = 0x0001,
     wxTEXT_BOX_ATTR_BORDER_COLOUR           = 0x0002
 };
 
+// Border width symbols for qualitative widths
+enum wxTextAttrBorderWidth
+{
+    wxTEXT_BOX_ATTR_BORDER_THIN             = -1,
+    wxTEXT_BOX_ATTR_BORDER_MEDIUM           = -2,
+    wxTEXT_BOX_ATTR_BORDER_THICK            = -3
+};
+
 // Float styles
 enum wxTextBoxAttrFloatStyle
 {
@@ -384,12 +443,12 @@ enum wxTextBoxAttrCollapseMode
 };
 
 // Border
-class WXDLLIMPEXP_RICHTEXT wxTextBoxAttrBorder
+class WXDLLIMPEXP_RICHTEXT wxTextAttrBorder
 {
 public:
-    wxTextBoxAttrBorder() { Reset(); }
+    wxTextAttrBorder() { Reset(); }
     
-    bool operator==(const wxTextBoxAttrBorder& border) const
+    bool operator==(const wxTextAttrBorder& border) const
     {
         return m_flags == border.m_flags && m_borderStyle == border.m_borderStyle &&
                m_borderColour == border.m_borderColour && m_borderWidth == border.m_borderWidth;
@@ -398,17 +457,17 @@ public:
     void Reset() { m_borderStyle = 0; m_borderColour = 0; m_flags = 0; m_borderWidth.Reset(); }
 
     // Partial equality test
-    bool EqPartial(const wxTextBoxAttrBorder& border) const;
+    bool EqPartial(const wxTextAttrBorder& border) const;
 
     // Apply border to 'this', but not if the same as compareWith
-    bool Apply(const wxTextBoxAttrBorder& border, const wxTextBoxAttrBorder* compareWith = NULL);
+    bool Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith = NULL);
 
     // Remove specified attributes from this object
-    bool RemoveStyle(const wxTextBoxAttrBorder& attr);
+    bool RemoveStyle(const wxTextAttrBorder& attr);
 
     // Collects the attributes that are common to a range of content, building up a note of
     // which attributes are absent in some objects and which clash in some objects.
-    void CollectCommonAttributes(const wxTextBoxAttrBorder& attr, wxTextBoxAttrBorder& clashingAttr, wxTextBoxAttrBorder& absentAttr);
+    void CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr);
 
     void SetStyle(int style) { m_borderStyle = style; m_flags |= wxTEXT_BOX_ATTR_BORDER_STYLE; }
     int GetStyle() const { return m_borderStyle; }
@@ -422,10 +481,12 @@ public:
     const wxTextAttrDimension& GetWidth() const { return m_borderWidth; }
     void SetWidth(const wxTextAttrDimension& width) { m_borderWidth = width; }
 
-    bool HasStyle() const { return (m_flags & wxTEXT_BOX_ATTR_BORDER_STYLE) == 0; }
-    bool HasColour() const { return (m_flags & wxTEXT_BOX_ATTR_BORDER_COLOUR) == 0; }
+    bool HasStyle() const { return (m_flags & wxTEXT_BOX_ATTR_BORDER_STYLE) != 0; }
+    bool HasColour() const { return (m_flags & wxTEXT_BOX_ATTR_BORDER_COLOUR) != 0; }
     bool HasWidth() const { return m_borderWidth.IsPresent(); }
     
+    bool IsValid() const { return HasStyle() && HasColour() && HasWidth(); }
+    
     int GetFlags() const { return m_flags; }
     void SetFlags(int flags) { m_flags = flags; }
     void AddFlag(int flag) { m_flags |= flag; }
@@ -438,12 +499,12 @@ public:
 };
 
 // Borders
-class WXDLLIMPEXP_RICHTEXT wxTextBoxAttrBorders
+class WXDLLIMPEXP_RICHTEXT wxTextAttrBorders
 {
 public:
-    wxTextBoxAttrBorders() { }
+    wxTextAttrBorders() { }
 
-    bool operator==(const wxTextBoxAttrBorders& borders) const
+    bool operator==(const wxTextAttrBorders& borders) const
     {
         return m_left == borders.m_left && m_right == borders.m_right &&
                m_top == borders.m_top && m_bottom == borders.m_bottom;
@@ -463,19 +524,33 @@ public:
     void Reset() { m_left.Reset(); m_right.Reset(); m_top.Reset(); m_bottom.Reset(); }
 
     // Partial equality test
-    bool EqPartial(const wxTextBoxAttrBorders& borders) const;
+    bool EqPartial(const wxTextAttrBorders& borders) const;
 
     // Apply border to 'this', but not if the same as compareWith
-    bool Apply(const wxTextBoxAttrBorders& borders, const wxTextBoxAttrBorders* compareWith = NULL);
+    bool Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith = NULL);
 
     // Remove specified attributes from this object
-    bool RemoveStyle(const wxTextBoxAttrBorders& attr);
+    bool RemoveStyle(const wxTextAttrBorders& attr);
 
     // Collects the attributes that are common to a range of content, building up a note of
     // which attributes are absent in some objects and which clash in some objects.
-    void CollectCommonAttributes(const wxTextBoxAttrBorders& attr, wxTextBoxAttrBorders& clashingAttr, wxTextBoxAttrBorders& absentAttr);
+    void CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr);
+    
+    bool HasBorder() const { return m_left.IsValid() || m_right.IsValid() || m_top.IsValid() || m_bottom.IsValid(); }
 
-    wxTextBoxAttrBorder m_left, m_right, m_top, m_bottom;
+    const wxTextAttrBorder& GetLeft() const { return m_left; }
+    wxTextAttrBorder& GetLeft() { return m_left; }
+    
+    const wxTextAttrBorder& GetRight() const { return m_right; }
+    wxTextAttrBorder& GetRight() { return m_right; }
+    
+    const wxTextAttrBorder& GetTop() const { return m_top; }
+    wxTextAttrBorder& GetTop() { return m_top; }
+    
+    const wxTextAttrBorder& GetBottom() const { return m_bottom; }
+    wxTextAttrBorder& GetBottom() { return m_bottom; }
+    
+    wxTextAttrBorder m_left, m_right, m_top, m_bottom;
 
 };
 
@@ -496,7 +571,7 @@ public:
     // Reset this object.
     void Reset();
 
-    // Copy. Unecessary since we let it do a binary copy
+    // Copy. Unnecessary since we let it do a binary copy
     //void Copy(const wxTextBoxAttr& attr);
 
     // Assignment
@@ -556,6 +631,9 @@ public:
     
     // Margins
     
+    wxTextAttrDimensions& GetMargins() { return m_margins; }
+    const wxTextAttrDimensions& GetMargins() const { return m_margins; }
+
     wxTextAttrDimension& GetLeftMargin() { return m_margins.m_left; }
     const wxTextAttrDimension& GetLeftMargin() const { return m_margins.m_left; }
 
@@ -570,6 +648,9 @@ public:
 
     // Position
     
+    wxTextAttrDimensions& GetPosition() { return m_position; }
+    const wxTextAttrDimensions& GetPosition() const { return m_position; }
+
     wxTextAttrDimension& GetLeft() { return m_position.m_left; }
     const wxTextAttrDimension& GetLeft() const { return m_position.m_left; }
 
@@ -584,6 +665,9 @@ public:
 
     // Padding
     
+    wxTextAttrDimensions& GetPadding() { return m_padding; }
+    const wxTextAttrDimensions& GetPadding() const { return m_padding; }
+
     wxTextAttrDimension& GetLeftPadding() { return m_padding.m_left; }
     const wxTextAttrDimension& GetLeftPadding() const { return m_padding.m_left; }
     
@@ -598,37 +682,37 @@ public:
     
     // Border
 
-    wxTextBoxAttrBorders& GetBorder() { return m_border; }
-    const wxTextBoxAttrBorders& GetBorder() const { return m_border; }
+    wxTextAttrBorders& GetBorder() { return m_border; }
+    const wxTextAttrBorders& GetBorder() const { return m_border; }
 
-    wxTextBoxAttrBorder& GetLeftBorder() { return m_border.m_left; }
-    const wxTextBoxAttrBorder& GetLeftBorder() const { return m_border.m_left; }
+    wxTextAttrBorder& GetLeftBorder() { return m_border.m_left; }
+    const wxTextAttrBorder& GetLeftBorder() const { return m_border.m_left; }
 
-    wxTextBoxAttrBorder& GetTopBorder() { return m_border.m_top; }
-    const wxTextBoxAttrBorder& GetTopBorder() const { return m_border.m_top; }
+    wxTextAttrBorder& GetTopBorder() { return m_border.m_top; }
+    const wxTextAttrBorder& GetTopBorder() const { return m_border.m_top; }
 
-    wxTextBoxAttrBorder& GetRightBorder() { return m_border.m_right; }
-    const wxTextBoxAttrBorder& GetRightBorder() const { return m_border.m_right; }
+    wxTextAttrBorder& GetRightBorder() { return m_border.m_right; }
+    const wxTextAttrBorder& GetRightBorder() const { return m_border.m_right; }
 
-    wxTextBoxAttrBorder& GetBottomBorder() { return m_border.m_bottom; }
-    const wxTextBoxAttrBorder& GetBottomBorder() const { return m_border.m_bottom; }
+    wxTextAttrBorder& GetBottomBorder() { return m_border.m_bottom; }
+    const wxTextAttrBorder& GetBottomBorder() const { return m_border.m_bottom; }
     
     // Outline
 
-    wxTextBoxAttrBorders& GetOutline() { return m_outline; }
-    const wxTextBoxAttrBorders& GetOutline() const { return m_outline; }
+    wxTextAttrBorders& GetOutline() { return m_outline; }
+    const wxTextAttrBorders& GetOutline() const { return m_outline; }
 
-    wxTextBoxAttrBorder& GetLeftOutline() { return m_outline.m_left; }
-    const wxTextBoxAttrBorder& GetLeftOutline() const { return m_outline.m_left; }
+    wxTextAttrBorder& GetLeftOutline() { return m_outline.m_left; }
+    const wxTextAttrBorder& GetLeftOutline() const { return m_outline.m_left; }
 
-    wxTextBoxAttrBorder& GetTopOutline() { return m_outline.m_top; }
-    const wxTextBoxAttrBorder& GetTopOutline() const { return m_outline.m_top; }
+    wxTextAttrBorder& GetTopOutline() { return m_outline.m_top; }
+    const wxTextAttrBorder& GetTopOutline() const { return m_outline.m_top; }
 
-    wxTextBoxAttrBorder& GetRightOutline() { return m_outline.m_right; }
-    const wxTextBoxAttrBorder& GetRightOutline() const { return m_outline.m_right; }
+    wxTextAttrBorder& GetRightOutline() { return m_outline.m_right; }
+    const wxTextAttrBorder& GetRightOutline() const { return m_outline.m_right; }
 
-    wxTextBoxAttrBorder& GetBottomOutline() { return m_outline.m_bottom; }
-    const wxTextBoxAttrBorder& GetBottomOutline() const { return m_outline.m_bottom; }
+    wxTextAttrBorder& GetBottomOutline() { return m_outline.m_bottom; }
+    const wxTextAttrBorder& GetBottomOutline() const { return m_outline.m_bottom; }
 
 
     // Width and height
@@ -643,15 +727,15 @@ public:
 
     int                         m_flags;
     
-    wxTextBoxAttrDimensions     m_margins;
-    wxTextBoxAttrDimensions     m_padding;
-    wxTextBoxAttrDimensions     m_position;
+    wxTextAttrDimensions     m_margins;
+    wxTextAttrDimensions     m_padding;
+    wxTextAttrDimensions     m_position;
 
     wxTextAttrDimension         m_width;
     wxTextAttrDimension         m_height;    
 
-    wxTextBoxAttrBorders        m_border;    
-    wxTextBoxAttrBorders        m_outline;
+    wxTextAttrBorders        m_border;    
+    wxTextAttrBorders        m_outline;
 
     short int                   m_floatMode;
     short int                   m_clearMode;
@@ -701,6 +785,57 @@ public:
     wxTextBoxAttr    m_textBoxAttr;
 };
 
+WX_DECLARE_USER_EXPORTED_OBJARRAY(wxVariant, wxRichTextVariantArray, WXDLLIMPEXP_RICHTEXT);
+
+// ----------------------------------------------------------------------------
+// wxRichTextProperties - A simple property class using wxVariants
+// ----------------------------------------------------------------------------
+
+class WXDLLIMPEXP_RICHTEXT wxRichTextProperties: public wxObject
+{
+DECLARE_DYNAMIC_CLASS(wxRichTextProperties)
+public:
+    wxRichTextProperties() {}
+    wxRichTextProperties(const wxRichTextProperties& props) { Copy(props); }
+
+    void operator=(const wxRichTextProperties& props) { Copy(props); }
+    bool operator==(const wxRichTextProperties& props) const;
+    void Copy(const wxRichTextProperties& props) { m_properties = props.m_properties; }
+    const wxVariant& operator[](size_t idx) const { return m_properties[idx]; }
+    wxVariant& operator[](size_t idx) { return m_properties[idx]; }
+    void Clear() { m_properties.Clear(); }
+
+    const wxRichTextVariantArray& GetProperties() const { return m_properties; }
+    wxRichTextVariantArray& GetProperties() { return m_properties; }
+    void SetProperties(const wxRichTextVariantArray& props) { m_properties = props; }
+
+    wxArrayString GetPropertyNames() const;
+
+    size_t GetCount() const { return m_properties.GetCount(); }
+
+    int HasProperty(const wxString& name) const { return Find(name) != -1; }
+
+    int Find(const wxString& name) const;
+    const wxVariant& GetProperty(const wxString& name) const;
+    wxVariant* FindOrCreateProperty(const wxString& name);
+
+    wxString GetPropertyString(const wxString& name) const;
+    long GetPropertyLong(const wxString& name) const;
+    bool GetPropertyBool(const wxString& name) const;
+    double GetPropertyDouble(const wxString& name) const;
+
+    void SetProperty(const wxVariant& variant);
+    void SetProperty(const wxString& name, const wxVariant& variant);
+    void SetProperty(const wxString& name, const wxString& value);
+    void SetProperty(const wxString& name, long value);
+    void SetProperty(const wxString& name, double value);
+    void SetProperty(const wxString& name, bool value);
+
+protected:
+    wxRichTextVariantArray  m_properties;
+};
+
+
 /*!
  * wxRichTextFontTable
  * Manages quick access to a pool of fonts for rendering rich text
@@ -850,13 +985,13 @@ public:
     virtual bool IsFloatable() const { return false; }
 
     /// Whether this object is currently floating
-    virtual bool IsFloating() const { return false; }
+    virtual bool IsFloating() const { return GetAttributes().GetTextBoxAttr().IsFloating(); }
 
     /// Whether this object is a place holding one
     // virtual bool IsPlaceHolding() const { return false; }
 
-    /// Floating direction
-    virtual int GetFloatDirection() const { return wxTEXT_BOX_ATTR_FLOAT_NONE; }
+    /// The floating direction
+    virtual int GetFloatDirection() const { return GetAttributes().GetTextBoxAttr().GetFloatMode(); }
 
     /// Get any text in this object for the given range
     virtual wxString GetTextForRange(const wxRichTextRange& WXUNUSED(range)) const { return wxEmptyString; }
@@ -877,6 +1012,27 @@ public:
     /// Edit properties via a GUI
     virtual bool EditProperties(wxWindow* WXUNUSED(parent), wxRichTextBuffer* WXUNUSED(buffer)) { return false; }
 
+#if wxUSE_XML
+    /// Import this object from XML
+    virtual bool ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler);
+#endif
+
+#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
+    /// Export this object directly to the given stream.
+    virtual bool ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler);
+#endif
+
+#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
+    /// Export this object to the given parent node, usually creating at least one child node.
+    virtual bool ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler);
+#endif
+
+    /// Does this object take note of paragraph attributes? Text and image objects don't.
+    virtual bool UsesParagraphAttributes() const { return true; }
+    
+    /// What is the XML node name of this object?
+    virtual wxString GetXMLNodeName() const { return wxT("unknown"); }
+
 // Accessors
 
     /// Get/set the cached object size as calculated by Layout.
@@ -916,10 +1072,15 @@ public:
     virtual int GetTopMargin() const { return m_topMargin; }
     virtual int GetBottomMargin() const { return m_bottomMargin; }
 
-    /// Set attributes object
+    /// Set/get attributes object
     void SetAttributes(const wxRichTextAttr& attr) { m_attributes = attr; }
     const wxRichTextAttr& GetAttributes() const { return m_attributes; }
     wxRichTextAttr& GetAttributes() { return m_attributes; }
+    
+    /// Set/get properties
+    wxRichTextProperties& GetProperties() { return m_properties; }
+    const wxRichTextProperties& GetProperties() const { return m_properties; }
+    void SetProperties(const wxRichTextProperties& props) { m_properties = props; }
 
     /// Set/get stored descent
     void SetDescent(int descent) { m_descent = descent; }
@@ -943,11 +1104,25 @@ public:
 
     /// Convert units in tenths of a millimetre to device units
     int ConvertTenthsMMToPixels(wxDC& dc, int units) const;
-    static int ConvertTenthsMMToPixels(int ppi, int units);
+    static int ConvertTenthsMMToPixels(int ppi, int units, double scale = 1.0);
 
     /// Convert units in pixels to tenths of a millimetre
     int ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const;
-    static int ConvertPixelsToTenthsMM(int ppi, int pixels);
+    static int ConvertPixelsToTenthsMM(int ppi, int pixels, double scale = 1.0);
+    
+    /// Draw the borders and background for the given rectangle and attributes.
+    /// Width and height are taken to be the content size, so excluding any
+    /// border, margin and padding.
+    static bool DrawBoxAttributes(wxDC& dc, const wxRichTextAttr& attr, const wxRect& boxRect);
+
+    /// Draw a border
+    static bool DrawBorder(wxDC& dc, const wxTextAttrBorders& attr, const wxRect& rect);
+
+    /// Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
+    /// or marginRect (outer), and the other must be the default rectangle (no width or height).
+    /// Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
+    /// is available.
+    static bool GetBoxRects(wxDC& dc, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect);
 
 protected:
     wxSize                  m_size;
@@ -968,6 +1143,9 @@ protected:
 
     /// Attributes
     wxRichTextAttr          m_attributes;
+    
+    /// Properties
+    wxRichTextProperties    m_properties;
 };
 
 WX_DECLARE_LIST_WITH_DECL( wxRichTextObject, wxRichTextObjectList, class WXDLLIMPEXP_RICHTEXT );
@@ -1056,58 +1234,19 @@ protected:
     wxRichTextObjectList    m_children;
 };
 
-/*!
- * wxRichTextBox class declaration
- * This defines a 2D space to lay out objects
- */
-
-class WXDLLIMPEXP_RICHTEXT wxRichTextBox: public wxRichTextCompositeObject
-{
-    DECLARE_DYNAMIC_CLASS(wxRichTextBox)
-public:
-// Constructors
-
-    wxRichTextBox(wxRichTextObject* parent = NULL);
-    wxRichTextBox(const wxRichTextBox& obj): wxRichTextCompositeObject() { Copy(obj); }
-
-// Overrideables
-
-    /// Draw the item
-    virtual bool Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int style);
-
-    /// Lay the item out
-    virtual bool Layout(wxDC& dc, const wxRect& rect, int style);
-
-    /// Get/set the object size for the given range. Returns false if the range
-    /// is invalid for this object.
-    virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const;
-
-// Accessors
-
-// Operations
-
-    /// Clone
-    virtual wxRichTextObject* Clone() const { return new wxRichTextBox(*this); }
-
-    /// Copy
-    void Copy(const wxRichTextBox& obj);
-
-protected:
-};
-
 /*!
  * wxRichTextParagraphBox class declaration
  * This box knows how to lay out paragraphs.
  */
 
-class WXDLLIMPEXP_RICHTEXT wxRichTextParagraphLayoutBox: public wxRichTextBox
+class WXDLLIMPEXP_RICHTEXT wxRichTextParagraphLayoutBox: public wxRichTextCompositeObject
 {
     DECLARE_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox)
 public:
 // Constructors
 
     wxRichTextParagraphLayoutBox(wxRichTextObject* parent = NULL);
-    wxRichTextParagraphLayoutBox(const wxRichTextParagraphLayoutBox& obj): wxRichTextBox() { Init(); Copy(obj); }
+    wxRichTextParagraphLayoutBox(const wxRichTextParagraphLayoutBox& obj): wxRichTextCompositeObject() { Init(); Copy(obj); }
     ~wxRichTextParagraphLayoutBox();
 
 // Overrideables
@@ -1132,6 +1271,24 @@ public:
     /// Get any text in this object for the given range
     virtual wxString GetTextForRange(const wxRichTextRange& range) const;
 
+#if wxUSE_XML
+    /// Import this object from XML
+    virtual bool ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler);
+#endif
+
+#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
+    /// Export this object directly to the given stream.
+    virtual bool ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler);
+#endif
+
+#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
+    /// Export this object to the given parent node, usually creating at least one child node.
+    virtual bool ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler);
+#endif
+
+    /// What is the XML node name of this object?
+    virtual wxString GetXMLNodeName() const { return wxT("paragraphlayout"); }
+
 // Accessors
 
     /// Associate a control with the buffer, for operations that for example require refreshing the window.
@@ -1153,7 +1310,7 @@ public:
     void DrawFloats(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int style);
 
     /// Move an anchored object to another paragraph
-    void MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextAnchoredObject* obj);
+    void MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj);
 
     /// Initialize the object.
     void Init();
@@ -1351,6 +1508,41 @@ protected:
     wxRichTextFloatCollector* m_floatCollector;
 };
 
+/*!
+ * wxRichTextBox class declaration
+ * TODO: a floating text box
+ */
+
+class WXDLLIMPEXP_RICHTEXT wxRichTextBox: public wxRichTextParagraphLayoutBox
+{
+    DECLARE_DYNAMIC_CLASS(wxRichTextBox)
+public:
+// Constructors
+
+    wxRichTextBox(wxRichTextObject* parent = NULL);
+    wxRichTextBox(const wxRichTextBox& obj): wxRichTextParagraphLayoutBox() { Copy(obj); }
+
+// Overrideables
+
+    /// Draw the item
+    virtual bool Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int style);
+
+    /// Lay the item out
+    virtual bool Layout(wxDC& dc, const wxRect& rect, int style);
+
+// Accessors
+
+// Operations
+
+    /// Clone
+    virtual wxRichTextObject* Clone() const { return new wxRichTextBox(*this); }
+
+    /// Copy
+    void Copy(const wxRichTextBox& obj);
+
+protected:
+};
+
 /*!
  * wxRichTextLine class declaration
  * This object represents a line in a paragraph, and stores
@@ -1480,6 +1672,9 @@ public:
     /// Calculate range
     virtual void CalculateRange(long start, long& end);
 
+    /// What is the XML node name of this object?
+    virtual wxString GetXMLNodeName() const { return wxT("paragraph"); }
+
 // Accessors
 
     /// Get the cached lines
@@ -1621,6 +1816,27 @@ public:
     /// Get the first position from pos that has a line break character.
     long GetFirstLineBreakPosition(long pos);
 
+    /// Does this object take note of paragraph attributes? Text and image objects don't.
+    virtual bool UsesParagraphAttributes() const { return false; }
+
+#if wxUSE_XML
+    /// Import this object from XML
+    virtual bool ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler);
+#endif
+
+#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
+    /// Export this object directly to the given stream.
+    virtual bool ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler);
+#endif
+
+#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
+    /// Export this object to the given parent node, usually creating at least one child node.
+    virtual bool ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler);
+#endif
+
+    /// What is the XML node name of this object?
+    virtual wxString GetXMLNodeName() const { return wxT("text"); }
+
 // Accessors
 
     /// Get the text
@@ -1732,56 +1948,21 @@ protected:
     wxBitmapType        m_imageType;
 };
 
-/*!
- * wxRichTextAnchoredObject class declaration
- * This object is an abstract one that represent some objects which can floats
- */
-class WXDLLIMPEXP_RICHTEXT wxRichTextAnchoredObject: public wxRichTextObject
-{
-    DECLARE_CLASS(wxRichTextAnchoredObject)
-public:
-// Constructors
-    wxRichTextAnchoredObject(wxRichTextObject* parent = NULL, const wxRichTextAttr& attr = wxRichTextAttr());
-    wxRichTextAnchoredObject(const wxRichTextAnchoredObject& obj) : wxRichTextObject(obj) /* , m_ph(NULL) */ { Copy(obj); }
-    ~wxRichTextAnchoredObject();
-
-// Virtuals
-    virtual bool IsFloatable() const { return true; }
-
-    /// Whether this object is currently floating
-    virtual bool IsFloating() const { return GetAttributes().GetTextBoxAttr().IsFloating(); }
-
-    virtual void SetParent(wxRichTextObject* parent);
-
-// Accessors
-
-    /// The floating direction
-    virtual int GetFloatDirection() const { return GetAttributes().GetTextBoxAttr().GetFloatMode(); }
-
-    void operator=(const wxRichTextAnchoredObject&) { wxASSERT("Nobody can reset this object using ="); }
-
-// Functions
-    void Copy(const wxRichTextAnchoredObject& obj);
-
-protected:
-
-};
-
 /*!
  * wxRichTextImage class declaration
  * This object represents an image.
  */
 
-class WXDLLIMPEXP_RICHTEXT wxRichTextImage: public wxRichTextAnchoredObject
+class WXDLLIMPEXP_RICHTEXT wxRichTextImage: public wxRichTextObject
 {
     DECLARE_DYNAMIC_CLASS(wxRichTextImage)
 public:
 // Constructors
 
-    wxRichTextImage(wxRichTextObject* parent = NULL): wxRichTextAnchoredObject(parent) { }
+    wxRichTextImage(wxRichTextObject* parent = NULL): wxRichTextObject(parent) { }
     wxRichTextImage(const wxImage& image, wxRichTextObject* parent = NULL, wxRichTextAttr* charStyle = NULL);
     wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent = NULL, wxRichTextAttr* charStyle = NULL);
-    wxRichTextImage(const wxRichTextImage& obj): wxRichTextAnchoredObject(obj) { Copy(obj); }
+    wxRichTextImage(const wxRichTextImage& obj): wxRichTextObject(obj) { Copy(obj); }
 
 // Overrideables
 
@@ -1795,8 +1976,8 @@ public:
     /// is invalid for this object.
     virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const;
 
-    /// Returns true if the object is empty
-    virtual bool IsEmpty() const { return !m_imageBlock.Ok(); }
+    /// Returns true if the object is empty. An image is never empty; if the image is broken, that's not the same thing as empty.
+    virtual bool IsEmpty() const { return false; /* !m_imageBlock.Ok(); */ }
 
     /// Can we edit properties via a GUI?
     virtual bool CanEditProperties() const { return true; }
@@ -1804,6 +1985,30 @@ public:
     /// Edit properties via a GUI
     virtual bool EditProperties(wxWindow* parent, wxRichTextBuffer* buffer);
 
+    /// Does this object take note of paragraph attributes? Text and image objects don't.
+    virtual bool UsesParagraphAttributes() const { return false; }
+
+#if wxUSE_XML
+    /// Import this object from XML
+    virtual bool ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler);
+#endif
+
+#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
+    /// Export this object directly to the given stream.
+    virtual bool ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler);
+#endif
+
+#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
+    /// Export this object to the given parent node, usually creating at least one child node.
+    virtual bool ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler);
+#endif
+
+    // Images can be floatable (optionally).
+    virtual bool IsFloatable() const { return true; }
+
+    /// What is the XML node name of this object?
+    virtual wxString GetXMLNodeName() const { return wxT("image"); }
+
 // Accessors
 
     /// Get the image cache (scaled bitmap)
@@ -2086,6 +2291,9 @@ public:
     /// Copy
     void Copy(const wxRichTextBuffer& obj);
 
+    /// Assignment
+    void operator= (const wxRichTextBuffer& obj) { Copy(obj); }
+
     /// Clone
     virtual wxRichTextObject* Clone() const { return new wxRichTextBuffer(*this); }
 
index ca114c748cc1cecb6ccf3e529526509d9d308785..a14e397bde663d18ba2c4cee52e129ab578172d4 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 class WXDLLIMPEXP_FWD_XML wxXmlNode;
+class WXDLLIMPEXP_FWD_XML wxXmlDocument;
 
 class WXDLLIMPEXP_RICHTEXT wxRichTextXMLHandler: public wxRichTextFileHandler
 {
@@ -33,30 +34,59 @@ class WXDLLIMPEXP_RICHTEXT wxRichTextXMLHandler: public wxRichTextFileHandler
 public:
     wxRichTextXMLHandler(const wxString& name = wxT("XML"), const wxString& ext = wxT("xml"), int type = wxRICHTEXT_TYPE_XML)
         : wxRichTextFileHandler(name, ext, type)
-        { }
+        { Init(); }
+        
+    void Init();
 
 #if wxUSE_STREAMS
+
+#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
     /// Recursively export an object
-    bool ExportXML(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextObject& obj, int level);
-    bool ExportStyleDefinition(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextStyleDefinition* def, int level);
+    bool ExportXML(wxOutputStream& stream, wxRichTextObject& obj, int level);
+    bool ExportStyleDefinition(wxOutputStream& stream, wxRichTextStyleDefinition* def, int level);
+    wxString AddAttributes(const wxRichTextAttr& attr, bool isPara = false);
+    bool WriteProperties(wxOutputStream& stream, const wxRichTextProperties& properties, int level);
+    void OutputString(wxOutputStream& stream, const wxString& str);
+    void OutputStringEnt(wxOutputStream& stream, const wxString& str);
+    void OutputIndentation(wxOutputStream& stream, int indent);
+    static wxString AttributeToXML(const wxString& str);
+#endif
+
+#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
+    bool ExportXML(wxXmlNode* parent, wxRichTextObject& obj);
+    bool ExportStyleDefinition(wxXmlNode* parent, wxRichTextStyleDefinition* def);
+    bool AddAttributes(wxXmlNode* node, wxRichTextAttr& attr, bool isPara = false);
+    bool WriteProperties(wxXmlNode* node, const wxRichTextProperties& properties);
+#endif
+
+    /// Make a string from the given property. This can be overridden for custom variants.
+    virtual wxString MakeStringFromProperty(const wxVariant& var);
+
+    /// Create a proprty from the string read from the XML file.
+    virtual wxVariant MakePropertyFromString(const wxString& name, const wxString& value, const wxString& type);
 
     /// Recursively import an object
-    bool ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node);
+    bool ImportXML(wxRichTextBuffer* buffer, wxRichTextObject* obj, wxXmlNode* node);
     bool ImportStyleDefinition(wxRichTextStyleSheet* sheet, wxXmlNode* node);
+    bool ImportProperties(wxRichTextObject* obj, wxXmlNode* node);
 
-    /// Create style parameters
-    wxString CreateStyle(const wxRichTextAttr& attr, bool isPara = false);
-
-    /// Get style parameters
-    bool GetStyle(wxRichTextAttr& attr, wxXmlNode* node, bool isPara = false);
+    /// Import style parameters
+    bool ImportStyle(wxRichTextAttr& attr, wxXmlNode* node, bool isPara = false);
 #endif
 
+    /// Creates an object given an XML element name
+    virtual wxRichTextObject* CreateObjectForXMLName(wxRichTextObject* parent, const wxString& name) const;
+    
     /// Can we save using this handler?
     virtual bool CanSave() const { return true; }
 
     /// Can we load using this handler?
     virtual bool CanLoad() const { return true; }
 
+    // Used during saving
+    wxMBConv* GetConvMem() const { return m_convMem; }
+    wxMBConv* GetConvFile() const { return m_convFile; }
+
 // Implementation
 
     bool HasParam(wxXmlNode* node, const wxString& param);
@@ -70,6 +100,12 @@ protected:
     virtual bool DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream);
     virtual bool DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream);
 #endif
+
+#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
+    // Used during saving
+    wxMBConv* m_convMem;
+    wxMBConv* m_convFile;
+#endif
 };
 
 #endif
index e45a90b288723eacb27645fa1f1a9598fd43338f..1c5b0b97b72bf759a160f34681ab180b97eeaaa2 100644 (file)
@@ -34,6 +34,7 @@
 #include "wx/splitter.h"
 #include "wx/sstream.h"
 #include "wx/html/htmlwin.h"
+#include "wx/stopwatch.h"
 
 #if wxUSE_FILESYSTEM
 #include "wx/filesys.h"
@@ -1071,7 +1072,14 @@ void MyFrame::OnSaveAs(wxCommandEvent& WXUNUSED(event))
 
         if (!path.empty())
         {
+            wxBusyCursor busy;
+            wxStopWatch stopwatch;
+
             m_richTextCtrl->SaveFile(path);
+
+            long t = stopwatch.Time();
+            wxLogDebug(wxT("Saving took %ldms"), t);
+            wxMessageBox(wxString::Format(wxT("Saving took %ldms"), t));
         }
     }
 }
index af17e754620a24f3f9b3ba47c6a6bc1bc6ce0546..86be3e8394eb7a27886e4e384c2519bfb8517f5b 100644 (file)
@@ -43,6 +43,7 @@
 #include "wx/richtext/richtextimagedlg.h"
 
 #include "wx/listimpl.cpp"
+#include "wx/arrimpl.cpp"
 
 WX_DEFINE_LIST(wxRichTextObjectList)
 WX_DEFINE_LIST(wxRichTextLineList)
@@ -270,11 +271,7 @@ void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
 
         if (floating->IsFloating())
         {
-            wxRichTextAnchoredObject* anchor = wxDynamicCast(floating, wxRichTextAnchoredObject);
-            if (anchor)
-            {
-                CollectFloat(para, floating);
-            }
+            CollectFloat(para, floating);
         }
 
         node = node->GetNext();
@@ -467,6 +464,7 @@ void wxRichTextObject::Copy(const wxRichTextObject& obj)
     m_dirty = obj.m_dirty;
     m_range = obj.m_range;
     m_attributes = obj.m_attributes;
+    m_properties = obj.m_properties;
     m_descent = obj.m_descent;
 }
 
@@ -486,21 +484,23 @@ void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin
 // Convert units in tenths of a millimetre to device units
 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
 {
-    int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units);
-
     // Unscale
-    wxRichTextBuffer* buffer = GetBuffer();
-    if (buffer)
-        p = (int) ((double)p / buffer->GetScale());
+    double scale = 1.0;
+    if (GetBuffer())
+        scale = GetBuffer()->GetScale();
+    int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
+
     return p;
 }
 
 // Convert units in tenths of a millimetre to device units
-int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units)
+int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
 {
     // There are ppi pixels in 254.1 "1/10 mm"
 
     double pixels = ((double) units * (double)ppi) / 254.1;
+    if (scale != 1.0)
+        pixels /= scale;
 
     return (int) pixels;
 }
@@ -509,19 +509,272 @@ int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units)
 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
 {
     int p = pixels;
-    if (GetBuffer() && GetBuffer()->GetScale() != 1.0)
-        p = (int) (double(p) * GetBuffer()->GetScale());
-    return ConvertPixelsToTenthsMM(dc.GetPPI().x, p);
+    double scale = 1.0;
+    if (GetBuffer())
+        scale = GetBuffer()->GetScale();
+
+    return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
 }
 
-int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels)
+int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
 {
     // There are ppi pixels in 254.1 "1/10 mm"
-    
-    int units = int( double(pixels) * 254.1 / (double) ppi );
+
+    double p = double(pixels);
+
+    if (scale != 1.0)
+        p *= scale;
+
+    int units = int( p * 254.1 / (double) ppi );
     return units;
 }
 
+// Draw the borders and background for the given rectangle and attributes.
+// Width and height are taken to be the content size, so excluding any
+// border, margin and padding.
+bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, const wxRichTextAttr& attr, const wxRect& boxRect)
+{
+    // Assume boxRect is the area around the content
+    wxRect contentRect = boxRect;
+    wxRect marginRect, borderRect, paddingRect, outlineRect;
+
+    GetBoxRects(dc, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
+
+    // Margin is transparent. Draw background from margin.
+    if (attr.HasBackgroundColour())
+    {
+        wxPen pen(attr.GetBackgroundColour());
+        wxBrush brush(attr.GetBackgroundColour());
+
+        dc.SetPen(pen);
+        dc.SetBrush(brush);
+        dc.DrawRectangle(marginRect);
+    }
+
+    if (attr.GetTextBoxAttr().GetBorder().HasBorder())
+        DrawBorder(dc, attr.GetTextBoxAttr().GetBorder(), borderRect);
+
+    if (attr.GetTextBoxAttr().GetOutline().HasBorder())
+        DrawBorder(dc, attr.GetTextBoxAttr().GetOutline(), outlineRect);
+
+    return true;
+}
+
+// Draw a border
+bool wxRichTextObject::DrawBorder(wxDC& dc, const wxTextAttrBorders& attr, const wxRect& rect)
+{
+    int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
+    wxTextAttrDimensionConverter converter(dc);
+
+    if (attr.GetLeft().IsValid())
+    {
+        borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
+        wxColour col(attr.GetLeft().GetColour());
+
+        // If pen width is > 1, resorts to a solid rectangle.
+        if (borderLeft == 1)
+        {
+            int penStyle = wxSOLID;
+            if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
+                penStyle = wxDOT;
+            else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
+                penStyle = wxLONG_DASH;
+            wxPen pen(col);
+            dc.SetPen(pen);
+            dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
+
+        }
+        else if (borderLeft > 1)
+        {
+            wxPen pen(col);
+            wxBrush brush(col);
+            dc.SetPen(pen);
+            dc.SetBrush(brush);
+            dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
+        }
+    }
+
+    if (attr.GetRight().IsValid())
+    {
+        borderRight = converter.GetPixels(attr.GetRight().GetWidth());
+
+        wxColour col(attr.GetRight().GetColour());
+
+        // If pen width is > 1, resorts to a solid rectangle.
+        if (borderRight == 1)
+        {
+            int penStyle = wxSOLID;
+            if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
+                penStyle = wxDOT;
+            else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
+                penStyle = wxLONG_DASH;
+            wxPen pen(col);
+            dc.SetPen(pen);
+            dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height);
+
+        }
+        else if (borderRight > 1)
+        {
+            wxPen pen(col);
+            wxBrush brush(col);
+            dc.SetPen(pen);
+            dc.SetBrush(brush);
+            dc.DrawRectangle(rect.x - borderRight, rect.y, borderRight, rect.height);
+        }
+    }
+
+    if (attr.GetTop().IsValid())
+    {
+        borderTop = converter.GetPixels(attr.GetTop().GetWidth());
+
+        wxColour col(attr.GetTop().GetColour());
+
+        // If pen width is > 1, resorts to a solid rectangle.
+        if (borderTop == 1)
+        {
+            int penStyle = wxSOLID;
+            if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
+                penStyle = wxDOT;
+            else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
+                penStyle = wxLONG_DASH;
+            wxPen pen(col);
+            dc.SetPen(pen);
+            dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
+
+        }
+        else if (borderTop > 1)
+        {
+            wxPen pen(col);
+            wxBrush brush(col);
+            dc.SetPen(pen);
+            dc.SetBrush(brush);
+            dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
+        }
+    }
+
+    if (attr.GetBottom().IsValid())
+    {
+        borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
+        wxColour col(attr.GetTop().GetColour());
+
+        // If pen width is > 1, resorts to a solid rectangle.
+        if (borderBottom == 1)
+        {
+            int penStyle = wxSOLID;
+            if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
+                penStyle = wxDOT;
+            else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
+                penStyle = wxLONG_DASH;
+            wxPen pen(col);
+            dc.SetPen(pen);
+            dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
+
+        }
+        else if (borderBottom > 1)
+        {
+            wxPen pen(col);
+            wxBrush brush(col);
+            dc.SetPen(pen);
+            dc.SetBrush(brush);
+            dc.DrawRectangle(rect.x, rect.y - rect.height - borderBottom, rect.width, borderBottom);
+        }
+    }
+
+    return true;
+}
+
+// Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
+// or marginRect (outer), and the other must be the default rectangle (no width or height).
+// Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
+// is available.
+//
+// | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
+
+bool wxRichTextObject::GetBoxRects(wxDC& dc, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
+{
+    int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
+    int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
+    int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
+    int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
+
+    wxTextAttrDimensionConverter converter(dc);
+
+    if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsPresent())
+        marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
+    if (attr.GetTextBoxAttr().GetMargins().GetRight().IsPresent())
+        marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
+    if (attr.GetTextBoxAttr().GetMargins().GetTop().IsPresent())
+        marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
+    if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsPresent())
+        marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
+
+    if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsPresent())
+        borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
+    if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsPresent())
+        borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
+    if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsPresent())
+        borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
+    if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsPresent())
+        borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
+
+    if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsPresent())
+        paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
+    if (attr.GetTextBoxAttr().GetPadding().GetRight().IsPresent())
+        paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
+    if (attr.GetTextBoxAttr().GetPadding().GetTop().IsPresent())
+        paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
+    if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsPresent())
+        paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
+
+    if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsPresent())
+        outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
+    if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsPresent())
+        outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
+    if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsPresent())
+        outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
+    if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsPresent())
+        outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
+
+    int leftTotal = marginLeft + borderLeft + paddingLeft;
+    int rightTotal = marginRight + borderRight + paddingRight;
+    int topTotal = marginTop + borderTop + paddingTop;
+    int bottomTotal = marginBottom + borderBottom + paddingBottom;
+
+    if (marginRect != wxRect())
+    {
+        contentRect.x = marginRect.x + leftTotal;
+        contentRect.y = marginRect.y + topTotal;
+        contentRect.width = marginRect.width - (leftTotal + rightTotal);
+        contentRect.height = marginRect.height - (topTotal + bottomTotal);
+    }
+    else
+    {
+        marginRect.x = contentRect.x - leftTotal;
+        marginRect.y = contentRect.y - topTotal;
+        marginRect.width = contentRect.width + (leftTotal + rightTotal);
+        marginRect.height = contentRect.height + (topTotal + bottomTotal);
+    }
+
+    borderRect.x = marginRect.x + marginLeft;
+    borderRect.y = marginRect.y + marginTop;
+    borderRect.width = marginRect.width - (marginLeft + marginRight);
+    borderRect.height = marginRect.height - (marginTop + marginBottom);
+
+    paddingRect.x = marginRect.x + marginLeft + borderLeft;
+    paddingRect.y = marginRect.y + marginTop + borderTop;
+    paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
+    paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
+
+    // The outline is outside the margin and doesn't influence the overall box position or content size.
+    outlineRect.x = marginRect.x - outlineLeft;
+    outlineRect.y = marginRect.y - outlineTop;
+    outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
+    outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
+
+    return true;
+}
+
+
 /// Dump to output stream for debugging
 void wxRichTextObject::Dump(wxTextOutputStream& stream)
 {
@@ -808,94 +1061,52 @@ bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range)
             node = node->GetNext();
     }
 
-    return true;
-}
-
-/// Dump to output stream for debugging
-void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
-{
-    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
-    while (node)
+    // Delete any remaining empty objects, but leave at least one empty object per composite object.
+    if (GetChildCount() > 1)
     {
-        wxRichTextObject* child = node->GetData();
-        child->Dump(stream);
-        node = node->GetNext();
+        node = m_children.GetFirst();
+        while (node)
+        {
+            wxRichTextObjectList::compatibility_iterator next = node->GetNext();
+            wxRichTextObject* child = node->GetData();
+            if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
+            {
+                if (child->IsEmpty())
+                {
+                    child->Dereference();
+                    m_children.Erase(node);
+                }
+                node = next;
+            }
+            else
+                node = node->GetNext();
+        }
     }
-}
-
 
-/*!
- * wxRichTextBox
- * This defines a 2D space to lay out objects
- */
-
-IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextCompositeObject)
-
-wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
-    wxRichTextCompositeObject(parent)
-{
-}
-
-/// Draw the item
-bool wxRichTextBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& WXUNUSED(rect), int descent, int style)
-{
-    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
-    while (node)
-    {
-        wxRichTextObject* child = node->GetData();
-
-        wxRect childRect = wxRect(child->GetPosition(), child->GetCachedSize());
-        child->Draw(dc, range, selectionRange, childRect, descent, style);
-
-        node = node->GetNext();
-    }
     return true;
 }
 
-/// Lay the item out
-bool wxRichTextBox::Layout(wxDC& dc, const wxRect& rect, int style)
+/// Dump to output stream for debugging
+void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
 {
     wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
     while (node)
     {
         wxRichTextObject* child = node->GetData();
-        child->Layout(dc, rect, style);
-
+        child->Dump(stream);
         node = node->GetNext();
     }
-    m_dirty = false;
-    return true;
-}
-
-/// Get/set the size for the given range. Assume only has one child.
-bool wxRichTextBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const
-{
-    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
-    if (node)
-    {
-        wxRichTextObject* child = node->GetData();
-        return child->GetRangeSize(range, size, descent, dc, flags, position, partialExtents);
-    }
-    else
-        return false;
 }
 
-/// Copy
-void wxRichTextBox::Copy(const wxRichTextBox& obj)
-{
-    wxRichTextCompositeObject::Copy(obj);
-}
-
-
 /*!
  * wxRichTextParagraphLayoutBox
  * This box knows how to lay out paragraphs.
  */
 
-IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextBox)
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
 
 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
-    wxRichTextBox(parent)
+    wxRichTextCompositeObject(parent)
 {
     Init();
 }
@@ -926,6 +1137,27 @@ void wxRichTextParagraphLayoutBox::Init()
     m_floatCollector = NULL;
 }
 
+void wxRichTextParagraphLayoutBox::Clear()
+{
+    DeleteChildren();
+
+    if (m_floatCollector)
+        delete m_floatCollector;
+    m_floatCollector = NULL;
+    m_partialParagraph = false;
+}
+
+/// Copy
+void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
+{
+    Clear();
+
+    wxRichTextCompositeObject::Copy(obj);
+
+    m_partialParagraph = obj.m_partialParagraph;
+    m_defaultAttributes = obj.m_defaultAttributes;
+}
+
 // Gather information about floating objects
 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(int width, wxRichTextObject* untilObj)
 {
@@ -965,7 +1197,7 @@ void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, const wxRichTextRange& r
         m_floatCollector->Draw(dc, range, selectionRange, rect, descent, style);
 }
 
-void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextAnchoredObject* obj)
+void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
 {
     if (from == to)
         return;
@@ -1150,15 +1382,6 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int styl
     return true;
 }
 
-/// Copy
-void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
-{
-    wxRichTextBox::Copy(obj);
-
-    m_partialParagraph = obj.m_partialParagraph;
-    m_defaultAttributes = obj.m_defaultAttributes;
-}
-
 /// Get/set the size for the given range.
 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const
 {
@@ -2309,7 +2532,7 @@ void wxRichTextParagraphLayoutBox::SetImageStyle(wxRichTextImage *image, const w
         action->SetRange(image->GetRange().FromInternal());
         action->SetPosition(GetRichTextCtrl()->GetCaretPosition());
         image->SetAttributes(textAttr);
-        
+
         // Set the new attribute
         newPara = new wxRichTextParagraph(*para);
         action->GetNewParagraphs().AppendChild(newPara);
@@ -2548,11 +2771,6 @@ bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange&
     return foundCount == matchingCount && foundCount != 0;
 }
 
-void wxRichTextParagraphLayoutBox::Clear()
-{
-    DeleteChildren();
-}
-
 void wxRichTextParagraphLayoutBox::Reset()
 {
     Clear();
@@ -3766,7 +3984,7 @@ bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
 
 void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
 {
-    wxRichTextBox::Copy(obj);
+    wxRichTextCompositeObject::Copy(obj);
 }
 
 /// Clear the cached lines
@@ -4553,13 +4771,13 @@ void wxRichTextParagraph::LayoutFloat(wxDC& dc, const wxRect& rect, int style, w
     wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
     while (node)
     {
-        wxRichTextAnchoredObject* anchored = wxDynamicCast(node->GetData(), wxRichTextAnchoredObject);
+        wxRichTextObject* anchored = node->GetData();
         if (anchored && anchored->IsFloating())
         {
             wxSize size;
             int descent, x = 0;
             anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, style);
-            
+
             int offsetY = 0;
             if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsPresent())
             {
@@ -4569,7 +4787,7 @@ void wxRichTextParagraph::LayoutFloat(wxDC& dc, const wxRect& rect, int style, w
                     offsetY = ConvertTenthsMMToPixels(dc, offsetY);
                 }
             }
-            
+
             int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
 
             /* Update the offset */
@@ -4580,9 +4798,6 @@ void wxRichTextParagraph::LayoutFloat(wxDC& dc, const wxRect& rect, int style, w
                     newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
                 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
             }
-            
-            // attr.m_offset = pos - rect.y;
-            //anchored->SetAnchoredAttr(attr);
 
             if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
                 x = 0;
@@ -5335,8 +5550,10 @@ void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
 
     m_styleSheet = obj.m_styleSheet;
     m_modified = obj.m_modified;
-    m_batchedCommandDepth = obj.m_batchedCommandDepth;
-    m_batchedCommand = obj.m_batchedCommand;
+    m_batchedCommandDepth = 0;
+    if (m_batchedCommand)
+        delete m_batchedCommand;
+    m_batchedCommand = NULL;
     m_suppressUndo = obj.m_suppressUndo;
 }
 
@@ -5544,7 +5761,6 @@ bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock&
     wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
     newPara->AppendChild(imageObject);
     imageObject->SetAttributes(textAttr);
-    //imageObject->SetAnchoredAttr(floatAttr);
     action->GetNewParagraphs().AppendChild(newPara);
     action->GetNewParagraphs().UpdateRanges();
 
@@ -6643,6 +6859,35 @@ bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNa
     return true;
 }
 
+/*!
+ * wxRichTextBox
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextCompositeObject)
+
+wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
+    wxRichTextParagraphLayoutBox(parent)
+{
+}
+
+/// Draw the item
+bool wxRichTextBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int style)
+{
+    return wxRichTextParagraphLayoutBox::Draw(dc, range, selectionRange, rect, descent, style);
+}
+
+/// Lay the item out
+bool wxRichTextBox::Layout(wxDC& dc, const wxRect& rect, int style)
+{
+    return wxRichTextParagraphLayoutBox::Layout(dc, rect, style);
+}
+
+/// Copy
+void wxRichTextBox::Copy(const wxRichTextBox& obj)
+{
+    wxRichTextParagraphLayoutBox::Copy(obj);
+}
+
 /*
  * Module to initialise and clean up handlers
  */
@@ -7075,40 +7320,15 @@ bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
     return true;
 }
 
-/*!
- * wxRichTextAnchoredObject implementation
- */
-IMPLEMENT_CLASS(wxRichTextAnchoredObject, wxRichTextObject)
-
-wxRichTextAnchoredObject::wxRichTextAnchoredObject(wxRichTextObject* parent, const wxRichTextAttr& attr):
-    wxRichTextObject(parent)
-{
-    SetAttributes(attr);
-}
-
-wxRichTextAnchoredObject::~wxRichTextAnchoredObject()
-{
-}
-
-void wxRichTextAnchoredObject::Copy(const wxRichTextAnchoredObject& obj)
-{
-    wxRichTextObject::Copy(obj);
-}
-
-void wxRichTextAnchoredObject::SetParent(wxRichTextObject* parent)
-{
-    wxRichTextObject::SetParent(parent);
-}
-
 /*!
  * wxRichTextImage implementation
  * This object represents an image.
  */
 
-IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextAnchoredObject)
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
 
 wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
-    wxRichTextAnchoredObject(parent)
+    wxRichTextObject(parent)
 {
     m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
     if (charStyle)
@@ -7116,7 +7336,7 @@ wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent,
 }
 
 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
-    wxRichTextAnchoredObject(parent)
+    wxRichTextObject(parent)
 {
     m_imageBlock = imageBlock;
     if (charStyle)
@@ -7138,7 +7358,7 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache)
 
         int width = image.GetWidth();
         int height = image.GetHeight();
-        
+
         if (GetAttributes().GetTextBoxAttr().GetWidth().IsPresent() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
         {
             if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
@@ -7243,7 +7463,7 @@ bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, i
 /// Copy
 void wxRichTextImage::Copy(const wxRichTextImage& obj)
 {
-    wxRichTextAnchoredObject::Copy(obj);
+    wxRichTextObject::Copy(obj);
 
     m_imageBlock = obj.m_imageBlock;
 }
@@ -7964,7 +8184,7 @@ void wxTextBoxAttr::Reset()
     m_floatMode = 0;
     m_clearMode = 0;
     m_collapseMode = 0;
-    
+
     m_margins.Reset();
     m_padding.Reset();
     m_position.Reset();
@@ -7984,13 +8204,13 @@ bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
         m_floatMode == attr.m_floatMode &&
         m_clearMode == attr.m_clearMode &&
         m_collapseMode == attr.m_collapseMode &&
-        
+
         m_margins == attr.m_margins &&
         m_padding == attr.m_padding &&
         m_position == attr.m_position &&
 
         m_width == attr.m_width &&
-        m_height == attr.m_height &&   
+        m_height == attr.m_height &&
 
         m_border == attr.m_border &&
         m_outline == attr.m_outline
@@ -8059,16 +8279,16 @@ bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compar
         if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
             SetCollapseBorders(true);
     }
-    
-    m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextBoxAttrDimensions*) NULL);
-    m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextBoxAttrDimensions*) NULL);
-    m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextBoxAttrDimensions*) NULL);
+
+    m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
+    m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
+    m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
 
     m_width.Apply(attr.m_width, compareWith ? (& attr.m_width) : (const wxTextAttrDimension*) NULL);
     m_height.Apply(attr.m_height, compareWith ? (& attr.m_height) : (const wxTextAttrDimension*) NULL);
 
-    m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextBoxAttrBorders*) NULL);
-    m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextBoxAttrBorders*) NULL);
+    m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
+    m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
 
     return true;
 }
@@ -8122,7 +8342,7 @@ void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBox
     }
     else
         absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
-        
+
     if (attr.HasClearMode())
     {
         if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
@@ -8160,7 +8380,7 @@ void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBox
     }
     else
         absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
-        
+
     m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
     m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
     m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
@@ -8176,8 +8396,8 @@ void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBox
 
 void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
 {
-    wxTextAttr::Copy(attr); 
-    
+    wxTextAttr::Copy(attr);
+
     m_textBoxAttr = attr.m_textBoxAttr;
 }
 
@@ -8185,7 +8405,7 @@ bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
 {
     if (!(wxTextAttr::operator==(attr)))
         return false;
-        
+
     return (m_textBoxAttr == attr.m_textBoxAttr);
 }
 
@@ -8194,7 +8414,7 @@ bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr) const
 {
     if (!(wxTextAttr::EqPartial(attr)))
         return false;
-        
+
     return m_textBoxAttr.EqPartial(attr.m_textBoxAttr);
 }
 
@@ -8221,12 +8441,12 @@ bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
 {
     wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
-    
+
     m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
 }
 
 // Partial equality test
-bool wxTextBoxAttrBorder::EqPartial(const wxTextBoxAttrBorder& border) const
+bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border) const
 {
     if (border.HasStyle() && !HasStyle() && (border.GetStyle() != GetStyle()))
         return false;
@@ -8241,7 +8461,7 @@ bool wxTextBoxAttrBorder::EqPartial(const wxTextBoxAttrBorder& border) const
 }
 
 // Apply border to 'this', but not if the same as compareWith
-bool wxTextBoxAttrBorder::Apply(const wxTextBoxAttrBorder& border, const wxTextBoxAttrBorder* compareWith)
+bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
 {
     if (border.HasStyle())
     {
@@ -8263,7 +8483,7 @@ bool wxTextBoxAttrBorder::Apply(const wxTextBoxAttrBorder& border, const wxTextB
 }
 
 // Remove specified attributes from this object
-bool wxTextBoxAttrBorder::RemoveStyle(const wxTextBoxAttrBorder& attr)
+bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
 {
     if (attr.HasStyle() && HasStyle())
         SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
@@ -8277,7 +8497,7 @@ bool wxTextBoxAttrBorder::RemoveStyle(const wxTextBoxAttrBorder& attr)
 
 // Collects the attributes that are common to a range of content, building up a note of
 // which attributes are absent in some objects and which clash in some objects.
-void wxTextBoxAttrBorder::CollectCommonAttributes(const wxTextBoxAttrBorder& attr, wxTextBoxAttrBorder& clashingAttr, wxTextBoxAttrBorder& absentAttr)
+void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
 {
     if (attr.HasStyle())
     {
@@ -8316,29 +8536,29 @@ void wxTextBoxAttrBorder::CollectCommonAttributes(const wxTextBoxAttrBorder& att
     }
     else
         absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
-        
+
     m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
 }
 
 // Partial equality test
-bool wxTextBoxAttrBorders::EqPartial(const wxTextBoxAttrBorders& borders) const
+bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders) const
 {
     return m_left.EqPartial(borders.m_left) && m_right.EqPartial(borders.m_right) &&
             m_top.EqPartial(borders.m_top) && m_bottom.EqPartial(borders.m_bottom);
 }
 
 // Apply border to 'this', but not if the same as compareWith
-bool wxTextBoxAttrBorders::Apply(const wxTextBoxAttrBorders& borders, const wxTextBoxAttrBorders* compareWith)
+bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
 {
-    m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextBoxAttrBorder*) NULL);
-    m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextBoxAttrBorder*) NULL);
-    m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextBoxAttrBorder*) NULL);
-    m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextBoxAttrBorder*) NULL);
+    m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
+    m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
+    m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
+    m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
     return true;
 }
 
 // Remove specified attributes from this object
-bool wxTextBoxAttrBorders::RemoveStyle(const wxTextBoxAttrBorders& attr)
+bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
 {
     m_left.RemoveStyle(attr.m_left);
     m_right.RemoveStyle(attr.m_right);
@@ -8349,7 +8569,7 @@ bool wxTextBoxAttrBorders::RemoveStyle(const wxTextBoxAttrBorders& attr)
 
 // Collects the attributes that are common to a range of content, building up a note of
 // which attributes are absent in some objects and which clash in some objects.
-void wxTextBoxAttrBorders::CollectCommonAttributes(const wxTextBoxAttrBorders& attr, wxTextBoxAttrBorders& clashingAttr, wxTextBoxAttrBorders& absentAttr)
+void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
 {
     m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
     m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
@@ -8358,7 +8578,7 @@ void wxTextBoxAttrBorders::CollectCommonAttributes(const wxTextBoxAttrBorders& a
 }
 
 // Set style of all borders
-void wxTextBoxAttrBorders::SetStyle(int style)
+void wxTextAttrBorders::SetStyle(int style)
 {
     m_left.SetStyle(style);
     m_right.SetStyle(style);
@@ -8367,7 +8587,7 @@ void wxTextBoxAttrBorders::SetStyle(int style)
 }
 
 // Set colour of all borders
-void wxTextBoxAttrBorders::SetColour(unsigned long colour)
+void wxTextAttrBorders::SetColour(unsigned long colour)
 {
     m_left.SetColour(colour);
     m_right.SetColour(colour);
@@ -8375,7 +8595,7 @@ void wxTextBoxAttrBorders::SetColour(unsigned long colour)
     m_bottom.SetColour(colour);
 }
 
-void wxTextBoxAttrBorders::SetColour(const wxColour& colour)
+void wxTextAttrBorders::SetColour(const wxColour& colour)
 {
     m_left.SetColour(colour);
     m_right.SetColour(colour);
@@ -8384,7 +8604,7 @@ void wxTextBoxAttrBorders::SetColour(const wxColour& colour)
 }
 
 // Set width of all borders
-void wxTextBoxAttrBorders::SetWidth(const wxTextAttrDimension& width)
+void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
 {
     m_left.SetWidth(width);
     m_right.SetWidth(width);
@@ -8436,8 +8656,52 @@ void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& att
         absentAttr.SetPresent(true);
 }
 
+int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
+{
+    return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
+}
+
+int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
+{
+    return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
+}
+
+int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
+{
+    if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
+        return ConvertTenthsMMToPixels(dim.GetValue());
+    else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
+        return dim.GetValue();
+    else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
+    {
+        wxASSERT(m_parentSize != wxDefaultSize);
+        if (direction == wxHORIZONTAL)
+            return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
+        else
+            return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
+    }
+    else
+    {
+        wxASSERT(false);
+        return 0;
+    }
+}
+
+int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
+{
+    if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
+        return dim.GetValue();
+    else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
+        return ConvertPixelsToTenthsMM(dim.GetValue());
+    else
+    {
+        wxASSERT(false);
+        return 0;
+    }
+}
+
 // Partial equality test
-bool wxTextBoxAttrDimensions::EqPartial(const wxTextBoxAttrDimensions& dims) const
+bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims) const
 {
     if (!m_left.EqPartial(dims.m_left))
         return false;
@@ -8455,7 +8719,7 @@ bool wxTextBoxAttrDimensions::EqPartial(const wxTextBoxAttrDimensions& dims) con
 }
 
 // Apply border to 'this', but not if the same as compareWith
-bool wxTextBoxAttrDimensions::Apply(const wxTextBoxAttrDimensions& dims, const wxTextBoxAttrDimensions* compareWith)
+bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
 {
     m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
     m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
@@ -8466,7 +8730,7 @@ bool wxTextBoxAttrDimensions::Apply(const wxTextBoxAttrDimensions& dims, const w
 }
 
 // Remove specified attributes from this object
-bool wxTextBoxAttrDimensions::RemoveStyle(const wxTextBoxAttrDimensions& attr)
+bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
 {
     if (attr.m_left.IsPresent())
         m_left.Reset();
@@ -8482,7 +8746,7 @@ bool wxTextBoxAttrDimensions::RemoveStyle(const wxTextBoxAttrDimensions& attr)
 
 // Collects the attributes that are common to a range of content, building up a note of
 // which attributes are absent in some objects and which clash in some objects.
-void wxTextBoxAttrDimensions::CollectCommonAttributes(const wxTextBoxAttrDimensions& attr, wxTextBoxAttrDimensions& clashingAttr, wxTextBoxAttrDimensions& absentAttr)
+void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
 {
     m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
     m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
@@ -8912,6 +9176,139 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt
     }
 }
 
+WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
+
+bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
+{
+    if (m_properties.GetCount() != props.GetCount())
+        return false;
+
+    size_t i;
+    for (i = 0; i < m_properties.GetCount(); i++)
+    {
+        const wxVariant& var1 = m_properties[i];
+        int idx = props.Find(var1.GetName());
+        if (idx == -1)
+            return false;
+        const wxVariant& var2 = props.m_properties[idx];
+        if (!(var1 == var2))
+            return false;
+    }
+
+    return true;
+}
+
+wxArrayString wxRichTextProperties::GetPropertyNames() const
+{
+    wxArrayString arr;
+    size_t i;
+    for (i = 0; i < m_properties.GetCount(); i++)
+    {
+        arr.Add(m_properties[i].GetName());
+    }
+    return arr;
+}
+
+int wxRichTextProperties::Find(const wxString& name) const
+{
+    size_t i;
+    for (i = 0; i < m_properties.GetCount(); i++)
+    {
+        if (m_properties[i].GetName() == name)
+            return (int) i;
+    }
+    return -1;
+}
+
+wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
+{
+    int idx = Find(name);
+    if (idx == wxNOT_FOUND)
+        SetProperty(name, wxString());
+    idx = Find(name);
+    if (idx != wxNOT_FOUND)
+    {
+        return & (*this)[idx];
+    }
+    else
+        return NULL;
+}
+
+const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
+{
+    static const wxVariant nullVariant;
+    int idx = Find(name);
+    if (idx != -1)
+        return m_properties[idx];
+    else
+        return nullVariant;
+}
+
+wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
+{
+    return GetProperty(name).GetString();
+}
+
+long wxRichTextProperties::GetPropertyLong(const wxString& name) const
+{
+    return GetProperty(name).GetLong();
+}
+
+bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
+{
+    return GetProperty(name).GetBool();
+}
+
+double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
+{
+    return GetProperty(name).GetDouble();
+}
+
+void wxRichTextProperties::SetProperty(const wxVariant& variant)
+{
+    wxASSERT(!variant.GetName().IsEmpty());
+
+    int idx = Find(variant.GetName());
+
+    if (idx == -1)
+        m_properties.Add(variant);
+    else
+        m_properties[idx] = variant;
+}
+
+void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
+{
+    int idx = Find(name);
+    wxVariant var(variant);
+    var.SetName(name);
+
+    if (idx == -1)
+        m_properties.Add(var);
+    else
+        m_properties[idx] = var;
+}
+
+void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
+{
+    SetProperty(name, wxVariant(value, name));
+}
+
+void wxRichTextProperties::SetProperty(const wxString& name, long value)
+{
+    SetProperty(name, wxVariant(value, name));
+}
+
+void wxRichTextProperties::SetProperty(const wxString& name, double value)
+{
+    SetProperty(name, wxVariant(value, name));
+}
+
+void wxRichTextProperties::SetProperty(const wxString& name, bool value)
+{
+    SetProperty(name, wxVariant(value, name));
+}
 
 #endif
     // wxUSE_RICHTEXT
index 4c1d11568c313eaf82f25b96cd3b6ac72fd8e039..3651d221a1e09b3ed3fd4deafaae3025ea827343 100644 (file)
 #include "wx/wfstream.h"
 #include "wx/sstream.h"
 #include "wx/txtstrm.h"
+#include "wx/mstream.h"
 #include "wx/tokenzr.h"
+#include "wx/stopwatch.h"
 #include "wx/xml/xml.h"
 
+// Set to 1 for slower wxXmlDocument method, 0 for faster direct method.
+// If we make wxXmlDocument::Save more efficient, we might switch to this
+// method.
+#define wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT 0
+
+#if wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT && !wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
+#   error Must define wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT in richtextxml.h to use this method.
+#endif
+
+#if !wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT && !wxRICHTEXT_HAVE_DIRECT_OUTPUT
+#   error Must define wxRICHTEXT_HAVE_DIRECT_OUTPUT in richtextxml.h to use this method.
+#endif
+
+// Set to 1 to time file saving
+#define wxRICHTEXT_USE_OUTPUT_TIMINGS 0
+
+// Convert a colour to a 6-digit hex string
+static wxString ColourToHexString(const wxColour& col)
+{
+    wxString hex;
+
+    hex += wxDecToHex(col.Red());
+    hex += wxDecToHex(col.Green());
+    hex += wxDecToHex(col.Blue());
+
+    return hex;
+}
+
+// Convert 6-digit hex string to a colour
+static wxColour HexStringToColour(const wxString& hex)
+{
+    unsigned char r = (unsigned char)wxHexToDec(hex.Mid(0, 2));
+    unsigned char g = (unsigned char)wxHexToDec(hex.Mid(2, 2));
+    unsigned char b = (unsigned char)wxHexToDec(hex.Mid(4, 2));
+
+    return wxColour(r, g, b);
+}
+
+static inline wxString MakeString(const int& v) { return wxString::Format(wxT("%d"), v); }
+static inline wxString MakeString(const long& v) { return wxString::Format(wxT("%ld"), v); }
+static inline wxString MakeString(const double& v) { return wxString::Format(wxT("%.2f"), (float) v); }
+static inline wxString MakeString(const wxString& s) { return s; }
+static inline wxString MakeString(const wxColour& col) { return wxT("#") + ColourToHexString(col); }
+
+static inline void AddString(wxString& str, const int& v) { str << wxString::Format(wxT("%d"), v); }
+static inline void AddString(wxString& str, const long& v) { str << wxString::Format(wxT("%ld"), v); }
+static inline void AddString(wxString& str, const double& v) { str << wxString::Format(wxT("%.2f"), (float) v); }
+static inline void AddString(wxString& str, const wxChar* s) { str << s; }
+static inline void AddString(wxString& str, const wxString& s) { str << s; }
+static inline void AddString(wxString& str, const wxColour& col) { str << wxT("#") << ColourToHexString(col); }
+
 IMPLEMENT_DYNAMIC_CLASS(wxRichTextXMLHandler, wxRichTextFileHandler)
 
+void wxRichTextXMLHandler::Init()
+{
+#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
+    // Used during saving
+    m_convMem = NULL;
+    m_convFile = NULL;
+#endif
+}
+
 #if wxUSE_STREAMS
 bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
 {
@@ -74,7 +136,7 @@ bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& s
                     {
                     }
                     else
-                        ImportXML(buffer, child);
+                        ImportXML(buffer, buffer, child);
                 }
 
                 child = child->GetNext();
@@ -93,175 +155,99 @@ bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& s
     return success;
 }
 
-/// Recursively import an object
-bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node)
+/// Creates an object given an XML element name
+wxRichTextObject* wxRichTextXMLHandler::CreateObjectForXMLName(wxRichTextObject* WXUNUSED(parent), const wxString& name) const
 {
-    wxString name = node->GetName();
-
-    bool doneChildren = false;
-
-    if (name == wxT("paragraphlayout"))
-    {
-        wxString partial = node->GetAttribute(wxT("partialparagraph"), wxEmptyString);
-        if (partial == wxT("true"))
-            buffer->SetPartialParagraph(true);
-    }
+    if (name == wxT("text") || name == wxT("symbol"))
+        return new wxRichTextPlainText;
+    else if (name == wxT("image"))
+        return new wxRichTextImage;
     else if (name == wxT("paragraph"))
-    {
-        wxRichTextParagraph* para = new wxRichTextParagraph(buffer);
-        buffer->AppendChild(para);
-
-        GetStyle(para->GetAttributes(), node, true);
+        return new wxRichTextParagraph;
+    else if (name == wxT("paragraphlayout"))
+        return new wxRichTextParagraphLayoutBox;
+    else
+        return NULL;
+}
 
+/// Recursively import an object
+bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxRichTextObject* obj, wxXmlNode* node)
+{
+    obj->ImportFromXML(buffer, node, this);
+    
+    wxRichTextCompositeObject* compositeParent = wxDynamicCast(obj, wxRichTextCompositeObject);
+    if (compositeParent)
+    {
         wxXmlNode* child = node->GetChildren();
         while (child)
         {
-            wxString childName = child->GetName();
-            if (childName == wxT("text"))
+            if (child->GetName() == wxT("stylesheet"))
             {
-                wxString text;
-                wxXmlNode* textChild = child->GetChildren();
-                while (textChild)
+                if (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET)
                 {
-                    if (textChild->GetType() == wxXML_TEXT_NODE ||
-                        textChild->GetType() == wxXML_CDATA_SECTION_NODE)
+                    wxRichTextStyleSheet* sheet = new wxRichTextStyleSheet;
+                    wxString sheetName = child->GetAttribute(wxT("name"), wxEmptyString);
+                    wxString sheetDescription = child->GetAttribute(wxT("description"), wxEmptyString);
+                    sheet->SetName(sheetName);
+                    sheet->SetDescription(sheetDescription);
+
+                    wxXmlNode* child2 = child->GetChildren();
+                    while (child2)
                     {
-                        wxString text2 = textChild->GetContent();
-
-                        // Strip whitespace from end
-                        if (!text2.empty() && text2[text2.length()-1] == wxT('\n'))
-                            text2 = text2.Mid(0, text2.length()-1);
-
-                        if (!text2.empty() && text2[0] == wxT('"'))
-                            text2 = text2.Mid(1);
-                        if (!text2.empty() && text2[text2.length()-1] == wxT('"'))
-                            text2 = text2.Mid(0, text2.length() - 1);
+                        ImportStyleDefinition(sheet, child2);
 
-                        text += text2;
+                        child2 = child2->GetNext();
                     }
-                    textChild = textChild->GetNext();
-                }
-
-                wxRichTextPlainText* textObject = new wxRichTextPlainText(text, para);
-                GetStyle(textObject->GetAttributes(), child, false);
 
-                para->AppendChild(textObject);
-            }
-            else if (childName == wxT("symbol"))
-            {
-                // This is a symbol that XML can't read in the normal way
-                wxString text;
-                wxXmlNode* textChild = child->GetChildren();
-                while (textChild)
-                {
-                    if (textChild->GetType() == wxXML_TEXT_NODE ||
-                        textChild->GetType() == wxXML_CDATA_SECTION_NODE)
-                    {
-                        wxString text2 = textChild->GetContent();
-                        text += text2;
-                    }
-                    textChild = textChild->GetNext();
+                    // Notify that styles have changed. If this is vetoed by the app,
+                    // the new sheet will be deleted. If it is not vetoed, the
+                    // old sheet will be deleted and replaced with the new one.
+                    buffer->SetStyleSheetAndNotify(sheet);
                 }
-
-                wxString actualText;
-                actualText << (wxChar) wxAtoi(text);
-
-                wxRichTextPlainText* textObject = new wxRichTextPlainText(actualText, para);
-                GetStyle(textObject->GetAttributes(), child, false);
-
-                para->AppendChild(textObject);
             }
-            else if (childName == wxT("image"))
+            else
             {
-                wxBitmapType imageType = wxBITMAP_TYPE_PNG;
-                wxString value = child->GetAttribute(wxT("imagetype"), wxEmptyString);
-                if (!value.empty())
-                {
-                    int type = wxAtoi(value);
-
-                    // note: 0 == wxBITMAP_TYPE_INVALID
-                    if (type <= 0 || type >= wxBITMAP_TYPE_MAX)
-                    {
-                        wxLogWarning("Invalid bitmap type specified for <image> tag: %d", type);
-                    }
-                    else
-                    {
-                        imageType = (wxBitmapType)type;
-                    }
-                }
-
-                wxString data;
-
-                wxXmlNode* imageChild = child->GetChildren();
-                while (imageChild)
-                {
-                    wxString childName = imageChild->GetName();
-                    if (childName == wxT("data"))
-                    {
-                        wxXmlNode* dataChild = imageChild->GetChildren();
-                        while (dataChild)
-                        {
-                            data = dataChild->GetContent();
-                            // wxLogDebug(data);
-                            dataChild = dataChild->GetNext();
-                        }
-
-                    }
-                    imageChild = imageChild->GetNext();
-                }
-
-                if (!data.empty())
+                wxRichTextObject* childObj = CreateObjectForXMLName(obj, child->GetName());
+                if (childObj)
                 {
-                    wxRichTextImage* imageObj = new wxRichTextImage(para);
-                    GetStyle(imageObj->GetAttributes(), child, false);
-                    para->AppendChild(imageObj);
-
-                    wxStringInputStream strStream(data);
-
-                    imageObj->GetImageBlock().ReadHex(strStream, data.length(), imageType);
+                    compositeParent->AppendChild(childObj);
+                    ImportXML(buffer, childObj, child);
                 }
             }
             child = child->GetNext();
         }
-
-        doneChildren = true;
     }
-    else if (name == wxT("stylesheet"))
-    {
-        if (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET)
-        {
-            wxRichTextStyleSheet* sheet = new wxRichTextStyleSheet;
-            wxString sheetName = node->GetAttribute(wxT("name"), wxEmptyString);
-            wxString sheetDescription = node->GetAttribute(wxT("description"), wxEmptyString);
-            sheet->SetName(sheetName);
-            sheet->SetDescription(sheetDescription);
-
-            wxXmlNode* child = node->GetChildren();
-            while (child)
-            {
-                ImportStyleDefinition(sheet, child);
 
-                child = child->GetNext();
-            }
-
-            // Notify that styles have changed. If this is vetoed by the app,
-            // the new sheet will be deleted. If it is not vetoed, the
-            // old sheet will be deleted and replaced with the new one.
-            buffer->SetStyleSheetAndNotify(sheet);
-        }
-        doneChildren = true;
-    }
+    return true;
+}
 
-    if (!doneChildren)
+bool wxRichTextXMLHandler::ImportProperties(wxRichTextObject* obj, wxXmlNode* node)
+{
+    wxXmlNode* child = node->GetChildren();
+    while (child)
     {
-        wxXmlNode* child = node->GetChildren();
-        while (child)
+        if (child->GetName() == wxT("properties"))
         {
-            ImportXML(buffer, child);
-            child = child->GetNext();
+            wxXmlNode* propertyChild = child->GetChildren();
+            while (propertyChild)
+            {
+                if (propertyChild->GetName() == wxT("property"))
+                {
+                    wxString name = propertyChild->GetAttribute(wxT("name"), wxEmptyString);
+                    wxString value = propertyChild->GetAttribute(wxT("value"), wxEmptyString);
+                    wxString type = propertyChild->GetAttribute(wxT("type"), wxEmptyString);
+                    
+                    wxVariant var = MakePropertyFromString(name, value, type);
+                    if (!var.IsNull())
+                    {
+                        obj->GetProperties().SetProperty(var);
+                    }
+                }
+                propertyChild = propertyChild->GetNext();
+            }
         }
+        child = child->GetNext();
     }
-
     return true;
 }
 
@@ -271,7 +257,7 @@ bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wx
     wxString styleName = node->GetAttribute(wxT("name"), wxEmptyString);
     wxString baseStyleName = node->GetAttribute(wxT("basestyle"), wxEmptyString);
 
-    if (styleName.IsEmpty())
+    if (styleName.empty())
         return false;
 
     if (styleType == wxT("characterstyle"))
@@ -285,7 +271,7 @@ bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wx
             if (child->GetName() == wxT("style"))
             {
                 wxRichTextAttr attr;
-                GetStyle(attr, child, false);
+                ImportStyle(attr, child, false);
                 def->SetStyle(attr);
             }
             child = child->GetNext();
@@ -307,7 +293,7 @@ bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wx
             if (child->GetName() == wxT("style"))
             {
                 wxRichTextAttr attr;
-                GetStyle(attr, child, false);
+                ImportStyle(attr, child, true);
                 def->SetStyle(attr);
             }
             child = child->GetNext();
@@ -329,10 +315,10 @@ bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wx
             if (child->GetName() == wxT("style"))
             {
                 wxRichTextAttr attr;
-                GetStyle(attr, child, false);
+                ImportStyle(attr, child, true);
 
                 wxString styleLevel = child->GetAttribute(wxT("level"), wxEmptyString);
-                if (styleLevel.IsEmpty())
+                if (styleLevel.empty())
                 {
                     def->SetStyle(attr);
                 }
@@ -422,9 +408,9 @@ wxString wxRichTextXMLHandler::GetText(wxXmlNode *node, const wxString& param, b
 #endif
 #endif
 
-// write string to output:
+// write string to output
 inline static void OutputString(wxOutputStream& stream, const wxString& str,
-                                wxMBConv *WXUNUSED_IN_UNICODE(convMem) = NULL, wxMBConv *convFile = NULL)
+                                wxMBConv *WXUNUSED_IN_UNICODE(convMem), wxMBConv *convFile)
 {
     if (str.empty()) return;
 #if wxUSE_UNICODE
@@ -449,6 +435,14 @@ inline static void OutputString(wxOutputStream& stream, const wxString& str,
 #endif
 }
 
+static void OutputIndentation(wxOutputStream& stream, int indent)
+{
+    wxString str = wxT("\n");
+    for (int i = 0; i < indent; i++)
+        str << wxT(' ') << wxT(' ');
+    ::OutputString(stream, str, NULL, NULL);
+}
+
 // Same as above, but create entities first.
 // Translates '<' to "&lt;", '>' to "&gt;" and '&' to "&amp;"
 static void OutputStringEnt(wxOutputStream& stream, const wxString& str,
@@ -508,7 +502,25 @@ static void OutputStringEnt(wxOutputStream& stream, const wxString& str,
     OutputString(stream, str.Mid(last, i - last), convMem, convFile);
 }
 
-static wxString AttributeToXML(const wxString& str)
+void wxRichTextXMLHandler::OutputString(wxOutputStream& stream, const wxString& str)
+{
+    ::OutputString(stream, str, m_convMem, m_convFile);
+}
+
+void wxRichTextXMLHandler::OutputStringEnt(wxOutputStream& stream, const wxString& str)
+{
+    ::OutputStringEnt(stream, str, m_convMem, m_convFile);
+}
+
+void wxRichTextXMLHandler::OutputIndentation(wxOutputStream& stream, int indent)
+{
+    wxString str = wxT("\n");
+    for (int i = 0; i < indent; i++)
+        str << wxT(' ') << wxT(' ');
+    ::OutputString(stream, str, NULL, NULL);
+}
+
+wxString wxRichTextXMLHandler::AttributeToXML(const wxString& str)
 {
     wxString str1;
     size_t i, last, len;
@@ -565,53 +577,167 @@ static wxString AttributeToXML(const wxString& str)
     return str1;
 }
 
-inline static void OutputIndentation(wxOutputStream& stream, int indent)
+#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
+
+static inline void AddAttribute(wxString& str, const wxChar* name, const int& v)
 {
-    wxString str = wxT("\n");
-    for (int i = 0; i < indent; i++)
-        str << wxT(' ') << wxT(' ');
-    OutputString(stream, str, NULL, NULL);
+    str << wxT(" ") << name << wxT("=\"") << wxString::Format(wxT("%d"), v) << wxT("\"");
 }
 
-// Convert a colour to a 6-digit hex string
-static wxString ColourToHexString(const wxColour& col)
+static inline void AddAttribute(wxString& str, const wxChar* name, const long& v)
 {
-    wxString hex;
+    str << wxT(" ") << name << wxT("=\"") << wxString::Format(wxT("%ld"), v) << wxT("\"");
+}
 
-    hex += wxDecToHex(col.Red());
-    hex += wxDecToHex(col.Green());
-    hex += wxDecToHex(col.Blue());
+static inline void AddAttribute(wxString& str, const wxChar* name, const double& v)
+{
+    str << wxT(" ") << name << wxT("=\"") << wxString::Format(wxT("%.2f"), (float) v) << wxT("\"");
+}
 
-    return hex;
+static inline void AddAttribute(wxString& str, const wxChar* name, const wxChar* s)
+{
+    str << wxT(" ") << name << wxT("=\"") << s << wxT("\"");
 }
 
-// Convert 6-digit hex string to a colour
-static wxColour HexStringToColour(const wxString& hex)
+static inline void AddAttribute(wxString& str, const wxChar* name, const wxString& s)
 {
-    unsigned char r = (unsigned char)wxHexToDec(hex.Mid(0, 2));
-    unsigned char g = (unsigned char)wxHexToDec(hex.Mid(2, 2));
-    unsigned char b = (unsigned char)wxHexToDec(hex.Mid(4, 2));
+    str << wxT(" ") << name << wxT("=\"") << s << wxT("\"");
+}
 
-    return wxColour(r, g, b);
+static inline void AddAttribute(wxString& str, const wxChar* name, const wxColour& col)
+{
+    str << wxT(" ") << name << wxT("=\"") << wxT("#") << ColourToHexString(col) << wxT("\"");
+}
+
+static inline void AddAttribute(wxString& str, const wxChar* name, const wxTextAttrDimension& dim)
+{
+    if (dim.IsPresent())
+    {
+        wxString value = MakeString(dim.GetValue()) + wxT(",") + MakeString((int) dim.GetFlags());
+        str << wxT(" ") << name << wxT("=\"");
+        str << value;
+        str << wxT("\"");
+    }
+}
+
+static inline void AddAttribute(wxString& str, const wxChar* rootName, const wxTextAttrDimensions& dims)
+{
+    if (dims.GetLeft().IsPresent())
+        AddAttribute(str, rootName + wxString(wxT("-left")), dims.GetLeft());
+    if (dims.GetRight().IsPresent())
+        AddAttribute(str, rootName + wxString(wxT("-right")), dims.GetRight());
+    if (dims.GetTop().IsPresent())
+        AddAttribute(str, rootName + wxString(wxT("-top")), dims.GetTop());
+    if (dims.GetBottom().IsPresent())
+        AddAttribute(str, rootName + wxString(wxT("-bottom")), dims.GetBottom());
+}
+
+static inline void AddAttribute(wxString& str, const wxChar* rootName, const wxTextAttrBorder& border)
+{
+    if (border.HasStyle())
+        AddAttribute(str, rootName + wxString(wxT("-style")), border.GetStyle());
+    if (border.HasColour())
+        AddAttribute(str, rootName + wxString(wxT("-color")), border.GetColour());
+    if (border.HasWidth())
+        AddAttribute(str, rootName + wxString(wxT("-width")), border.GetWidth());
+}
+
+static inline void AddAttribute(wxString& str, const wxChar* rootName, const wxTextAttrBorders& borders)
+{
+    AddAttribute(str, rootName + wxString(wxT("-left")), borders.GetLeft());
+    AddAttribute(str, rootName + wxString(wxT("-right")), borders.GetRight());
+    AddAttribute(str, rootName + wxString(wxT("-top")), borders.GetTop());
+    AddAttribute(str, rootName + wxString(wxT("-bottom")), borders.GetBottom());
+}
+
+#endif
+    // wxRICHTEXT_HAVE_DIRECT_OUTPUT
+
+#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
+
+static inline void AddAttribute(wxXmlNode* node, const wxChar* name, const int& v)
+{
+    node->AddAttribute(name, MakeString(v));
+}
+
+static inline void AddAttribute(wxXmlNode* node, const wxChar* name, const long& v)
+{
+    node->AddAttribute(name, MakeString(v));
+}
+
+static inline void AddAttribute(wxXmlNode* node, const wxChar* name, const double& v)
+{
+    node->AddAttribute(name, MakeString(v));
+}
+
+static inline void AddAttribute(wxXmlNode* node, const wxChar* name, const wxString& s)
+{
+    node->AddAttribute(name, s);
+}
+
+static inline void AddAttribute(wxXmlNode* node, const wxChar* name, const wxColour& col)
+{
+    node->AddAttribute(name, MakeString(col));
 }
 
+static inline void AddAttribute(wxXmlNode* node, const wxChar* name, const wxTextAttrDimension& dim)
+{
+    if (dim.IsPresent())
+    {
+        wxString value = MakeString(dim.GetValue()) + wxT(",") + MakeString(dim.GetFlags());
+        AddAttribute(node, name, value);
+    }
+}
+
+static inline void AddAttribute(wxXmlNode* node, const wxChar* rootName, const wxTextAttrDimensions& dims)
+{
+    if (dims.GetLeft().IsPresent())
+        AddAttribute(node, rootName + wxString(wxT("-left")), dims.GetLeft());
+    if (dims.GetRight().IsPresent())
+        AddAttribute(node, rootName + wxString(wxT("-right")), dims.GetRight());
+    if (dims.GetTop().IsPresent())
+        AddAttribute(node, rootName + wxString(wxT("-top")), dims.GetTop());
+    if (dims.GetBottom().IsPresent())
+        AddAttribute(node, rootName + wxString(wxT("-bottom")), dims.GetBottom());
+}
+
+static inline void AddAttribute(wxXmlNode* node, const wxChar* rootName, const wxTextAttrBorder& border)
+{
+    if (border.HasStyle())
+        AddAttribute(node, rootName + wxString(wxT("-style")), border.GetStyle());
+    if (border.HasColour())
+        AddAttribute(node, rootName + wxString(wxT("-color")), border.GetColour());
+    if (border.HasWidth())
+        AddAttribute(node, rootName + wxString(wxT("-width")), border.GetWidth());
+}
+
+static inline void AddAttribute(wxXmlNode* node, const wxChar* rootName, const wxTextAttrBorders& borders)
+{
+    AddAttribute(node, rootName + wxString(wxT("-left")), borders.GetLeft());
+    AddAttribute(node, rootName + wxString(wxT("-right")), borders.GetRight());
+    AddAttribute(node, rootName + wxString(wxT("-top")), borders.GetTop());
+    AddAttribute(node, rootName + wxString(wxT("-bottom")), borders.GetBottom());
+}
+#endif
+    // wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
+
 bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
 {
     if (!stream.IsOk())
         return false;
 
     wxString version(wxT("1.0") ) ;
-
+    
     bool deleteConvFile = false;
     wxString fileEncoding;
-    wxMBConv* convFile = NULL;
+    //wxMBConv* convFile = NULL;
 
 #if wxUSE_UNICODE
     fileEncoding = wxT("UTF-8");
-    convFile = & wxConvUTF8;
+    m_convFile = & wxConvUTF8;
 #else
     fileEncoding = wxT("ISO-8859-1");
-    convFile = & wxConvISO8859_1;
+    m_convFile = & wxConvISO8859_1;
 #endif
 
     // If SetEncoding has been called, change the output encoding.
@@ -636,21 +762,89 @@ bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream&
 #else
             fileEncoding = wxT("ISO-8859-1");
 #endif
-        convFile = new wxCSConv(fileEncoding);
+        m_convFile = new wxCSConv(fileEncoding);
         deleteConvFile = true;
     }
 
+#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT && wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT
+#if wxRICHTEXT_USE_OUTPUT_TIMINGS
+    wxStopWatch stopwatch;
+#endif
+    wxXmlDocument* doc = new wxXmlDocument;
+    doc->SetFileEncoding(fileEncoding);
+    
+    wxXmlNode* rootNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("richtext"));
+    doc->SetRoot(rootNode);
+    rootNode->AddAttribute(wxT("version"), wxT("1.0.0.0"));
+    rootNode->AddAttribute(wxT("xmlns"), wxT("http://www.wxwidgets.org"));
+
+    if (buffer->GetStyleSheet() && (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET))
+    {
+        wxXmlNode* styleSheetNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("stylesheet"));
+        rootNode->AddChild(styleSheetNode);
+        
+        wxString nameAndDescr;
+        
+        if (!buffer->GetStyleSheet()->GetName().empty())
+            styleSheetNode->AddAttribute(wxT("name"), buffer->GetStyleSheet()->GetName());
+            
+        if (!buffer->GetStyleSheet()->GetDescription().empty())
+            styleSheetNode->AddAttribute(wxT("description"), buffer->GetStyleSheet()->GetDescription());
+
+        int i;
+        for (i = 0; i < (int) buffer->GetStyleSheet()->GetCharacterStyleCount(); i++)
+        {
+            wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->GetCharacterStyle(i);
+            ExportStyleDefinition(styleSheetNode, def);
+        }
+
+        for (i = 0; i < (int) buffer->GetStyleSheet()->GetParagraphStyleCount(); i++)
+        {
+            wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->GetParagraphStyle(i);
+            ExportStyleDefinition(styleSheetNode, def);
+        }
+
+        for (i = 0; i < (int) buffer->GetStyleSheet()->GetListStyleCount(); i++)
+        {
+            wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->GetListStyle(i);
+            ExportStyleDefinition(styleSheetNode, def);
+        }
+    }
+    bool success = ExportXML(rootNode, *buffer);
+#if wxRICHTEXT_USE_OUTPUT_TIMINGS
+    long t = stopwatch.Time();
+    wxLogDebug(wxT("Creating the document took %ldms"), t);
+    wxMessageBox(wxString::Format(wxT("Creating the document took %ldms"), t));
+#endif
+    if (success)
+    {
+#if wxRICHTEXT_USE_OUTPUT_TIMINGS
+        wxStopWatch s2;
+#endif
+        success = doc->Save(stream);
+#if wxRICHTEXT_USE_OUTPUT_TIMINGS
+        long t2 = s2.Time();
+        wxLogDebug(wxT("Save() took %ldms"), t2);
+        wxMessageBox(wxString::Format(wxT("Save() took %ldms"), t2));
+#endif
+    }
+    delete doc;
+    doc = NULL;
+
+#else
+    // !(wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT && wxRICHTEXT_USE_XMLDOCUMENT_OUTPUT)
+
 #if !wxUSE_UNICODE
-    wxMBConv* convMem = wxConvCurrent;
+    m_convMem = wxConvCurrent;
 #else
-    wxMBConv* convMem = NULL;
+    m_convMem = NULL;
 #endif
 
     wxString s ;
     s.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
              version, fileEncoding);
-    OutputString(stream, s, NULL, NULL);
-    OutputString(stream, wxT("<richtext version=\"1.0.0.0\" xmlns=\"http://www.wxwidgets.org\">") , NULL, NULL);
+    OutputString(stream, s);
+    OutputString(stream, wxT("<richtext version=\"1.0.0.0\" xmlns=\"http://www.wxwidgets.org\">"));
 
     int level = 1;
 
@@ -658,271 +852,115 @@ bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream&
     {
         OutputIndentation(stream, level);
         wxString nameAndDescr;
-        if (!buffer->GetStyleSheet()->GetName().IsEmpty())
+        if (!buffer->GetStyleSheet()->GetName().empty())
             nameAndDescr << wxT(" name=\"") << buffer->GetStyleSheet()->GetName() << wxT("\"");
-        if (!buffer->GetStyleSheet()->GetDescription().IsEmpty())
+        if (!buffer->GetStyleSheet()->GetDescription().empty())
             nameAndDescr << wxT(" description=\"") << buffer->GetStyleSheet()->GetDescription() << wxT("\"");
-        OutputString(stream, wxString(wxT("<stylesheet")) + nameAndDescr + wxT(">"), convMem, convFile);
+        OutputString(stream, wxString(wxT("<stylesheet")) + nameAndDescr + wxT(">"));
 
         int i;
 
         for (i = 0; i < (int) buffer->GetStyleSheet()->GetCharacterStyleCount(); i++)
         {
             wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->GetCharacterStyle(i);
-            ExportStyleDefinition(stream, convMem, convFile, def, level + 1);
+            ExportStyleDefinition(stream, def, level + 1);
         }
 
         for (i = 0; i < (int) buffer->GetStyleSheet()->GetParagraphStyleCount(); i++)
         {
             wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->GetParagraphStyle(i);
-            ExportStyleDefinition(stream, convMem, convFile, def, level + 1);
+            ExportStyleDefinition(stream, def, level + 1);
         }
 
         for (i = 0; i < (int) buffer->GetStyleSheet()->GetListStyleCount(); i++)
         {
             wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->GetListStyle(i);
-            ExportStyleDefinition(stream, convMem, convFile, def, level + 1);
+            ExportStyleDefinition(stream, def, level + 1);
         }
 
         OutputIndentation(stream, level);
-        OutputString(stream, wxT("</stylesheet>"), convMem, convFile);
+        OutputString(stream, wxT("</stylesheet>"));
     }
 
 
-    bool success = ExportXML(stream, convMem, convFile, *buffer, level);
+    bool success = ExportXML(stream, *buffer, level);
 
-    OutputString(stream, wxT("\n</richtext>") , NULL, NULL);
-    OutputString(stream, wxT("\n"), NULL, NULL);
+    OutputString(stream, wxT("\n</richtext>"));
+    OutputString(stream, wxT("\n"));
 
     if (deleteConvFile)
-        delete convFile;
+        delete m_convFile;
+    m_convFile = NULL;
+    m_convMem = NULL;
+#endif
 
     return success;
 }
 
+#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
+
 /// Recursively export an object
-bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextObject& obj, int indent)
-{
-    wxString objectName;
-    if (obj.IsKindOf(CLASSINFO(wxRichTextParagraphLayoutBox)))
-        objectName = wxT("paragraphlayout");
-    else if (obj.IsKindOf(CLASSINFO(wxRichTextParagraph)))
-        objectName = wxT("paragraph");
-    else if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText)))
-        objectName = wxT("text");
-    else if (obj.IsKindOf(CLASSINFO(wxRichTextImage)))
-        objectName = wxT("image");
-    else
-        objectName = wxT("object");
+bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxRichTextObject& obj, int indent)
+{
+    obj.ExportXML(stream, indent, this);
 
-    bool terminateTag = true;
+    return true;
+}
 
-    if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText)))
-    {
-        wxRichTextPlainText& textObj = (wxRichTextPlainText&) obj;
+bool wxRichTextXMLHandler::ExportStyleDefinition(wxOutputStream& stream, wxRichTextStyleDefinition* def, int level)
+{
+    wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition);
+    wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition);
+    wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition);
 
-        wxString style = CreateStyle(obj.GetAttributes(), false);
+    wxString baseStyle = def->GetBaseStyle();
+    wxString baseStyleProp;
+    if (!baseStyle.empty())
+        baseStyleProp = wxT(" basestyle=\"") + baseStyle + wxT("\"");
 
-        int i;
-        int last = 0;
-        const wxString& text = textObj.GetText();
-        int len = (int) text.Length();
+    wxString descr = def->GetDescription();
+    wxString descrProp;
+    if (!descr.empty())
+        descrProp = wxT(" description=\"") + descr + wxT("\"");
 
-        if (len == 0)
-        {
-            i = 0;
-            OutputIndentation(stream, indent);
-            OutputString(stream, wxT("<") + objectName, convMem, convFile);
-            OutputString(stream, style + wxT(">"), convMem, convFile);
-            OutputString(stream, wxT("</text>"), convMem, convFile);
-        }
-        else for (i = 0; i < len; i++)
-        {
-#if wxUSE_UNICODE
-            int c = (int) text[i];
-#else
-            int c = (int) wxUChar(text[i]);
-#endif
-            if ((c < 32 || c == 34) && /* c != 9 && */ c != 10 && c != 13)
-            {
-                if (i > 0)
-                {
-                    wxString fragment(text.Mid(last, i-last));
-                    if (!fragment.IsEmpty())
-                    {
-                        OutputIndentation(stream, indent);
-                        OutputString(stream, wxT("<") + objectName, convMem, convFile);
+    if (charDef)
+    {
+        OutputIndentation(stream, level);
+        OutputString(stream, wxT("<characterstyle") + baseStyleProp + descrProp + wxT(">"));
 
-                        OutputString(stream, style + wxT(">"), convMem, convFile);
+        level ++;
 
-                        if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')))
-                        {
-                            OutputString(stream, wxT("\""), convMem, convFile);
-                            OutputStringEnt(stream, fragment, convMem, convFile);
-                            OutputString(stream, wxT("\""), convMem, convFile);
-                        }
-                        else
-                            OutputStringEnt(stream, fragment, convMem, convFile);
-
-                        OutputString(stream, wxT("</text>"), convMem, convFile);
-                    }
-                }
-
-
-                // Output this character as a number in a separate tag, because XML can't cope
-                // with entities below 32 except for 10 and 13
-                last = i + 1;
-                OutputIndentation(stream, indent);
-                OutputString(stream, wxT("<symbol"), convMem, convFile);
-
-                OutputString(stream, style + wxT(">"), convMem, convFile);
-                OutputString(stream, wxString::Format(wxT("%d"), c), convMem, convFile);
-
-                OutputString(stream, wxT("</symbol>"), convMem, convFile);
-            }
-        }
-
-        wxString fragment;
-        if (last == 0)
-            fragment = text;
-        else
-            fragment = text.Mid(last, i-last);
-
-        if (last < len)
-        {
-            OutputIndentation(stream, indent);
-            OutputString(stream, wxT("<") + objectName, convMem, convFile);
-
-            OutputString(stream, style + wxT(">"), convMem, convFile);
-
-            if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')))
-            {
-                OutputString(stream, wxT("\""), convMem, convFile);
-                OutputStringEnt(stream, fragment, convMem, convFile);
-                OutputString(stream, wxT("\""), convMem, convFile);
-            }
-            else
-                OutputStringEnt(stream, fragment, convMem, convFile);
-        }
-        else
-            terminateTag = false;
-    }
-    else if (obj.IsKindOf(CLASSINFO(wxRichTextImage)))
-    {
-        wxRichTextImage& imageObj = (wxRichTextImage&) obj;
-
-        wxString style = CreateStyle(obj.GetAttributes(), false);
-
-        //if (imageObj.GetImage().Ok() && !imageObj.GetImageBlock().Ok())
-        //    imageObj.MakeBlock();
-
-        OutputIndentation(stream, indent);
-        OutputString(stream, wxT("<") + objectName, convMem, convFile);
-        if (!imageObj.GetImageBlock().Ok())
-        {
-            // No data
-            OutputString(stream, style + wxT(">"), convMem, convFile);
-        }
-        else
-        {
-            OutputString(stream, wxString::Format(wxT(" imagetype=\"%d\""), (int) imageObj.GetImageBlock().GetImageType()) + style + wxT(">"));
-        }
-
-        OutputIndentation(stream, indent+1);
-        OutputString(stream, wxT("<data>"), convMem, convFile);
-
-        imageObj.GetImageBlock().WriteHex(stream);
-
-        OutputString(stream, wxT("</data>"), convMem, convFile);
-    }
-    else if (obj.IsKindOf(CLASSINFO(wxRichTextCompositeObject)))
-    {
-        OutputIndentation(stream, indent);
-        OutputString(stream, wxT("<") + objectName, convMem, convFile);
-
-        bool isPara = false;
-        if (objectName == wxT("paragraph") || objectName == wxT("paragraphlayout"))
-            isPara = true;
-
-        wxString style = CreateStyle(obj.GetAttributes(), isPara);
-
-        if (objectName == wxT("paragraphlayout") && ((wxRichTextParagraphLayoutBox&) obj).GetPartialParagraph())
-            style << wxT(" partialparagraph=\"true\"");
-
-        OutputString(stream, style + wxT(">"), convMem, convFile);
-
-        wxRichTextCompositeObject& composite = (wxRichTextCompositeObject&) obj;
-        size_t i;
-        for (i = 0; i < composite.GetChildCount(); i++)
-        {
-            wxRichTextObject* child = composite.GetChild(i);
-            ExportXML(stream, convMem, convFile, *child, indent+1);
-        }
-    }
-
-    if (objectName != wxT("text"))
-        OutputIndentation(stream, indent);
-
-    if (terminateTag)
-        OutputString(stream, wxT("</") + objectName + wxT(">"), convMem, convFile);
-
-    return true;
-}
-
-bool wxRichTextXMLHandler::ExportStyleDefinition(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextStyleDefinition* def, int level)
-{
-    wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition);
-    wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition);
-    wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition);
-
-    wxString baseStyle = def->GetBaseStyle();
-    wxString baseStyleProp;
-    if (!baseStyle.IsEmpty())
-        baseStyleProp = wxT(" basestyle=\"") + baseStyle + wxT("\"");
-
-    wxString descr = def->GetDescription();
-    wxString descrProp;
-    if (!descr.IsEmpty())
-        descrProp = wxT(" description=\"") + descr + wxT("\"");
-
-    if (charDef)
-    {
-        OutputIndentation(stream, level);
-        OutputString(stream, wxT("<characterstyle") + baseStyleProp + descrProp + wxT(">"), convMem, convFile);
-
-        level ++;
-
-        wxString style = CreateStyle(def->GetStyle(), false);
+        wxString style = AddAttributes(def->GetStyle(), false);
 
         OutputIndentation(stream, level);
-        OutputString(stream, wxT("<style ") + style + wxT(">"), convMem, convFile);
+        OutputString(stream, wxT("<style ") + style + wxT(">"));
 
         OutputIndentation(stream, level);
-        OutputString(stream, wxT("</style>"), convMem, convFile);
+        OutputString(stream, wxT("</style>"));
 
         level --;
 
         OutputIndentation(stream, level);
-        OutputString(stream, wxT("</characterstyle>"), convMem, convFile);
+        OutputString(stream, wxT("</characterstyle>"));
     }
     else if (listDef)
     {
         OutputIndentation(stream, level);
 
-        if (!listDef->GetNextStyle().IsEmpty())
-            baseStyleProp << wxT(" basestyle=\"") << listDef->GetNextStyle() << wxT("\"");
+        if (!listDef->GetNextStyle().empty())
+            baseStyleProp << wxT(" nextstyle=\"") << listDef->GetNextStyle() << wxT("\"");
 
-        OutputString(stream, wxT("<liststyle") + baseStyleProp + descrProp + wxT(">"), convMem, convFile);
+        OutputString(stream, wxT("<liststyle") + baseStyleProp + descrProp + wxT(">"));
 
         level ++;
 
-        wxString style = CreateStyle(def->GetStyle(), false);
+        wxString style = AddAttributes(def->GetStyle(), true);
 
         OutputIndentation(stream, level);
-        OutputString(stream, wxT("<style ") + style + wxT(">"), convMem, convFile);
+        OutputString(stream, wxT("<style ") + style + wxT(">"));
 
         OutputIndentation(stream, level);
-        OutputString(stream, wxT("</style>"), convMem, convFile);
+        OutputString(stream, wxT("</style>"));
 
         int i;
         for (i = 0; i < 10; i ++)
@@ -930,428 +968,1463 @@ bool wxRichTextXMLHandler::ExportStyleDefinition(wxOutputStream& stream, wxMBCon
             wxRichTextAttr* levelAttr = listDef->GetLevelAttributes(i);
             if (levelAttr)
             {
-                wxString style = CreateStyle(def->GetStyle(), false);
+                wxString style = AddAttributes(def->GetStyle(), true);
                 wxString levelStr = wxString::Format(wxT(" level=\"%d\" "), (i+1));
 
                 OutputIndentation(stream, level);
-                OutputString(stream, wxT("<style ") + levelStr + style + wxT(">"), convMem, convFile);
+                OutputString(stream, wxT("<style ") + levelStr + style + wxT(">"));
 
                 OutputIndentation(stream, level);
-                OutputString(stream, wxT("</style>"), convMem, convFile);
+                OutputString(stream, wxT("</style>"));
             }
         }
 
         level --;
 
         OutputIndentation(stream, level);
-        OutputString(stream, wxT("</liststyle>"), convMem, convFile);
+        OutputString(stream, wxT("</liststyle>"));
     }
     else if (paraDef)
     {
         OutputIndentation(stream, level);
 
-        if (!paraDef->GetNextStyle().IsEmpty())
-            baseStyleProp << wxT(" basestyle=\"") << paraDef->GetNextStyle() << wxT("\"");
+        if (!paraDef->GetNextStyle().empty())
+            baseStyleProp << wxT(" nextstyle=\"") << paraDef->GetNextStyle() << wxT("\"");
 
-        OutputString(stream, wxT("<paragraphstyle") + baseStyleProp + descrProp + wxT(">"), convMem, convFile);
+        OutputString(stream, wxT("<paragraphstyle") + baseStyleProp + descrProp + wxT(">"));
 
         level ++;
 
-        wxString style = CreateStyle(def->GetStyle(), false);
+        wxString style = AddAttributes(def->GetStyle(), false);
 
         OutputIndentation(stream, level);
-        OutputString(stream, wxT("<style ") + style + wxT(">"), convMem, convFile);
+        OutputString(stream, wxT("<style ") + style + wxT(">"));
 
         OutputIndentation(stream, level);
-        OutputString(stream, wxT("</style>"), convMem, convFile);
+        OutputString(stream, wxT("</style>"));
 
         level --;
 
         OutputIndentation(stream, level);
-        OutputString(stream, wxT("</paragraphstyle>"), convMem, convFile);
+        OutputString(stream, wxT("</paragraphstyle>"));
     }
 
     return true;
 }
 
-/// Create style parameters
-wxString wxRichTextXMLHandler::CreateStyle(const wxRichTextAttr& attr, bool isPara)
+/// Create a string containing style attributes
+wxString wxRichTextXMLHandler::AddAttributes(const wxRichTextAttr& attr, bool isPara)
 {
     wxString str;
     if (attr.HasTextColour() && attr.GetTextColour().Ok())
-    {
-        str << wxT(" textcolor=\"#") << ColourToHexString(attr.GetTextColour()) << wxT("\"");
-    }
+        AddAttribute(str, wxT("textcolor"), attr.GetTextColour());
+
     if (attr.HasBackgroundColour() && attr.GetBackgroundColour().Ok())
-    {
-        str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\"");
-    }
+        AddAttribute(str, wxT("bgcolor"), attr.GetBackgroundColour());
 
     if (attr.HasFontSize())
-        str << wxT(" fontsize=\"") << attr.GetFontSize() << wxT("\"");
+        AddAttribute(str, wxT("fontsize"), attr.GetFontSize());
 
     if (attr.HasFontFamily())
-        str << wxT(" fontfamily=\"") << attr.GetFont().GetFamily() << wxT("\"");
+        AddAttribute(str, wxT("fontfamily"), attr.GetFontFamily());
 
     if (attr.HasFontItalic())
-        str << wxT(" fontstyle=\"") << attr.GetFontStyle() << wxT("\"");
+        AddAttribute(str, wxT("fontstyle"), attr.GetFontStyle());
 
     if (attr.HasFontWeight())
-        str << wxT(" fontweight=\"") << attr.GetFontWeight() << wxT("\"");
+        AddAttribute(str, wxT("fontweight"), attr.GetFontWeight());
 
     if (attr.HasFontUnderlined())
-        str << wxT(" fontunderlined=\"") << (int) attr.GetFontUnderlined() << wxT("\"");
+        AddAttribute(str, wxT("fontunderlined"), (int) attr.GetFontUnderlined());
 
     if (attr.HasFontFaceName())
-        str << wxT(" fontface=\"") << attr.GetFontFaceName() << wxT("\"");
+        AddAttribute(str, wxT("fontface"), attr.GetFontFaceName());
 
     if (attr.HasTextEffects())
     {
-        str << wxT(" texteffects=\"");
-        str << attr.GetTextEffects();
-        str << wxT("\"");
-
-        str << wxT(" texteffectflags=\"");
-        str << attr.GetTextEffectFlags();
-        str << wxT("\"");
+        AddAttribute(str, wxT("texteffects"), attr.GetTextEffects());
+        AddAttribute(str, wxT("texteffectflags"), attr.GetTextEffectFlags());
     }
 
     if (!attr.GetCharacterStyleName().empty())
-        str << wxT(" characterstyle=\"") << wxString(attr.GetCharacterStyleName()) << wxT("\"");
+        AddAttribute(str, wxT("characterstyle"), attr.GetCharacterStyleName());
 
     if (attr.HasURL())
-        str << wxT(" url=\"") << AttributeToXML(attr.GetURL()) << wxT("\"");
+        AddAttribute(str, wxT("url"), AttributeToXML(attr.GetURL()));
 
     if (isPara)
     {
         if (attr.HasAlignment())
-            str << wxT(" alignment=\"") << (int) attr.GetAlignment() << wxT("\"");
+            AddAttribute(str, wxT("alignment"), (int) attr.GetAlignment());
 
         if (attr.HasLeftIndent())
         {
-            str << wxT(" leftindent=\"") << (int) attr.GetLeftIndent() << wxT("\"");
-            str << wxT(" leftsubindent=\"") << (int) attr.GetLeftSubIndent() << wxT("\"");
+            AddAttribute(str, wxT("leftindent"), (int) attr.GetLeftIndent());
+            AddAttribute(str, wxT("leftsubindent"), (int) attr.GetLeftSubIndent());
         }
 
         if (attr.HasRightIndent())
-            str << wxT(" rightindent=\"") << (int) attr.GetRightIndent() << wxT("\"");
+            AddAttribute(str, wxT("rightindent"), (int) attr.GetRightIndent());
 
         if (attr.HasParagraphSpacingAfter())
-            str << wxT(" parspacingafter=\"") << (int) attr.GetParagraphSpacingAfter() << wxT("\"");
+            AddAttribute(str, wxT("parspacingafter"), (int) attr.GetParagraphSpacingAfter());
 
         if (attr.HasParagraphSpacingBefore())
-            str << wxT(" parspacingbefore=\"") << (int) attr.GetParagraphSpacingBefore() << wxT("\"");
+            AddAttribute(str, wxT("parspacingbefore"), (int) attr.GetParagraphSpacingBefore());
 
         if (attr.HasLineSpacing())
-            str << wxT(" linespacing=\"") << (int) attr.GetLineSpacing() << wxT("\"");
+            AddAttribute(str, wxT("linespacing"), (int) attr.GetLineSpacing());
 
         if (attr.HasBulletStyle())
-            str << wxT(" bulletstyle=\"") << (int) attr.GetBulletStyle() << wxT("\"");
+            AddAttribute(str, wxT("bulletstyle"), (int) attr.GetBulletStyle());
 
         if (attr.HasBulletNumber())
-            str << wxT(" bulletnumber=\"") << (int) attr.GetBulletNumber() << wxT("\"");
+            AddAttribute(str, wxT("bulletnumber"), (int) attr.GetBulletNumber());
 
         if (attr.HasBulletText())
         {
             // If using a bullet symbol, convert to integer in case it's a non-XML-friendly character.
             // Otherwise, assume it's XML-friendly text such as outline numbering, e.g. 1.2.3.1
-            if (!attr.GetBulletText().IsEmpty() && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL))
-                str << wxT(" bulletsymbol=\"") << (int) (attr.GetBulletText()[0]) << wxT("\"");
+            if (!attr.GetBulletText().empty() && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL))
+                AddAttribute(str, wxT("bulletsymbol"), (int) (attr.GetBulletText()[0]));
             else
-                str << wxT(" bullettext=\"") << attr.GetBulletText() << wxT("\"");
+                AddAttribute(str, wxT("bullettext"), attr.GetBulletText());
 
-            str << wxT(" bulletfont=\"") << attr.GetBulletFont() << wxT("\"");
+            AddAttribute(str, wxT("bulletfont"), attr.GetBulletFont());
         }
 
         if (attr.HasBulletName())
-            str << wxT(" bulletname=\"") << attr.GetBulletName() << wxT("\"");
+            AddAttribute(str, wxT("bulletname"), attr.GetBulletName());
 
         if (!attr.GetParagraphStyleName().empty())
-            str << wxT(" parstyle=\"") << wxString(attr.GetParagraphStyleName()) << wxT("\"");
+            AddAttribute(str, wxT("parstyle"), attr.GetParagraphStyleName());
 
         if (!attr.GetListStyleName().empty())
-            str << wxT(" liststyle=\"") << wxString(attr.GetListStyleName()) << wxT("\"");
+            AddAttribute(str, wxT("liststyle"), attr.GetListStyleName());
 
         if (attr.HasTabs())
         {
-            str << wxT(" tabs=\"");
+            wxString strTabs;
             size_t i;
             for (i = 0; i < attr.GetTabs().GetCount(); i++)
             {
-                if (i > 0)
-                    str << wxT(",");
-                str << attr.GetTabs()[i];
+                if (i > 0) strTabs << wxT(",");
+                strTabs << attr.GetTabs()[i];
             }
-            str << wxT("\"");
+            AddAttribute(str, wxT("tabs"), strTabs);
         }
 
         if (attr.HasPageBreak())
         {
-            str << wxT(" pagebreak=\"1\"");
+            AddAttribute(str, wxT("pagebreak"), 1);
         }
 
         if (attr.HasOutlineLevel())
-            str << wxT(" outlinelevel=\"") << (int) attr.GetOutlineLevel() << wxT("\"");
+            AddAttribute(str, wxT("outlinelevel"), (int) attr.GetOutlineLevel());
+    }
+    
+    AddAttribute(str, wxT("margin"), attr.GetTextBoxAttr().GetMargins());
+    AddAttribute(str, wxT("padding"), attr.GetTextBoxAttr().GetPadding());
+    AddAttribute(str, wxT("position"), attr.GetTextBoxAttr().GetPosition());
+    AddAttribute(str, wxT("border"), attr.GetTextBoxAttr().GetBorder());
+    AddAttribute(str, wxT("outline"), attr.GetTextBoxAttr().GetOutline());
+    AddAttribute(str, wxT("width"), attr.GetTextBoxAttr().GetWidth());
+    AddAttribute(str, wxT("height"), attr.GetTextBoxAttr().GetWidth());
+    
+    if (attr.GetTextBoxAttr().HasFloatMode())
+    {
+        wxString value;
+        if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
+            value = wxT("left");
+        else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
+            value = wxT("right");
+        else
+            value = wxT("none");
+        AddAttribute(str, wxT("float"), value);
+    }
 
+    if (attr.GetTextBoxAttr().HasClearMode())
+    {
+        wxString value;
+        if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_LEFT)
+            value = wxT("left");
+        else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_CLEAR_RIGHT)
+            value = wxT("right");
+        else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_CLEAR_BOTH)
+            value = wxT("both");
+        else
+            value = wxT("none");
+        AddAttribute(str, wxT("clear"), value);
     }
 
+    if (attr.GetTextBoxAttr().HasCollapseBorders())
+        AddAttribute(str, wxT("collapse-borders"), (int) attr.GetTextBoxAttr().GetCollapseBorders());
+
     return str;
 }
 
-/// Replace face name with current name for platform.
-/// TODO: introduce a virtual function or settable table to
-/// do this comprehensively.
-bool wxRichTextFixFaceName(wxString& facename)
+// Make a string from the given property. This can be overridden for custom variants.
+wxString wxRichTextXMLHandler::MakeStringFromProperty(const wxVariant& var)
 {
-    if (facename.IsEmpty())
-        return false;
+    return var.MakeString();
+}
 
-#ifdef __WXMSW__
-    if (facename == wxT("Times"))
-    {
-        facename = wxT("Times New Roman");
-        return true;
-    }
-    else if (facename == wxT("Helvetica"))
-    {
-        facename = wxT("Arial");
-        return true;
-    }
-    else if (facename == wxT("Courier"))
-    {
-        facename = wxT("Courier New");
-        return true;
-    }
-    else
-        return false;
-#else
-    if (facename == wxT("Times New Roman"))
-    {
-        facename = wxT("Times");
-        return true;
-    }
-    else if (facename == wxT("Arial"))
-    {
-        facename = wxT("Helvetica");
-        return true;
-    }
-    else if (facename == wxT("Courier New"))
+// Create a proprty from the string read from the XML file.
+wxVariant wxRichTextXMLHandler::MakePropertyFromString(const wxString& name, const wxString& value, const wxString& WXUNUSED(type))
+{
+    wxVariant var(value, name);
+    // TODO: use type to create using common types
+    return var;
+}
+
+// Write the properties
+bool wxRichTextXMLHandler::WriteProperties(wxOutputStream& stream, const wxRichTextProperties& properties, int level)
+{
+    if (properties.GetCount() > 0)
     {
-        facename = wxT("Courier");
-        return true;
+        level ++;
+
+        OutputIndentation(stream, level);
+        OutputString(stream, wxT("<properties"));
+
+        level ++;
+
+        size_t i;
+        for (i = 0; i < properties.GetCount(); i++)
+        {
+            const wxVariant& var = properties[i];
+            if (!var.IsNull())
+            {            
+                const wxString& name = var.GetName();
+                wxString value = MakeStringFromProperty(var);
+
+                OutputIndentation(stream, level);
+                OutputString(stream, wxT("<property name=\"") + name +
+                    wxT("\" type=\"") + var.GetType() + wxT("\" value=\""));
+                OutputStringEnt(stream, value);
+                OutputString(stream, wxT("\"/>\n"));
+            }
+        }
+
+        level --;
+
+        OutputIndentation(stream, level);
+        OutputString(stream, wxT("</properties>\n"));
+
+        level --;
     }
-    else
-        return false;
-#endif
+
+    return true;
 }
 
-/// Get style parameters
-bool wxRichTextXMLHandler::GetStyle(wxRichTextAttr& attr, wxXmlNode* node, bool isPara)
+
+#endif
+    // wxRICHTEXT_HAVE_DIRECT_OUTPUT
+
+#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
+bool wxRichTextXMLHandler::ExportXML(wxXmlNode* parent, wxRichTextObject& obj)
 {
-    wxString fontFacename;
-    int fontSize = 12;
-    wxFontFamily fontFamily = wxFONTFAMILY_DEFAULT;
-    wxFontWeight fontWeight = wxFONTWEIGHT_NORMAL;
-    wxFontStyle fontStyle = wxFONTSTYLE_NORMAL;
-    bool fontUnderlined = false;
-    const wxString emptyString; // save a temporary string construction in GetPropVal
+    obj.ExportXML(parent, this);
 
-    // int fontFlags = 0;
+    return true;
+}
 
-    fontFacename = node->GetAttribute(wxT("fontface"), emptyString);
-    if (!fontFacename.IsEmpty())
-    {
-        attr.SetFontFaceName(fontFacename);
-        if (GetFlags() & wxRICHTEXT_HANDLER_CONVERT_FACENAMES)
-            wxRichTextFixFaceName(fontFacename);
-    }
+bool wxRichTextXMLHandler::ExportStyleDefinition(wxXmlNode* parent, wxRichTextStyleDefinition* def)
+{
+    wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition);
+    wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition);
+    wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition);
 
-    wxString value;
-    value = node->GetAttribute(wxT("fontfamily"), emptyString);
-    if (!value.empty())
+    wxString baseStyle = def->GetBaseStyle();
+    wxString descr = def->GetDescription();
+    
+    wxXmlNode* defNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxEmptyString);
+    parent->AddChild(defNode);
+    if (!baseStyle.empty())
+        defNode->AddAttribute(wxT("basestyle"), baseStyle);
+    if (!descr.empty())
+        defNode->AddAttribute(wxT("description"), descr);
+        
+    wxXmlNode* styleNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("style"));
+    defNode->AddChild(styleNode);
+        
+    if (charDef)
     {
-        fontFamily = (wxFontFamily)wxAtoi(value);
-        attr.SetFontFamily(fontFamily);
+        defNode->SetName(wxT("characterstyle"));
+        AddAttributes(styleNode, def->GetStyle(), false);
     }
-
-    value = node->GetAttribute(wxT("fontstyle"), emptyString);
-    if (!value.empty())
+    else if (listDef)
     {
-        fontStyle = (wxFontStyle)wxAtoi(value);
-        attr.SetFontStyle(fontStyle);
-    }
+        defNode->SetName(wxT("liststyle"));
 
-    value = node->GetAttribute(wxT("fontsize"), emptyString);
-    if (!value.empty())
-    {
-        fontSize = wxAtoi(value);
-        attr.SetFontSize(fontSize);
-    }
+        if (!listDef->GetNextStyle().empty())
+            defNode->AddAttribute(wxT("nextstyle"), listDef->GetNextStyle());
 
-    value = node->GetAttribute(wxT("fontweight"), emptyString);
-    if (!value.empty())
-    {
-        fontWeight = (wxFontWeight)wxAtoi(value);
-        attr.SetFontWeight(fontWeight);
-    }
+        AddAttributes(styleNode, def->GetStyle(), true);
 
-    value = node->GetAttribute(wxT("fontunderlined"), emptyString);
-    if (!value.empty())
-    {
-        fontUnderlined = wxAtoi(value) != 0;
-        attr.SetFontUnderlined(fontUnderlined);
+        int i;
+        for (i = 0; i < 10; i ++)
+        {
+            wxRichTextAttr* levelAttr = listDef->GetLevelAttributes(i);
+            if (levelAttr)
+            {
+                wxXmlNode* levelNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("style"));
+                defNode->AddChild(levelNode);
+                levelNode->AddAttribute(wxT("level"), MakeString(i+1));
+                AddAttributes(levelNode, * levelAttr, true);
+            }
+        }
     }
-
-    value = node->GetAttribute(wxT("textcolor"), emptyString);
-    if (!value.empty())
+    else if (paraDef)
     {
-        if (value[0] == wxT('#'))
-            attr.SetTextColour(HexStringToColour(value.Mid(1)));
-        else
-            attr.SetTextColour(value);
-    }
+        defNode->SetName(wxT("paragraphstyle"));
 
-    value = node->GetAttribute(wxT("bgcolor"), emptyString);
-    if (!value.empty())
-    {
-        if (value[0] == wxT('#'))
-            attr.SetBackgroundColour(HexStringToColour(value.Mid(1)));
-        else
-            attr.SetBackgroundColour(value);
+        if (!paraDef->GetNextStyle().empty())
+            defNode->AddAttribute(wxT("nextstyle"), paraDef->GetNextStyle());
+
+        AddAttributes(styleNode, def->GetStyle(), true);
     }
 
-    value = node->GetAttribute(wxT("characterstyle"), emptyString);
-    if (!value.empty())
-        attr.SetCharacterStyleName(value);
+    return true;
+}
 
-    value = node->GetAttribute(wxT("texteffects"), emptyString);
-    if (!value.IsEmpty())
-    {
-        attr.SetTextEffects(wxAtoi(value));
-    }
+bool wxRichTextXMLHandler::AddAttributes(wxXmlNode* node, wxRichTextAttr& attr, bool isPara)
+{
+    if (attr.HasTextColour() && attr.GetTextColour().Ok())
+        node->AddAttribute(wxT("textcolor"), MakeString(attr.GetTextColour()));
+    if (attr.HasBackgroundColour() && attr.GetBackgroundColour().Ok())
+        node->AddAttribute(wxT("bgcolor"), MakeString(attr.GetBackgroundColour()));
 
-    value = node->GetAttribute(wxT("texteffectflags"), emptyString);
-    if (!value.IsEmpty())
+    if (attr.HasFontSize())
+        node->AddAttribute(wxT("fontsize"), MakeString(attr.GetFontSize()));
+    if (attr.HasFontFamily())
+        node->AddAttribute(wxT("fontfamily"), MakeString(attr.GetFontFamily()));
+    if (attr.HasFontItalic())
+        node->AddAttribute(wxT("fontstyle"), MakeString(attr.GetFontStyle()));
+    if (attr.HasFontWeight())
+        node->AddAttribute(wxT("fontweight"), MakeString(attr.GetFontWeight()));
+    if (attr.HasFontUnderlined())
+        node->AddAttribute(wxT("fontunderlined"), MakeString((int) attr.GetFontUnderlined()));
+    if (attr.HasFontFaceName())
+        node->AddAttribute(wxT("fontface"), attr.GetFontFaceName());
+
+    if (attr.HasTextEffects())
     {
-        attr.SetTextEffectFlags(wxAtoi(value));
+        node->AddAttribute(wxT("texteffects"), MakeString(attr.GetTextEffects()));
+        node->AddAttribute(wxT("texteffectflags"), MakeString(attr.GetTextEffectFlags()));
     }
+    if (attr.HasCharacterStyleName() && !attr.GetCharacterStyleName().empty())
+        node->AddAttribute(wxT("characterstyle"), attr.GetCharacterStyleName());
 
-    value = node->GetAttribute(wxT("url"), emptyString);
-    if (!value.empty())
-        attr.SetURL(value);
+    if (attr.HasURL())
+        node->AddAttribute(wxT("url"), attr.GetURL()); // TODO: do we need to wrap this in AttributeToXML?
 
-    // Set paragraph attributes
     if (isPara)
     {
-        value = node->GetAttribute(wxT("alignment"), emptyString);
-        if (!value.empty())
-            attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value));
-
-        int leftSubIndent = 0;
-        int leftIndent = 0;
-        bool hasLeftIndent = false;
-
-        value = node->GetAttribute(wxT("leftindent"), emptyString);
-        if (!value.empty())
-        {
-            leftIndent = wxAtoi(value);
-            hasLeftIndent = true;
-        }
+        if (attr.HasAlignment())
+            node->AddAttribute(wxT("alignment"), MakeString((int) attr.GetAlignment()));
 
-        value = node->GetAttribute(wxT("leftsubindent"), emptyString);
-        if (!value.empty())
+        if (attr.HasLeftIndent())
         {
-            leftSubIndent = wxAtoi(value);
-            hasLeftIndent = true;
+            node->AddAttribute(wxT("leftindent"), MakeString((int) attr.GetLeftIndent()));
+            node->AddAttribute(wxT("leftsubindent"), MakeString((int) attr.GetLeftSubIndent()));
         }
 
-        if (hasLeftIndent)
-            attr.SetLeftIndent(leftIndent, leftSubIndent);
-
-        value = node->GetAttribute(wxT("rightindent"), emptyString);
-        if (!value.empty())
-            attr.SetRightIndent(wxAtoi(value));
+        if (attr.HasRightIndent())
+            node->AddAttribute(wxT("rightindent"), MakeString((int) attr.GetRightIndent()));
 
-        value = node->GetAttribute(wxT("parspacingbefore"), emptyString);
-        if (!value.empty())
-            attr.SetParagraphSpacingBefore(wxAtoi(value));
+        if (attr.HasParagraphSpacingAfter())
+            node->AddAttribute(wxT("parspacingafter"), MakeString((int) attr.GetParagraphSpacingAfter()));
 
-        value = node->GetAttribute(wxT("parspacingafter"), emptyString);
-        if (!value.empty())
-            attr.SetParagraphSpacingAfter(wxAtoi(value));
+        if (attr.HasParagraphSpacingBefore())
+            node->AddAttribute(wxT("parspacingbefore"), MakeString((int) attr.GetParagraphSpacingBefore()));
 
-        value = node->GetAttribute(wxT("linespacing"), emptyString);
-        if (!value.empty())
-            attr.SetLineSpacing(wxAtoi(value));
+        if (attr.HasLineSpacing())
+            node->AddAttribute(wxT("linespacing"), MakeString((int) attr.GetLineSpacing()));
 
-        value = node->GetAttribute(wxT("bulletstyle"), emptyString);
-        if (!value.empty())
-            attr.SetBulletStyle(wxAtoi(value));
+        if (attr.HasBulletStyle())
+            node->AddAttribute(wxT("bulletstyle"), MakeString((int) attr.GetBulletStyle()));
 
-        value = node->GetAttribute(wxT("bulletnumber"), emptyString);
-        if (!value.empty())
-            attr.SetBulletNumber(wxAtoi(value));
+        if (attr.HasBulletNumber())
+            node->AddAttribute(wxT("bulletnumber"), MakeString((int) attr.GetBulletNumber()));
 
-        value = node->GetAttribute(wxT("bulletsymbol"), emptyString);
-        if (!value.empty())
+        if (attr.HasBulletText())
         {
-            wxChar ch = wxAtoi(value);
-            wxString s;
-            s << ch;
-            attr.SetBulletText(s);
-        }
-
-        value = node->GetAttribute(wxT("bullettext"), emptyString);
-        if (!value.empty())
-            attr.SetBulletText(value);
+            // If using a bullet symbol, convert to integer in case it's a non-XML-friendly character.
+            // Otherwise, assume it's XML-friendly text such as outline numbering, e.g. 1.2.3.1
+            if (!attr.GetBulletText().empty() && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL))
+                node->AddAttribute(wxT("bulletsymbol"), MakeString((int) (attr.GetBulletText()[0])));
+            else
+                node->AddAttribute(wxT("bullettext"), attr.GetBulletText());
 
-        value = node->GetAttribute(wxT("bulletfont"), emptyString);
-        if (!value.empty())
-            attr.SetBulletFont(value);
+            if (!attr.GetBulletFont().empty())
+                node->AddAttribute(wxT("bulletfont"), attr.GetBulletFont());
+        }
 
-        value = node->GetAttribute(wxT("bulletname"), emptyString);
-        if (!value.empty())
-            attr.SetBulletName(value);
+        if (attr.HasBulletName())
+            node->AddAttribute(wxT("bulletname"), attr.GetBulletName());
 
-        value = node->GetAttribute(wxT("parstyle"), emptyString);
-        if (!value.empty())
-            attr.SetParagraphStyleName(value);
+        if (!attr.GetParagraphStyleName().empty())
+            node->AddAttribute(wxT("parstyle"), attr.GetParagraphStyleName());
 
-        value = node->GetAttribute(wxT("liststyle"), emptyString);
-        if (!value.empty())
-            attr.SetListStyleName(value);
+        if (!attr.GetListStyleName().empty())
+            node->AddAttribute(wxT("liststyle"), attr.GetListStyleName());
 
-        value = node->GetAttribute(wxT("tabs"), emptyString);
-        if (!value.empty())
+        if (attr.HasTabs())
         {
-            wxArrayInt tabs;
-            wxStringTokenizer tkz(value, wxT(","));
-            while (tkz.HasMoreTokens())
+            wxString tabs;
+            size_t i;
+            for (i = 0; i < attr.GetTabs().GetCount(); i++)
             {
-                wxString token = tkz.GetNextToken();
-                tabs.Add(wxAtoi(token));
+                if (i > 0)
+                    tabs << wxT(",");
+                tabs << attr.GetTabs()[i];
             }
-            attr.SetTabs(tabs);
+            node->AddAttribute(wxT("tabs"), tabs);
         }
 
-        value = node->GetAttribute(wxT("pagebreak"), emptyString);
-        if (!value.IsEmpty())
-        {
-            attr.SetPageBreak(wxAtoi(value) != 0);
-        }
+        if (attr.HasPageBreak())
+            node->AddAttribute(wxT("pagebreak"), wxT("1"));
 
-        value = node->GetAttribute(wxT("outlinelevel"), emptyString);
-        if (!value.IsEmpty())
-        {
-            attr.SetOutlineLevel(wxAtoi(value));
-        }
+        if (attr.HasOutlineLevel())
+            node->AddAttribute(wxT("outlinelevel"), MakeString((int) attr.GetOutlineLevel()));
+    }
+
+    AddAttribute(node, wxT("margin"), attr.GetTextBoxAttr().GetMargins());
+    AddAttribute(node, wxT("padding"), attr.GetTextBoxAttr().GetPadding());
+    AddAttribute(node, wxT("position"), attr.GetTextBoxAttr().GetPosition());
+    AddAttribute(node, wxT("border"), attr.GetTextBoxAttr().GetBorder());
+    AddAttribute(node, wxT("outline"), attr.GetTextBoxAttr().GetOutline());
+    AddAttribute(node, wxT("width"), attr.GetTextBoxAttr().GetWidth());
+    AddAttribute(node, wxT("height"), attr.GetTextBoxAttr().GetWidth());
+
+    if (attr.GetTextBoxAttr().HasFloatMode())
+    {
+        wxString value;
+        if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
+            value = wxT("left");
+        else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
+            value = wxT("right");
+        else
+            value = wxT("none");
+        AddAttribute(node, wxT("float"), value);
     }
 
+    if (attr.GetTextBoxAttr().HasClearMode())
+    {
+        wxString value;
+        if (attr.GetTextBoxAttr().GetClearMode() == wxTEXT_BOX_ATTR_CLEAR_LEFT)
+            value = wxT("left");
+        else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_CLEAR_RIGHT)
+            value = wxT("right");
+        else if (attr.GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_CLEAR_BOTH)
+            value = wxT("both");
+        else
+            value = wxT("none");
+        AddAttribute(node, wxT("clear"), value);
+    }
+
+    if (attr.GetTextBoxAttr().HasCollapseBorders())
+        AddAttribute(node, wxT("collapse-borders"), (int) attr.GetTextBoxAttr().GetCollapseBorders());
+
     return true;
 }
 
-#endif
-    // wxUSE_STREAMS
+bool wxRichTextXMLHandler::WriteProperties(wxXmlNode* node, const wxRichTextProperties& properties)
+{
+    if (properties.GetCount() > 0)
+    {
+        wxXmlNode* propertiesNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("properties"));
+        node->AddChild(propertiesNode);
+        size_t i;
+        for (i = 0; i < properties.GetCount(); i++)
+        {
+            const wxVariant& var = properties[i];
+            if (!var.IsNull())
+            {
+                wxXmlNode* propertyNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property"));
+                propertiesNode->AddChild(propertyNode);
+
+                const wxString& name = var.GetName();
+                wxString value = MakeStringFromProperty(var);
+
+                AddAttribute(propertyNode, wxT("name"), name);
+                AddAttribute(propertyNode, wxT("type"), var.GetType());
+                AddAttribute(propertyNode, wxT("value"), value);
+            }
+        }
+    }
+    return true;
+}
+
+#endif
+    // wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
+
+/// Replace face name with current name for platform.
+/// TODO: introduce a virtual function or settable table to
+/// do this comprehensively.
+bool wxRichTextFixFaceName(wxString& facename)
+{
+    if (facename.empty())
+        return false;
+
+#ifdef __WXMSW__
+    if (facename == wxT("Times"))
+    {
+        facename = wxT("Times New Roman");
+        return true;
+    }
+    else if (facename == wxT("Helvetica"))
+    {
+        facename = wxT("Arial");
+        return true;
+    }
+    else if (facename == wxT("Courier"))
+    {
+        facename = wxT("Courier New");
+        return true;
+    }
+    else
+        return false;
+#else
+    if (facename == wxT("Times New Roman"))
+    {
+        facename = wxT("Times");
+        return true;
+    }
+    else if (facename == wxT("Arial"))
+    {
+        facename = wxT("Helvetica");
+        return true;
+    }
+    else if (facename == wxT("Courier New"))
+    {
+        facename = wxT("Courier");
+        return true;
+    }
+    else
+        return false;
+#endif
+}
+
+static inline long wxRichTextColourStringToLong(const wxString& colStr)
+{
+    if (!colStr.IsEmpty())
+    {
+        wxColour col(colStr);
+        return col.GetRGB();
+    }
+    else
+        return 0;
+}
+
+static inline wxTextAttrDimension wxRichTextParseDimension(const wxString& dimStr)
+{
+    wxString valuePart = dimStr.BeforeFirst(wxT(','));
+    wxString flagsPart;
+    if (dimStr.Contains(wxT(",")))
+        flagsPart = dimStr.AfterFirst(wxT(','));
+    wxTextAttrDimension dim;
+    dim.SetValue(wxAtoi(valuePart));
+    dim.SetFlags(wxAtoi(flagsPart));
+
+    return dim;
+}
+
+/// Import style parameters
+bool wxRichTextXMLHandler::ImportStyle(wxRichTextAttr& attr, wxXmlNode* node, bool isPara)
+{
+    wxXmlAttribute* xmlAttr = node->GetAttributes();
+    bool found;
+    while (xmlAttr)
+    {
+        const wxString& name = xmlAttr->GetName();
+        const wxString& value = xmlAttr->GetValue();
+        found = true;
+
+        if (name == wxT("fontface"))
+        {
+            if (!value.empty())
+            {
+                wxString v = value;
+                if (GetFlags() & wxRICHTEXT_HANDLER_CONVERT_FACENAMES)
+                    wxRichTextFixFaceName(v);
+                attr.SetFontFaceName(v);
+            }
+        }
+        else if (name == wxT("fontfamily"))
+        {
+            if (!value.empty())
+                attr.SetFontFamily((wxFontFamily)wxAtoi(value));
+        }
+        else if (name == wxT("fontstyle"))
+        {
+            if (!value.empty())
+                attr.SetFontStyle((wxFontStyle)wxAtoi(value));
+        }
+        else if (name == wxT("fontsize"))
+        {
+            if (!value.empty())
+                attr.SetFontSize(wxAtoi(value));
+        }
+        else if (name == wxT("fontweight"))
+        {
+            if (!value.empty())
+                attr.SetFontWeight((wxFontWeight) wxAtoi(value));
+        }
+        else if (name == wxT("fontunderlined"))
+        {
+            if (!value.empty())
+                attr.SetFontUnderlined(wxAtoi(value) != 0);
+        }
+        else if (name == wxT("textcolor"))
+        {
+            if (!value.empty())
+            {
+                if (value[0] == wxT('#'))
+                    attr.SetTextColour(HexStringToColour(value.Mid(1)));
+                else
+                    attr.SetTextColour(value);
+            }
+        }
+        else if (name == wxT("bgcolor"))
+        {
+            if (!value.empty())
+            {
+                if (value[0] == wxT('#'))
+                    attr.SetBackgroundColour(HexStringToColour(value.Mid(1)));
+                else
+                    attr.SetBackgroundColour(value);
+            }
+        }
+        else if (name == wxT("characterstyle"))
+        {
+            if (!value.empty())
+                attr.SetCharacterStyleName(value);
+        }
+        else if (name == wxT("texteffects"))
+        {
+            if (!value.empty())
+                attr.SetTextEffects(wxAtoi(value));
+        }
+        else if (name == wxT("texteffectflags"))
+        {
+            if (!value.empty())
+                attr.SetTextEffectFlags(wxAtoi(value));
+        }
+        else if (name == wxT("url"))
+        {
+            if (!value.empty())
+                attr.SetURL(value);
+        }
+        else if (isPara)
+        {
+            if (name == wxT("alignment"))
+            {
+                if (!value.empty())
+                    attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value));
+            }
+            else if (name == wxT("leftindent"))
+            {
+                if (!value.empty())
+                    attr.SetLeftIndent(wxAtoi(value), attr.GetLeftSubIndent());
+            }
+            else if (name == wxT("leftsubindent"))
+            {
+                if (!value.empty())
+                    attr.SetLeftIndent(attr.GetLeftIndent(), wxAtoi(value));
+            }
+            else if (name == wxT("rightindent"))
+            {
+                if (!value.empty())
+                    attr.SetRightIndent(wxAtoi(value));
+            }
+            else if (name == wxT("parspacingbefore"))
+            {
+                if (!value.empty())
+                    attr.SetParagraphSpacingBefore(wxAtoi(value));
+            }
+            else if (name == wxT("parspacingafter"))
+            {
+                if (!value.empty())
+                    attr.SetParagraphSpacingAfter(wxAtoi(value));
+            }
+            else if (name == wxT("linespacing"))
+            {
+                if (!value.empty())
+                    attr.SetLineSpacing(wxAtoi(value));
+            }
+            else if (name == wxT("bulletstyle"))
+            {
+                if (!value.empty())
+                    attr.SetBulletStyle(wxAtoi(value));
+            }
+            else if (name == wxT("bulletnumber"))
+            {
+                if (!value.empty())
+                    attr.SetBulletNumber(wxAtoi(value));
+            }
+            else if (name == wxT("bulletsymbol"))
+            {
+                if (!value.empty())
+                {
+                    wxChar ch = wxAtoi(value);
+                    wxString s;
+                    s << ch;
+                    attr.SetBulletText(s);
+                }
+            }
+            else if (name == wxT("bullettext"))
+            {
+                if (!value.empty())
+                {
+                    attr.SetBulletText(value);
+                }
+            }
+            else if (name == wxT("bulletfont"))
+            {
+                if (!value.empty())
+                {
+                    attr.SetBulletFont(value);
+                }
+            }
+            else if (name == wxT("bulletname"))
+            {
+                if (!value.empty())
+                {
+                    attr.SetBulletName(value);
+                }
+            }
+            else if (name == wxT("parstyle"))
+            {
+                if (!value.empty())
+                {
+                    attr.SetParagraphStyleName(value);
+                }
+            }
+            else if (name == wxT("liststyle"))
+            {
+                if (!value.empty())
+                {
+                    attr.SetListStyleName(value);
+                }
+            }
+            else if (name == wxT("tabs"))
+            {
+                if (!value.empty())
+                {
+                    wxArrayInt tabs;
+                    wxStringTokenizer tkz(value, wxT(","));
+                    while (tkz.HasMoreTokens())
+                    {
+                        wxString token = tkz.GetNextToken();
+                        tabs.Add(wxAtoi(token));
+                    }
+                    attr.SetTabs(tabs);
+                }
+            }
+            else if (name == wxT("pagebreak"))
+            {
+                if (!value.empty())
+                {
+                    attr.SetPageBreak(wxAtoi(value) != 0);
+                }
+            }
+            else if (name == wxT("outlinelevel"))
+            {
+                if (!value.empty())
+                {
+                    attr.SetOutlineLevel(wxAtoi(value));
+                }
+            }
+            else
+                found = false;
+        }
+        else
+            found = false;
+
+        if (!found)
+        {
+            // Box attributes
+
+            if (name == wxT("width"))
+            {
+                attr.GetTextBoxAttr().GetWidth().SetValue(wxRichTextParseDimension(value));
+            }
+            else if (name == wxT("height"))
+            {
+                attr.GetTextBoxAttr().GetHeight().SetValue(wxRichTextParseDimension(value));
+            }
+
+            else if (name == wxT("float"))
+            {
+                if (value == wxT("left"))
+                    attr.GetTextBoxAttr().SetFloatMode(wxTEXT_BOX_ATTR_FLOAT_LEFT);
+                else if (value == wxT("right"))
+                    attr.GetTextBoxAttr().SetFloatMode(wxTEXT_BOX_ATTR_FLOAT_RIGHT);
+                else if (value == wxT("none"))
+                    attr.GetTextBoxAttr().SetFloatMode(wxTEXT_BOX_ATTR_FLOAT_NONE);
+            }
+            else if (name == wxT("clear"))
+            {
+                if (value == wxT("left"))
+                    attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_LEFT);
+                else if (value == wxT("right"))
+                    attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_RIGHT);
+                else if (value == wxT("both"))
+                    attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_BOTH);
+                else if (value == wxT("none"))
+                    attr.GetTextBoxAttr().SetClearMode(wxTEXT_BOX_ATTR_CLEAR_NONE);
+            }
+            else if (name == wxT("collapse-borders"))
+                attr.GetTextBoxAttr().SetCollapseBorders(value == wxT("1"));
+
+            else if (name.Contains(wxT("border-")))
+            {
+                if (name == wxT("border-left-style"))
+                    attr.GetTextBoxAttr().GetBorder().GetLeft().SetStyle(wxAtoi(value));
+                else if (name == wxT("border-right-style"))
+                    attr.GetTextBoxAttr().GetBorder().GetRight().SetStyle(wxAtoi(value));
+                else if (name == wxT("border-top-style"))
+                    attr.GetTextBoxAttr().GetBorder().GetTop().SetStyle(wxAtoi(value));
+                else if (name == wxT("border-bottom-style"))
+                    attr.GetTextBoxAttr().GetBorder().GetBottom().SetStyle(wxAtoi(value));
+
+                else if (name == wxT("border-left-colour"))
+                    attr.GetTextBoxAttr().GetBorder().GetLeft().SetColour(wxRichTextColourStringToLong(value));
+                else if (name == wxT("border-right-colour"))
+                    attr.GetTextBoxAttr().GetBorder().GetRight().SetColour(wxRichTextColourStringToLong(value));
+                else if (name == wxT("border-top-colour"))
+                    attr.GetTextBoxAttr().GetBorder().GetTop().SetColour(wxRichTextColourStringToLong(value));
+                else if (name == wxT("border-bottom-colour"))
+                    attr.GetTextBoxAttr().GetBorder().GetBottom().SetColour(wxRichTextColourStringToLong(value));
+
+                else if (name == wxT("border-left-width"))
+                    attr.GetTextBoxAttr().GetBorder().GetLeft().SetWidth(wxRichTextParseDimension(value));
+                else if (name == wxT("border-right-width"))
+                    attr.GetTextBoxAttr().GetBorder().GetRight().SetWidth(wxRichTextParseDimension(value));
+                else if (name == wxT("border-top-width"))
+                    attr.GetTextBoxAttr().GetBorder().GetTop().SetWidth(wxRichTextParseDimension(value));
+                else if (name == wxT("border-bottom-width"))
+                    attr.GetTextBoxAttr().GetBorder().GetBottom().SetWidth(wxRichTextParseDimension(value));
+            }
+            else if (name.Contains(wxT("outline-")))
+            {
+                if (name == wxT("outline-left-style"))
+                    attr.GetTextBoxAttr().GetOutline().GetLeft().SetStyle(wxAtoi(value));
+                else if (name == wxT("outline-right-style"))
+                    attr.GetTextBoxAttr().GetOutline().GetRight().SetStyle(wxAtoi(value));
+                else if (name == wxT("outline-top-style"))
+                    attr.GetTextBoxAttr().GetOutline().GetTop().SetStyle(wxAtoi(value));
+                else if (name == wxT("outline-bottom-style"))
+                    attr.GetTextBoxAttr().GetOutline().GetBottom().SetStyle(wxAtoi(value));
+
+                else if (name == wxT("outline-left-colour"))
+                    attr.GetTextBoxAttr().GetOutline().GetLeft().SetColour(wxRichTextColourStringToLong(value));
+                else if (name == wxT("outline-right-colour"))
+                    attr.GetTextBoxAttr().GetOutline().GetRight().SetColour(wxRichTextColourStringToLong(value));
+                else if (name == wxT("outline-top-colour"))
+                    attr.GetTextBoxAttr().GetOutline().GetTop().SetColour(wxRichTextColourStringToLong(value));
+                else if (name == wxT("outline-bottom-colour"))
+                    attr.GetTextBoxAttr().GetOutline().GetBottom().SetColour(wxRichTextColourStringToLong(value));
+
+                else if (name == wxT("outline-left-width"))
+                    attr.GetTextBoxAttr().GetOutline().GetLeft().SetWidth(wxRichTextParseDimension(value));
+                else if (name == wxT("outline-right-width"))
+                    attr.GetTextBoxAttr().GetOutline().GetRight().SetWidth(wxRichTextParseDimension(value));
+                else if (name == wxT("outline-top-width"))
+                    attr.GetTextBoxAttr().GetOutline().GetTop().SetWidth(wxRichTextParseDimension(value));
+                else if (name == wxT("outline-bottom-width"))
+                    attr.GetTextBoxAttr().GetOutline().GetBottom().SetWidth(wxRichTextParseDimension(value));
+            }
+            else if (name.Contains(wxT("margin-")))
+            {
+                if (name == wxT("margin-left"))
+                    attr.GetTextBoxAttr().GetMargins().GetLeft().SetValue(wxRichTextParseDimension(value));
+                else if (name == wxT("margin-right"))
+                    attr.GetTextBoxAttr().GetMargins().GetRight().SetValue(wxRichTextParseDimension(value));
+                else if (name == wxT("margin-top"))
+                    attr.GetTextBoxAttr().GetMargins().GetTop().SetValue(wxRichTextParseDimension(value));
+                else if (name == wxT("margin-bottom"))
+                    attr.GetTextBoxAttr().GetMargins().GetBottom().SetValue(wxRichTextParseDimension(value));
+            }
+            else if (name.Contains(wxT("padding-")))
+            {
+                if (name == wxT("padding-left"))
+                    attr.GetTextBoxAttr().GetPadding().GetLeft().SetValue(wxRichTextParseDimension(value));
+                else if (name == wxT("padding-right"))
+                    attr.GetTextBoxAttr().GetPadding().GetRight().SetValue(wxRichTextParseDimension(value));
+                else if (name == wxT("padding-top"))
+                    attr.GetTextBoxAttr().GetPadding().GetTop().SetValue(wxRichTextParseDimension(value));
+                else if (name == wxT("padding-bottom"))
+                    attr.GetTextBoxAttr().GetPadding().GetBottom().SetValue(wxRichTextParseDimension(value));
+            }
+            else if (name.Contains(wxT("position-")))
+            {
+                if (name == wxT("position-left"))
+                    attr.GetTextBoxAttr().GetPosition().GetLeft().SetValue(wxRichTextParseDimension(value));
+                else if (name == wxT("position-right"))
+                    attr.GetTextBoxAttr().GetPosition().GetRight().SetValue(wxRichTextParseDimension(value));
+                else if (name == wxT("position-top"))
+                    attr.GetTextBoxAttr().GetPosition().GetTop().SetValue(wxRichTextParseDimension(value));
+                else if (name == wxT("position-bottom"))
+                    attr.GetTextBoxAttr().GetPosition().GetBottom().SetValue(wxRichTextParseDimension(value));
+            }
+        }
+        
+        xmlAttr = xmlAttr->GetNext();
+    }
+
+    return true;
+}
+
+#endif
+    // wxUSE_STREAMS
+
+// Import this object from XML
+bool wxRichTextObject::ImportFromXML(wxRichTextBuffer* WXUNUSED(buffer), wxXmlNode* node, wxRichTextXMLHandler* handler)
+{
+    handler->ImportProperties(this, node);
+    handler->ImportStyle(GetAttributes(), node, UsesParagraphAttributes());
+
+    return true;
+}
+
+#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
+// Export this object directly to the given stream.
+bool wxRichTextObject::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler)
+{
+    ::OutputIndentation(stream, indent);
+    ::OutputString(stream, wxT("<") + GetXMLNodeName(), handler->GetConvMem(), handler->GetConvFile());
+
+    wxString style = handler->AddAttributes(GetAttributes(), true);
+
+    ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
+
+    if (GetProperties().GetCount() > 0)
+    {
+        handler->WriteProperties(stream, GetProperties(), indent);
+    }
+    
+    wxRichTextCompositeObject* composite = wxDynamicCast(this, wxRichTextCompositeObject);
+    if (composite)
+    {
+        size_t i;
+        for (i = 0; i < composite->GetChildCount(); i++)
+        {
+            wxRichTextObject* child = composite->GetChild(i);
+            child->ExportXML(stream, indent+1, handler);
+        }
+    }
+
+    ::OutputIndentation(stream, indent);
+    ::OutputString(stream, wxT("</") + GetXMLNodeName() + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
+    return true;
+}
+#endif
+
+#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
+// Export this object to the given parent node, usually creating at least one child node.
+bool wxRichTextObject::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler)
+{
+    wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, GetXMLNodeName());
+    parent->AddChild(elementNode);
+    handler->AddAttributes(elementNode, GetAttributes(), true);
+    handler->WriteProperties(elementNode, GetProperties());
+
+    wxRichTextCompositeObject* composite = wxDynamicCast(this, wxRichTextCompositeObject);
+    if (composite)
+    {
+        size_t i;
+        for (i = 0; i < composite->GetChildCount(); i++)
+        {
+            wxRichTextObject* child = composite->GetChild(i);
+            child->ExportXML(elementNode, handler);
+        }
+    }
+    return true;
+}
+#endif
+
+
+// Import this object from XML
+bool wxRichTextPlainText::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler)
+{
+    wxRichTextObject::ImportFromXML(buffer, node, handler);
+    
+    if (node->GetName() == wxT("text"))
+    {
+        wxString text;
+        wxXmlNode* textChild = node->GetChildren();
+        while (textChild)
+        {
+            if (textChild->GetType() == wxXML_TEXT_NODE ||
+                textChild->GetType() == wxXML_CDATA_SECTION_NODE)
+            {
+                wxString text2 = textChild->GetContent();
+
+                // Strip whitespace from end
+                if (!text2.empty() && text2[text2.length()-1] == wxT('\n'))
+                    text2 = text2.Mid(0, text2.length()-1);
+
+                if (!text2.empty() && text2[0] == wxT('"'))
+                    text2 = text2.Mid(1);
+                if (!text2.empty() && text2[text2.length()-1] == wxT('"'))
+                    text2 = text2.Mid(0, text2.length() - 1);
+
+                text += text2;
+            }
+            textChild = textChild->GetNext();
+        }
+        
+        SetText(text);
+    }
+    else if (node->GetName() == wxT("symbol"))
+    {
+        // This is a symbol that XML can't read in the normal way
+        wxString text;
+        wxXmlNode* textChild = node->GetChildren();
+        while (textChild)
+        {
+            if (textChild->GetType() == wxXML_TEXT_NODE ||
+                textChild->GetType() == wxXML_CDATA_SECTION_NODE)
+            {
+                wxString text2 = textChild->GetContent();
+                text += text2;
+            }
+            textChild = textChild->GetNext();
+        }
+
+        wxString actualText;
+        actualText << (wxChar) wxAtoi(text);
+        SetText(actualText);
+    }
+    else
+        return false;
+
+    return true;
+}
+
+#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
+// Export this object directly to the given stream.
+bool wxRichTextPlainText::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler)
+{
+    wxString style = handler->AddAttributes(GetAttributes(), false);
+
+    int i;
+    int last = 0;
+    const wxString& text = GetText();
+    int len = (int) text.Length();
+
+    if (len == 0)
+    {
+        i = 0;
+        ::OutputIndentation(stream, indent);
+        ::OutputString(stream, wxT("<text"), handler->GetConvMem(), handler->GetConvFile());
+        ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
+        if (GetProperties().GetCount() > 0)
+        {
+            handler->WriteProperties(stream, GetProperties(), indent);
+            ::OutputIndentation(stream, indent);
+        }
+        ::OutputString(stream, wxT("</text>"), handler->GetConvMem(), handler->GetConvFile());
+    }
+    else for (i = 0; i < len; i++)
+    {
+#if wxUSE_UNICODE
+        int c = (int) text[i];
+#else
+        int c = (int) wxUChar(text[i]);
+#endif
+        if ((c < 32 || c == 34) && /* c != 9 && */ c != 10 && c != 13)
+        {
+            if (i > 0)
+            {
+                wxString fragment(text.Mid(last, i-last));
+                if (!fragment.empty())
+                {
+                    ::OutputIndentation(stream, indent);
+                    ::OutputString(stream, wxT("<text"), handler->GetConvMem(), handler->GetConvFile());
+
+                    ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
+
+                    if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')))
+                    {
+                        ::OutputString(stream, wxT("\""), handler->GetConvMem(), handler->GetConvFile());
+                        ::OutputStringEnt(stream, fragment, handler->GetConvMem(), handler->GetConvFile());
+                        ::OutputString(stream, wxT("\""), handler->GetConvMem(), handler->GetConvFile());
+                    }
+                    else
+                        ::OutputStringEnt(stream, fragment, handler->GetConvMem(), handler->GetConvFile());
+
+                    if (GetProperties().GetCount() > 0)
+                    {
+                        handler->WriteProperties(stream, GetProperties(), indent);
+                        ::OutputIndentation(stream, indent);
+                    }
+                    ::OutputString(stream, wxT("</text>"), handler->GetConvMem(), handler->GetConvFile());
+                }
+            }
+
+
+            // Output this character as a number in a separate tag, because XML can't cope
+            // with entities below 32 except for 10 and 13
+            last = i + 1;
+            ::OutputIndentation(stream, indent);
+            ::OutputString(stream, wxT("<symbol"), handler->GetConvMem(), handler->GetConvFile());
+
+            ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
+            ::OutputString(stream, wxString::Format(wxT("%d"), c), handler->GetConvMem(), handler->GetConvFile());
+
+            if (GetProperties().GetCount() > 0)
+            {
+                handler->WriteProperties(stream, GetProperties(), indent);
+                ::OutputIndentation(stream, indent);
+            }
+            ::OutputString(stream, wxT("</symbol>"), handler->GetConvMem(), handler->GetConvFile());
+        }
+    }
+
+    wxString fragment;
+    if (last == 0)
+        fragment = text;
+    else
+        fragment = text.Mid(last, i-last);
+
+    if (last < len)
+    {
+        ::OutputIndentation(stream, indent);
+        ::OutputString(stream, wxT("<text"), handler->GetConvMem(), handler->GetConvFile());
+
+        ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
+        
+        if (GetProperties().GetCount() > 0)
+        {
+            handler->WriteProperties(stream, GetProperties(), indent);
+            ::OutputIndentation(stream, indent);
+        }
+
+        if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')))
+        {
+            ::OutputString(stream, wxT("\""), handler->GetConvMem(), handler->GetConvFile());
+            ::OutputStringEnt(stream, fragment, handler->GetConvMem(), handler->GetConvFile());
+            ::OutputString(stream, wxT("\""), handler->GetConvMem(), handler->GetConvFile());
+        }
+        else
+            ::OutputStringEnt(stream, fragment, handler->GetConvMem(), handler->GetConvFile());
+
+        ::OutputString(stream, wxT("</text>"), handler->GetConvMem(), handler->GetConvFile());
+    }
+    return true;
+}
+#endif
+
+#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
+// Export this object to the given parent node, usually creating at least one child node.
+bool wxRichTextPlainText::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler)
+{
+    int i;
+    int last = 0;
+    const wxString& text = GetText();
+    int len = (int) text.Length();
+
+    if (len == 0)
+    {
+        i = 0;
+
+        wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("text"));
+        parent->AddChild(elementNode);
+
+        handler->AddAttributes(elementNode, GetAttributes(), false);
+        handler->WriteProperties(elementNode, GetProperties());
+    }
+    else for (i = 0; i < len; i++)
+    {
+#if wxUSE_UNICODE
+        int c = (int) text[i];
+#else
+        int c = (int) wxUChar(text[i]);
+#endif
+        if ((c < 32 || c == 34) && c != 10 && c != 13)
+        {
+            if (i > 0)
+            {
+                wxString fragment(text.Mid(last, i-last));
+                if (!fragment.empty())
+                {
+                    // TODO: I'm assuming wxXmlDocument will output quotes if necessary
+                    wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("text"));
+                    parent->AddChild(elementNode);
+                    handler->AddAttributes(elementNode, GetAttributes(), false);
+                    handler->WriteProperties(elementNode, GetProperties());
+                    
+                    wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"));
+                    elementNode->AddChild(textNode);
+
+                    if (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' '))
+                        fragment = wxT("\"") + fragment + wxT("\"");
+
+                    textNode->SetContent(fragment);
+                }
+            }
+
+
+            // Output this character as a number in a separate tag, because XML can't cope
+            // with entities below 32 except for 10 and 13
+            
+            wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("symbol"));
+            parent->AddChild(elementNode);
+
+            handler->AddAttributes(elementNode, GetAttributes(), false);
+            handler->WriteProperties(elementNode, GetProperties());
+
+            wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"));
+            elementNode->AddChild(textNode);
+            textNode->SetContent(wxString::Format(wxT("%d"), c));
+
+            last = i + 1;
+        }
+    }
+
+    wxString fragment;
+    if (last == 0)
+        fragment = text;
+    else
+        fragment = text.Mid(last, i-last);
+
+    if (last < len)
+    {
+        wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("text"));
+        parent->AddChild(elementNode);
+        handler->AddAttributes(elementNode, GetAttributes(), false);
+                    
+        wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"));
+        elementNode->AddChild(textNode);
+
+        if (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' '))
+            fragment = wxT("\"") + fragment + wxT("\"");
+
+        textNode->SetContent(fragment);            
+    }
+    return true;
+}
+#endif
+
+
+// Import this object from XML
+bool wxRichTextImage::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler)
+{
+    wxRichTextObject::ImportFromXML(buffer, node, handler);
+
+    wxBitmapType imageType = wxBITMAP_TYPE_PNG;
+    wxString value = node->GetAttribute(wxT("imagetype"), wxEmptyString);
+    if (!value.empty())
+    {
+        int type = wxAtoi(value);
+
+        // note: 0 == wxBITMAP_TYPE_INVALID
+        if (type <= 0 || type >= wxBITMAP_TYPE_MAX)
+        {
+            wxLogWarning("Invalid bitmap type specified for <image> tag: %d", type);
+        }
+        else
+        {
+            imageType = (wxBitmapType)type;
+        }
+    }
+
+    wxString data;
+
+    wxXmlNode* imageChild = node->GetChildren();
+    while (imageChild)
+    {
+        wxString childName = imageChild->GetName();
+        if (childName == wxT("data"))
+        {
+            wxXmlNode* dataChild = imageChild->GetChildren();
+            while (dataChild)
+            {
+                data = dataChild->GetContent();
+                // wxLogDebug(data);
+                dataChild = dataChild->GetNext();
+            }
+
+        }
+        imageChild = imageChild->GetNext();
+    }
+
+    if (!data.empty())
+    {
+        wxStringInputStream strStream(data);
+
+        GetImageBlock().ReadHex(strStream, data.length(), imageType);
+        
+        return true;
+    }
+    else
+        return false;
+}
+
+#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
+// Export this object directly to the given stream.
+bool wxRichTextImage::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler)
+{
+    wxString style = handler->AddAttributes(GetAttributes(), false);
+
+    ::OutputIndentation(stream, indent);
+    ::OutputString(stream, wxT("<image"), handler->GetConvMem(), handler->GetConvFile());
+    if (!GetImageBlock().Ok())
+    {
+        // No data
+        ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
+    }
+    else
+    {
+        ::OutputString(stream, wxString::Format(wxT(" imagetype=\"%d\""), (int) GetImageBlock().GetImageType()) + style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
+    }
+    if (GetProperties().GetCount() > 0)
+    {
+        handler->WriteProperties(stream, GetProperties(), indent);
+        ::OutputIndentation(stream, indent);
+    }
+
+    ::OutputIndentation(stream, indent+1);
+    ::OutputString(stream, wxT("<data>"), handler->GetConvMem(), handler->GetConvFile());
+
+    // wxStopWatch stopwatch;
+
+    GetImageBlock().WriteHex(stream);
+
+    // wxLogDebug(wxT("Image conversion to hex took %ldms"), stopwatch.Time());
+
+    ::OutputString(stream, wxT("</data>\n"), handler->GetConvMem(), handler->GetConvFile());
+    ::OutputIndentation(stream, indent);
+    ::OutputString(stream, wxT("</image>"), handler->GetConvMem(), handler->GetConvFile());
+    return true;
+}
+#endif
+
+#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
+// Export this object to the given parent node, usually creating at least one child node.
+bool wxRichTextImage::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler)
+{
+    wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("image"));
+    parent->AddChild(elementNode);
+
+    if (GetImageBlock().Ok())
+        elementNode->AddAttribute(wxT("imagetype"), MakeString((int) GetImageBlock().GetImageType()));
+
+    handler->AddAttributes(elementNode, GetAttributes(), false);
+    handler->WriteProperties(elementNode, GetProperties());
+
+    wxXmlNode* dataNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("data"));
+    elementNode->AddChild(dataNode);
+    wxXmlNode* textNode = new wxXmlNode(wxXML_TEXT_NODE, wxT("text"));
+    dataNode->AddChild(textNode);
+
+    wxString strData;
+#if 1        
+    {
+        wxMemoryOutputStream stream;
+        if (GetImageBlock().WriteHex(stream))
+        {
+            if (stream.GetSize() > 0)
+            {
+                int size = stream.GetSize();
+                int size2 = stream.GetOutputStreamBuffer()->GetIntPosition();
+                wxASSERT(size == size2);
+
+                unsigned char* data = new unsigned char[size];
+                stream.CopyTo(data, size);
+                strData = wxString((const char*) data, wxConvUTF8, size);
+                delete[] data;
+            }
+            else
+                strData = wxEmptyString;
+        }
+    
+    }
+#else
+    {
+        wxStringOutputStream strStream(& strData);
+        GetImageBlock().WriteHex(strStream);        
+    }
+#endif
+
+    textNode->SetContent(strData);
+    textNode->SetNoConversion(true); // optimize speed
+
+    return true;
+}
+#endif
+
+
+// Import this object from XML
+bool wxRichTextParagraphLayoutBox::ImportFromXML(wxRichTextBuffer* buffer, wxXmlNode* node, wxRichTextXMLHandler* handler)
+{
+    wxRichTextObject::ImportFromXML(buffer, node, handler);
+
+    wxString partial = node->GetAttribute(wxT("partialparagraph"), wxEmptyString);
+    if (partial == wxT("true"))
+        SetPartialParagraph(true);
+
+    return true;
+}
+
+#if wxRICHTEXT_HAVE_DIRECT_OUTPUT
+// Export this object directly to the given stream.
+bool wxRichTextParagraphLayoutBox::ExportXML(wxOutputStream& stream, int indent, wxRichTextXMLHandler* handler)
+{
+    ::OutputIndentation(stream, indent);
+    ::OutputString(stream, wxT("<paragraphlayout"), handler->GetConvMem(), handler->GetConvFile());
+
+    wxString style = handler->AddAttributes(GetAttributes(), true);
+
+    if (GetPartialParagraph())
+        style << wxT(" partialparagraph=\"true\"");
+
+    ::OutputString(stream, style + wxT(">"), handler->GetConvMem(), handler->GetConvFile());
+
+    if (GetProperties().GetCount() > 0)
+    {
+        handler->WriteProperties(stream, GetProperties(), indent);
+    }
+    
+    size_t i;
+    for (i = 0; i < GetChildCount(); i++)
+    {
+        wxRichTextObject* child = GetChild(i);
+        child->ExportXML(stream, indent+1, handler);
+    }
+
+    ::OutputIndentation(stream, indent);
+    ::OutputString(stream, wxT("</paragraphlayout>"), handler->GetConvMem(), handler->GetConvFile());
+    return true;
+}
+#endif
+
+#if wxRICHTEXT_HAVE_XMLDOCUMENT_OUTPUT
+// Export this object to the given parent node, usually creating at least one child node.
+bool wxRichTextParagraphLayoutBox::ExportXML(wxXmlNode* parent, wxRichTextXMLHandler* handler)
+{
+    wxXmlNode* elementNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("paragraphlayout"));
+    parent->AddChild(elementNode);
+    handler->AddAttributes(elementNode, GetAttributes(), true);
+    handler->WriteProperties(elementNode, GetProperties());
+
+    if (GetPartialParagraph())
+        elementNode->AddAttribute(wxT("partialparagraph"), wxT("true"));
+
+    size_t i;
+    for (i = 0; i < GetChildCount(); i++)
+    {
+        wxRichTextObject* child = GetChild(i);
+        child->ExportXML(elementNode, handler);
+    }
+
+    return true;
+}
+#endif
 
 #endif
     // wxUSE_RICHTEXT && wxUSE_XML