]> git.saurik.com Git - wxWidgets.git/commitdiff
First cut at printing support for wxRichTextCtrl
authorJulian Smart <julian@anthemion.co.uk>
Fri, 27 Oct 2006 08:43:44 +0000 (08:43 +0000)
committerJulian Smart <julian@anthemion.co.uk>
Fri, 27 Oct 2006 08:43:44 +0000 (08:43 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@42494 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/richtext/richtextbuffer.h
include/wx/richtext/richtextprint.h [new file with mode: 0644]
src/richtext/richtextbuffer.cpp
src/richtext/richtextprint.cpp [new file with mode: 0644]

index 6d1a07395f2b09df8dd44112fa5cd2836dab65e2..8ea8b0bbd736acd2756ff7a8961d656f3464d483 100644 (file)
@@ -101,6 +101,7 @@ class WXDLLIMPEXP_RICHTEXT wxTextAttrEx;
 class WXDLLIMPEXP_RICHTEXT wxRichTextListStyleDefinition;
 class WXDLLIMPEXP_RICHTEXT wxRichTextEvent;
 class WXDLLIMPEXP_RICHTEXT wxRichTextRenderer;
+class WXDLLIMPEXP_RICHTEXT wxRichTextBuffer;
 
 /*!
  * Flags determining the available space, passed to Layout
@@ -115,6 +116,15 @@ class WXDLLIMPEXP_RICHTEXT wxRichTextRenderer;
 // the rect passed to Layout.
 #define wxRICHTEXT_LAYOUT_SPECIFIED_RECT 0x10
 
+/*!
+ * Flags to pass to Draw
+ */
+
+// Ignore paragraph cache optimization, e.g. for printing purposes
+// where one line may be drawn higher (on the next page) compared
+// with the previous line
+#define wxRICHTEXT_DRAW_IGNORE_CACHE    0x01
+
 /*!
  * Flags returned from hit-testing
  */
@@ -714,6 +724,9 @@ public:
     void SetDescent(int descent) { m_descent = descent; }
     int GetDescent() const { return m_descent; }
 
+    /// Gets the containing buffer
+    wxRichTextBuffer* GetBuffer() const;
+
 // Operations
 
     /// Clone the object
@@ -727,8 +740,9 @@ public:
     void Reference() { m_refCount ++; }
     void Dereference();
 
-    /// Convert units in tends of a millimetre to device units
-    static int ConvertTenthsMMToPixels(wxDC& dc, int units);
+    /// Convert units in tenths of a millimetre to device units
+    int ConvertTenthsMMToPixels(wxDC& dc, int units);
+    static int ConvertTenthsMMToPixels(int ppi, int units);
 
 protected:
     wxSize                  m_size;
@@ -1861,6 +1875,11 @@ public:
     /// Factor to multiply by character height to get a reasonable bullet size
     static float GetBulletProportion() { return sm_bulletProportion; }
     static void SetBulletProportion(float prop) { sm_bulletProportion = prop; }
+
+    /// Scale factor for calculating dimensions
+    double GetScale() const { return m_scale; }
+    void SetScale(double scale) { m_scale = scale; }
+
 protected:
 
     /// Command processor
@@ -1904,6 +1923,9 @@ protected:
 
     /// Factor to multiply by character height to get a reasonable bullet size
     static float            sm_bulletProportion;
+
+    /// Scaling factor in use: needed to calculate correct dimensions when printing
+    double                  m_scale;
 };
 
 /*!
diff --git a/include/wx/richtext/richtextprint.h b/include/wx/richtext/richtextprint.h
new file mode 100644 (file)
index 0000000..69d1a43
--- /dev/null
@@ -0,0 +1,237 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        wx/richtext/richtextprint.h
+// Purpose:     Rich text printing classes
+// Author:      Julian Smart
+// Created:     2006-10-23
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _WX_RICHTEXTPRINT_H_
+#define _WX_RICHTEXTPRINT_H_
+
+#include "wx/defs.h"
+
+#if wxUSE_RICHTEXT & wxUSE_PRINTING_ARCHITECTURE
+
+#include "wx/richtext/richtextbuffer.h"
+
+#include "wx/print.h"
+#include "wx/printdlg.h"
+
+#define wxRICHTEXT_PRINT_MAX_PAGES 99999
+
+// Header/footer page identifiers
+enum wxRichTextOddEvenPage {
+    wxRICHTEXT_PAGE_ODD,
+    wxRICHTEXT_PAGE_EVEN,
+    wxRICHTEXT_PAGE_ALL,
+};
+
+// Header/footer text locations
+enum wxRichTextPageLocation {
+    wxRICHTEXT_PAGE_LEFT,
+    wxRICHTEXT_PAGE_CENTRE,
+    wxRICHTEXT_PAGE_RIGHT
+};
+
+/*!
+ * Header/footer data
+ */
+
+class WXDLLIMPEXP_RICHTEXT wxRichTextHeaderFooterData: public wxObject
+{
+public:
+    wxRichTextHeaderFooterData() { Init(); }
+    wxRichTextHeaderFooterData(const wxRichTextHeaderFooterData& data) { Copy(data); }
+
+    /// Initialise
+    void Init() { m_headerMargin = 20; m_footerMargin = 20; m_showOnFirstPage = true; }
+
+    /// Copy
+    void Copy(const wxRichTextHeaderFooterData& data);
+
+    /// Assignment
+    void operator= (const wxRichTextHeaderFooterData& data) { Copy(data); }
+
+    /// Set/get header text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT
+    void SetHeaderText(const wxString& text, wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_ALL, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE);
+    wxString GetHeaderText(wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_EVEN, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE) const;
+
+    /// Set/get footer text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT
+    void SetFooterText(const wxString& text, wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_ALL, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE);
+    wxString GetFooterText(wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_EVEN, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE) const;
+
+    /// Set/get text
+    void SetText(const wxString& text, int headerFooter, wxRichTextOddEvenPage page, wxRichTextPageLocation location);
+    wxString GetText(int headerFooter, wxRichTextOddEvenPage page, wxRichTextPageLocation location) const;
+
+    /// Set/get margins between text and header or footer, in tenths of a millimeter
+    void SetMargins(int headerMargin, int footerMargin) { m_headerMargin = headerMargin; m_footerMargin = footerMargin; }
+    int GetHeaderMargin() const { return m_headerMargin; }
+    int GetFooterMargin() const { return m_footerMargin; }
+
+    /// Set/get whether to show header or footer on first page
+    void SetShowOnFirstPage(bool showOnFirstPage) { m_showOnFirstPage = showOnFirstPage; }
+    bool GetShowOnFirstPage() const { return m_showOnFirstPage; }
+
+    /// Clear all text
+    void Clear();
+
+    /// Set/get font
+    void SetFont(const wxFont& font) { m_font = font; }
+    const wxFont& GetFont() const { return m_font; }
+
+    /// Set/get colour
+    void SetTextColour(const wxColour& col) { m_colour = col; }
+    const wxColour& GetTextColour() const { return m_colour; }
+
+    DECLARE_CLASS(wxRichTextHeaderFooterData)
+
+private:
+
+    // Strings for left, centre, right, top, bottom, odd, even
+    wxString    m_text[12];
+    wxFont      m_font;
+    wxColour    m_colour;
+    int         m_headerMargin;
+    int         m_footerMargin;
+    bool        m_showOnFirstPage;
+};
+
+/*!
+ * wxRichTextPrintout
+ */
+
+class WXDLLIMPEXP_RICHTEXT wxRichTextPrintout : public wxPrintout
+{
+public:
+    wxRichTextPrintout(const wxString& title = wxT("Printout"));
+    virtual ~wxRichTextPrintout();
+
+    /// The buffer to print
+    void SetRichTextBuffer(wxRichTextBuffer* buffer) { m_richTextBuffer = buffer; }
+    wxRichTextBuffer* GetRichTextBuffer() const { return m_richTextBuffer; }
+
+    /// Set/get header/footer data
+    void SetHeaderFooterData(const wxRichTextHeaderFooterData& data) { m_headerFooterData = data; }
+    const wxRichTextHeaderFooterData& GetHeaderFooterData() const { return m_headerFooterData; }
+
+    /// Sets margins in 10ths of millimetre. Defaults to 1 inch for margins.
+    void SetMargins(int top = 252, int bottom = 252, int left = 252, int right = 252);
+
+    /// Calculate scaling and rectangles, setting the device context scaling
+    void CalculateScaling(wxDC* dc, wxRect& textRect, wxRect& headerRect, wxRect& footerRect);
+
+    // wxPrintout virtual functions
+    virtual bool OnPrintPage(int page);
+    virtual bool HasPage(int page);
+    virtual void GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo);
+    virtual bool OnBeginDocument(int startPage, int endPage);
+    virtual void OnPreparePrinting();
+
+private:
+
+    /// Renders one page into dc
+    void RenderPage(wxDC *dc, int page);
+
+    /// Substitute keywords
+    static bool SubstituteKeywords(wxString& str, const wxString& title, int pageNum, int pageCount);
+
+private:
+
+    wxRichTextBuffer*           m_richTextBuffer;
+    int                         m_numPages;
+    wxArrayInt                  m_pageBreaksStart;
+    wxArrayInt                  m_pageBreaksEnd;
+    int                         m_marginLeft, m_marginTop, m_marginRight, m_marginBottom;
+
+    wxRichTextHeaderFooterData  m_headerFooterData;
+
+    DECLARE_NO_COPY_CLASS(wxRichTextPrintout)
+};
+
+/*
+ *! wxRichTextPrinting
+ * A simple interface to perform wxRichTextBuffer printing.
+ */
+
+class WXDLLIMPEXP_RICHTEXT wxRichTextPrinting : public wxObject
+{
+public:
+    wxRichTextPrinting(const wxString& name = wxT("Printing"), wxWindow *parentWindow = NULL);
+    virtual ~wxRichTextPrinting();
+
+    /// Preview the file or buffer
+    bool PreviewFile(const wxString& richTextFile);
+    bool PreviewBuffer(const wxRichTextBuffer& buffer);
+
+    /// Print the file or buffer
+    bool PrintFile(const wxString& richTextFile);
+    bool PrintBuffer(const wxRichTextBuffer& buffer);
+
+    /// Shows page setup dialog
+    void PageSetup();
+
+    /// Set/get header/footer data
+    void SetHeaderFooterData(const wxRichTextHeaderFooterData& data) { m_headerFooterData = data; }
+    const wxRichTextHeaderFooterData& GetHeaderFooterData() const { return m_headerFooterData; }
+
+    /// Set/get header text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT
+    void SetHeaderText(const wxString& text, wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_ALL, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE);
+    wxString GetHeaderText(wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_EVEN, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE) const;
+
+    /// Set/get footer text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT
+    void SetFooterText(const wxString& text, wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_ALL, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE);
+    wxString GetFooterText(wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_EVEN, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE) const;
+
+    /// Show header/footer on first page, or not
+    bool SetShowOnFirstPage(bool show) { m_headerFooterData.SetShowOnFirstPage(show); }
+
+    /// Get print and page setup data
+    wxPrintData *GetPrintData();
+    wxPageSetupDialogData *GetPageSetupData() { return m_pageSetupData; }
+
+    /// Set the rich text buffer pointer, deleting the existing object if present
+    void SetRichTextBufferPreview(wxRichTextBuffer* buf);
+    wxRichTextBuffer* GetRichTextBufferPreview() const { return m_richTextBufferPreview; }
+
+    void SetRichTextBufferPrinting(wxRichTextBuffer* buf);
+    wxRichTextBuffer* GetRichTextBufferPrinting() const { return m_richTextBufferPrinting; }
+
+    /// Set/get the parent window
+    void SetParentWindow(wxWindow* parent) { m_parentWindow = parent; }
+    wxWindow* GetParentWindow() const { return m_parentWindow; }
+
+    /// Set/get the title
+    void SetTitle(const wxString& title) { m_title = title; }
+    const wxString& GetTitle() const { return m_title; }
+
+    /// Set/get the preview rect
+    void SetPreviewRect(const wxRect& rect) { m_previewRect = rect; }
+    const wxRect& GetPreviewRect() const { return m_previewRect; }
+
+protected:
+    virtual wxRichTextPrintout *CreatePrintout();
+    virtual bool DoPreview(wxRichTextPrintout *printout1, wxRichTextPrintout *printout2);
+    virtual bool DoPrint(wxRichTextPrintout *printout);
+
+private:
+    wxPrintData*                m_printData;
+    wxPageSetupDialogData*      m_pageSetupData;
+
+    wxRichTextHeaderFooterData  m_headerFooterData;
+    wxString                    m_title;
+    wxWindow*                   m_parentWindow;
+    wxRichTextBuffer*           m_richTextBufferPreview;
+    wxRichTextBuffer*           m_richTextBufferPrinting;
+    wxRect                      m_previewRect;
+
+    DECLARE_NO_COPY_CLASS(wxRichTextPrinting)
+};
+
+#endif  // wxUSE_RICHTEXT & wxUSE_PRINTING_ARCHITECTURE
+
+#endif // _WX_RICHTEXTPRINT_H_
+
index d250a15a0eed6ecd52ad3881a757b5ad9a3ef2ee..2c03c59d5ffdb457e4a9bfbb18cb26b29c742db2 100644 (file)
@@ -103,11 +103,21 @@ void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin
     m_bottomMargin = bottomMargin;
 }
 
-// Convert units in tends of a millimetre to device units
+// Convert units in tenths of a millimetre to device units
 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units)
 {
-    int ppi = dc.GetPPI().x;
+    int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units);
 
+    // Unscale
+    wxRichTextBuffer* buffer = GetBuffer();
+    if (buffer)
+        p = (int) ((double)p / buffer->GetScale());
+    return p;
+}
+
+// Convert units in tenths of a millimetre to device units
+int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units)
+{
     // There are ppi pixels in 254.1 "1/10 mm"
 
     double pixels = ((double) units * (double)ppi) / 254.1;
@@ -123,6 +133,14 @@ void wxRichTextObject::Dump(wxTextOutputStream& stream)
     stream << wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes.GetTextColour().Red(), (int) m_attributes.GetTextColour().Green(), (int) m_attributes.GetTextColour().Blue()) << wxT("\n");
 }
 
+/// Gets the containing buffer
+wxRichTextBuffer* wxRichTextObject::GetBuffer() const
+{
+    const wxRichTextObject* obj = this;
+    while (obj && !obj->IsKindOf(CLASSINFO(wxRichTextBuffer)))
+        obj = obj->GetParent();
+    return wxDynamicCast(obj, wxRichTextBuffer);
+}
 
 /*!
  * wxRichTextCompositeObject
@@ -508,7 +526,7 @@ bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range,
         {
             wxRect childRect(child->GetPosition(), child->GetCachedSize());
 
-            if (childRect.GetTop() > rect.GetBottom() || childRect.GetBottom() < rect.GetTop())
+            if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom() || childRect.GetBottom() < rect.GetTop())
             {
                 // Skip
             }
@@ -528,7 +546,10 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int styl
     bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
 
     // If only laying out a specific area, the passed rect has a different meaning:
-    // the visible part of the buffer.
+    // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
+    // so that during a size, only the visible part will be relaid out, or
+    // it would take too long causing flicker. As an approximation, we assume that
+    // everything up to the start of the visible area is laid out correctly.
     if (formatRect)
     {
         availableSpace = wxRect(0 + m_leftMargin,
@@ -3328,7 +3349,7 @@ void wxRichTextParagraph::ApplyParagraphStyle(const wxTextAttrEx& attr, const wx
         }
         else if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
         {
-            pos.x = rect.GetRight() - size.x;
+            pos.x = pos.x + rect.GetWidth() - size.x;
             line->SetPosition(pos);
         }
 
@@ -4510,6 +4531,7 @@ void wxRichTextBuffer::Init()
     m_batchedCommand = NULL;
     m_suppressUndo = 0;
     m_handlerFlags = 0;
+    m_scale = 1.0;
 }
 
 /// Initialisation
@@ -5629,7 +5651,7 @@ void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
     sm_renderer = renderer;
 }
 
-bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& dc, const wxTextAttrEx& bulletAttr, const wxRect& rect)
+bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxTextAttrEx& bulletAttr, const wxRect& rect)
 {
     if (bulletAttr.GetTextColour().Ok())
     {
@@ -5664,7 +5686,7 @@ bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* WXUNUSED(par
     y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
                 
     // The margin between a bullet and text.
-    int margin = wxRichTextObject::ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
+    int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
                 
     if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
         x = rect.x + rect.width - bulletWidth - margin;
@@ -5702,7 +5724,7 @@ bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* WXUNUSED(par
     return true;
 }
 
-bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& dc, const wxTextAttrEx& attr, const wxRect& rect, const wxString& text)
+bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxTextAttrEx& attr, const wxRect& rect, const wxString& text)
 {
     if (!text.empty())
     {
@@ -5735,7 +5757,7 @@ bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* WXUNUSED(paragra
         int y = rect.y + (rect.height - charHeight);    
 
         // The margin between a bullet and text.
-        int margin = wxRichTextObject::ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
+        int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
                 
         if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
             x = (rect.x + rect.width) - tw - margin;
diff --git a/src/richtext/richtextprint.cpp b/src/richtext/richtextprint.cpp
new file mode 100644 (file)
index 0000000..0032487
--- /dev/null
@@ -0,0 +1,693 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        src/richtext/richtextprint.cpp
+// Purpose:     Rich text printing classes
+// Author:      Julian Smart
+// Created:     2006-10-24
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+// For compilers that support precompilation, includes "wx/wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+    #pragma hdrstop
+#endif
+
+#if wxUSE_RICHTEXT && wxUSE_PRINTING_ARCHITECTURE && wxUSE_STREAMS
+
+#ifndef WX_PRECOMP
+    #include "wx/log.h"
+    #include "wx/intl.h"
+    #include "wx/dc.h"
+    #include "wx/settings.h"
+    #include "wx/msgdlg.h"
+#endif
+
+#include "wx/datetime.h"
+#include "wx/print.h"
+#include "wx/printdlg.h"
+#include "wx/richtext/richtextprint.h"
+#include "wx/wfstream.h"
+
+/*!
+ * wxRichTextPrintout
+ */
+
+wxRichTextPrintout::wxRichTextPrintout(const wxString& title) : wxPrintout(title)
+{
+    m_numPages = wxRICHTEXT_PRINT_MAX_PAGES;
+
+    SetMargins(); // to default values
+}
+
+wxRichTextPrintout::~wxRichTextPrintout()
+{
+}
+
+void wxRichTextPrintout::OnPreparePrinting()
+{
+    wxBusyCursor wait;
+
+    m_numPages = 0;
+
+    m_pageBreaksStart.Clear();
+    m_pageBreaksEnd.Clear();
+    
+    int lastStartPos = 0;
+    
+    wxRect rect, headerRect, footerRect;
+
+    /// Sets the DC scaling and returns important page rectangles
+    CalculateScaling(GetDC(), rect, headerRect, footerRect);
+
+    if (GetRichTextBuffer())
+    {
+        GetRichTextBuffer()->Invalidate(wxRICHTEXT_ALL);
+
+        GetRichTextBuffer()->Layout(*GetDC(), rect, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT);
+    
+        // Now calculate the page breaks
+        
+        int yOffset = 0;
+        
+        wxRichTextLine* lastLine = NULL;
+        
+        wxRichTextObjectList::compatibility_iterator node = GetRichTextBuffer()->GetChildren().GetFirst();
+        while (node)
+        {
+            // child is a paragraph
+            wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+            wxASSERT (child != NULL);
+
+            wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
+            while (node2)
+            {
+                wxRichTextLine* line = node2->GetData();
+                
+                // Set the line to the page-adjusted position
+                line->SetPosition(wxPoint(line->GetPosition().x, line->GetPosition().y - yOffset));
+
+                int lineY = child->GetPosition().y + line->GetPosition().y;
+                
+                if ((lineY + line->GetSize().y) > rect.GetBottom())
+                {
+                    // New page starting at this line
+                    int newY = rect.y;
+                    
+                    // We increase the offset by the difference between new and old positions
+                    
+                    int increaseOffsetBy = lineY - newY;
+                    yOffset += increaseOffsetBy;
+                    
+                    line->SetPosition(wxPoint(line->GetPosition().x, newY - child->GetPosition().y));
+
+                    if (!lastLine)
+                        lastLine = line;
+                
+                    m_pageBreaksStart.Add(lastStartPos);
+                    m_pageBreaksEnd.Add(lastLine->GetAbsoluteRange().GetEnd());
+                    
+                    lastStartPos = line->GetAbsoluteRange().GetStart();
+                    
+                    m_numPages ++;
+                }
+                
+                lastLine = line;
+
+                node2 = node2->GetNext();
+            }
+
+            node = node->GetNext();
+        }
+
+        // Closing page break
+        if (m_pageBreaksStart.GetCount() > 0 && (m_pageBreaksEnd[m_pageBreaksEnd.GetCount()-1] < (GetRichTextBuffer()->GetRange().GetEnd()-1)))
+        {
+            m_pageBreaksStart.Add(lastStartPos);
+            m_pageBreaksEnd.Add(GetRichTextBuffer()->GetRange().GetEnd());
+            m_numPages ++;
+        }
+    }
+}
+
+bool wxRichTextPrintout::OnBeginDocument(int startPage, int endPage)
+{
+    if (!wxPrintout::OnBeginDocument(startPage, endPage)) return false;
+
+    return true;
+}
+
+bool wxRichTextPrintout::OnPrintPage(int page)
+{
+    wxDC *dc = GetDC();
+    if (dc)
+    {
+        if (HasPage(page))
+            RenderPage(dc, page);
+        return true;
+    }
+    else return false;
+}
+
+void wxRichTextPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
+{
+    *minPage = 1;
+    *maxPage = m_numPages;
+    *selPageFrom = 1;
+    *selPageTo = m_numPages;
+}
+
+bool wxRichTextPrintout::HasPage(int pageNum)
+{
+    return pageNum > 0 && pageNum <= m_numPages;
+}
+
+void wxRichTextPrintout::RenderPage(wxDC *dc, int page)
+{
+    if (!GetRichTextBuffer())
+        return;
+
+    wxBusyCursor wait;
+    
+    wxRect textRect, headerRect, footerRect;
+
+    /// Sets the DC scaling and returns important page rectangles
+    CalculateScaling(dc, textRect, headerRect, footerRect);
+
+    if (page > 1 || m_headerFooterData.GetShowOnFirstPage())
+    {
+        if (m_headerFooterData.GetFont().Ok())
+            dc->SetFont(m_headerFooterData.GetFont());
+        else
+            dc->SetFont(*wxNORMAL_FONT);
+        if (m_headerFooterData.GetTextColour().Ok())
+            dc->SetTextForeground(m_headerFooterData.GetTextColour());
+        else
+            dc->SetTextForeground(*wxBLACK);
+        dc->SetBackgroundMode(wxTRANSPARENT);
+
+        // Draw header, if any    
+        wxRichTextOddEvenPage oddEven = ((page % 2) == 1) ? wxRICHTEXT_PAGE_ODD : wxRICHTEXT_PAGE_EVEN;
+
+        wxString headerTextCentre = m_headerFooterData.GetHeaderText(oddEven, wxRICHTEXT_PAGE_CENTRE);
+        wxString headerTextLeft = m_headerFooterData.GetHeaderText(oddEven, wxRICHTEXT_PAGE_LEFT);
+        wxString headerTextRight = m_headerFooterData.GetHeaderText(oddEven, wxRICHTEXT_PAGE_RIGHT);
+
+        if (!headerTextLeft.IsEmpty())
+        {
+            SubstituteKeywords(headerTextLeft, GetTitle(), page, m_numPages);
+
+            //int tx, ty;
+            //dc->GetTextExtent(headerTextLeft, & tx, & ty);
+            
+            int x = headerRect.GetLeft();
+            int y = headerRect.GetX();
+            dc->DrawText(headerTextLeft, x, y);
+        }
+        if (!headerTextCentre.IsEmpty())
+        {
+            SubstituteKeywords(headerTextCentre, GetTitle(), page, m_numPages);
+
+            int tx, ty;
+            dc->GetTextExtent(headerTextCentre, & tx, & ty);
+
+            int x = headerRect.GetWidth()/2 - tx/2 + headerRect.GetLeft();
+            int y = headerRect.GetY();
+            dc->DrawText(headerTextCentre, x, y);
+        }
+        if (!headerTextRight.IsEmpty())
+        {
+            SubstituteKeywords(headerTextRight, GetTitle(), page, m_numPages);
+
+            int tx, ty;
+            dc->GetTextExtent(headerTextRight, & tx, & ty);
+
+            int x = headerRect.GetRight() - tx;
+            int y = headerRect.GetY();
+            dc->DrawText(headerTextRight, x, y);
+        }
+
+        // Draw footer, if any    
+        wxString footerTextCentre = m_headerFooterData.GetFooterText(oddEven, wxRICHTEXT_PAGE_CENTRE);
+        wxString footerTextLeft = m_headerFooterData.GetFooterText(oddEven, wxRICHTEXT_PAGE_LEFT);
+        wxString footerTextRight = m_headerFooterData.GetFooterText(oddEven, wxRICHTEXT_PAGE_RIGHT);
+
+        if (!footerTextLeft.IsEmpty())
+        {
+            SubstituteKeywords(footerTextLeft, GetTitle(), page, m_numPages);
+
+            int tx, ty;
+            dc->GetTextExtent(footerTextLeft, & tx, & ty);
+
+            int x = footerRect.GetLeft();
+            int y = footerRect.GetBottom() - ty;
+            dc->DrawText(footerTextLeft, x, y);
+        }
+        if (!footerTextCentre.IsEmpty())
+        {
+            SubstituteKeywords(footerTextCentre, GetTitle(), page, m_numPages);
+
+            int tx, ty;
+            dc->GetTextExtent(footerTextCentre, & tx, & ty);
+
+            int x = footerRect.GetWidth()/2 - tx/2 + footerRect.GetLeft();
+            int y = footerRect.GetBottom() - ty;
+            dc->DrawText(footerTextCentre, x, y);
+        }
+        if (!footerTextRight.IsEmpty())
+        {
+            SubstituteKeywords(footerTextRight, GetTitle(), page, m_numPages);
+
+            int tx, ty;
+            dc->GetTextExtent(footerTextRight, & tx, & ty);
+
+            int x = footerRect.GetRight() - tx;
+            int y = footerRect.GetBottom() - ty;
+            dc->DrawText(footerTextRight, x, y);
+        }
+    }    
+
+    wxRichTextRange rangeToDraw(m_pageBreaksStart[page-1], m_pageBreaksEnd[page-1]);
+
+    GetRichTextBuffer()->Draw(*dc, rangeToDraw, wxRichTextRange(-1,-1), textRect, 0 /* descent */, wxRICHTEXT_DRAW_IGNORE_CACHE /* flags */);
+}
+
+void wxRichTextPrintout::SetMargins(int top, int bottom, int left, int right)
+{
+    m_marginTop = top;
+    m_marginBottom = bottom;
+    m_marginLeft = left;
+    m_marginRight = right;
+}
+
+/// Calculate scaling and rectangles, setting the device context scaling
+void wxRichTextPrintout::CalculateScaling(wxDC* dc, wxRect& textRect, wxRect& headerRect, wxRect& footerRect)
+{
+    // Get the logical pixels per inch of screen and printer
+    int ppiScreenX, ppiScreenY;
+    GetPPIScreen(&ppiScreenX, &ppiScreenY);
+    int ppiPrinterX, ppiPrinterY;
+    GetPPIPrinter(&ppiPrinterX, &ppiPrinterY);
+
+    // This scales the DC so that the printout roughly represents the
+    // the screen scaling.
+    float scale = (float)((float)ppiPrinterX/(float)ppiScreenX);
+
+    // Now we have to check in case our real page size is reduced
+    // (e.g. because we're drawing to a print preview memory DC)
+    int pageWidth, pageHeight;
+    int w, h;
+    dc->GetSize(&w, &h);
+    GetPageSizePixels(&pageWidth, &pageHeight);
+    
+    // If printer pageWidth == current DC width, then this doesn't
+    // change. But w might be the preview bitmap width, so scale down.
+    float previewScale = (float)(w/(float)pageWidth);
+    float overallScale = scale * previewScale;
+
+    // The dimensions used for indentation etc. have to be unscaled
+    // during printing to be correct when scaling is applied.
+    if (!IsPreview())
+        m_richTextBuffer->SetScale(scale);
+    
+    // Calculate margins
+    int marginLeft = wxRichTextObject::ConvertTenthsMMToPixels(ppiPrinterX, m_marginLeft);
+    int marginTop = wxRichTextObject::ConvertTenthsMMToPixels(ppiPrinterX, m_marginTop);
+    int marginRight = wxRichTextObject::ConvertTenthsMMToPixels(ppiPrinterX, m_marginRight);
+    int marginBottom = wxRichTextObject::ConvertTenthsMMToPixels(ppiPrinterX, m_marginBottom);
+    
+    // Header and footer margins
+    int headerMargin = wxRichTextObject::ConvertTenthsMMToPixels(ppiPrinterX, m_headerFooterData.GetHeaderMargin());
+    int footerMargin = wxRichTextObject::ConvertTenthsMMToPixels(ppiPrinterX, m_headerFooterData.GetFooterMargin());
+    
+    dc->SetUserScale(overallScale, overallScale);
+
+    wxRect rect(marginLeft/scale, marginTop/scale,
+                (pageWidth - marginLeft - marginRight)/scale, (pageHeight - marginTop - marginBottom)/scale);
+                
+    headerRect = wxRect(0, 0, 0, 0);
+
+    if (!m_headerFooterData.GetHeaderText(wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT).IsEmpty() ||
+        !m_headerFooterData.GetHeaderText(wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_CENTRE).IsEmpty() ||
+        !m_headerFooterData.GetHeaderText(wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_RIGHT).IsEmpty() ||
+
+        !m_headerFooterData.GetHeaderText(wxRICHTEXT_PAGE_EVEN, wxRICHTEXT_PAGE_LEFT).IsEmpty() ||
+        !m_headerFooterData.GetHeaderText(wxRICHTEXT_PAGE_EVEN, wxRICHTEXT_PAGE_CENTRE).IsEmpty() ||
+        !m_headerFooterData.GetHeaderText(wxRICHTEXT_PAGE_EVEN, wxRICHTEXT_PAGE_RIGHT).IsEmpty())
+    {
+        if (m_headerFooterData.GetFont().Ok())
+            dc->SetFont(m_headerFooterData.GetFont());
+        else
+            dc->SetFont(*wxNORMAL_FONT);
+        
+        int charHeight = dc->GetCharHeight();
+        
+        int headerHeight = charHeight + headerMargin/scale;
+
+        headerRect = wxRect(rect.x, rect.y, rect.width, headerHeight);
+
+        rect.y += headerHeight;
+        rect.height -= headerHeight;        
+    }
+
+    footerRect = wxRect(0, 0, 0, 0);
+
+    if (!m_headerFooterData.GetFooterText(wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT).IsEmpty() ||
+        !m_headerFooterData.GetFooterText(wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_CENTRE).IsEmpty() ||
+        !m_headerFooterData.GetFooterText(wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_RIGHT).IsEmpty() ||
+
+        !m_headerFooterData.GetFooterText(wxRICHTEXT_PAGE_EVEN, wxRICHTEXT_PAGE_LEFT).IsEmpty() ||
+        !m_headerFooterData.GetFooterText(wxRICHTEXT_PAGE_EVEN, wxRICHTEXT_PAGE_CENTRE).IsEmpty() ||
+        !m_headerFooterData.GetFooterText(wxRICHTEXT_PAGE_EVEN, wxRICHTEXT_PAGE_RIGHT).IsEmpty())
+    {
+        if (m_headerFooterData.GetFont().Ok())
+            dc->SetFont(m_headerFooterData.GetFont());
+        else
+            dc->SetFont(*wxNORMAL_FONT);
+        
+        int charHeight = dc->GetCharHeight();
+        
+        int footerHeight = charHeight + footerMargin/scale;
+
+        footerRect = wxRect(rect.x, rect.y + rect.height, rect.width, footerHeight);
+
+        rect.height -= footerHeight;        
+    }
+
+    textRect = rect;
+}
+
+bool wxRichTextPrintout::SubstituteKeywords(wxString& str, const wxString& title, int pageNum, int pageCount)
+{
+    wxString num;
+
+    num.Printf(wxT("%i"), pageNum);
+    str.Replace(wxT("@PAGENUM@"), num);
+
+    num.Printf(wxT("%lu"), (unsigned long) pageCount);
+    str.Replace(wxT("@PAGESCNT@"), num);
+
+    wxDateTime now = wxDateTime::Now();
+
+    str.Replace(wxT("@DATE@"), now.FormatDate());
+    str.Replace(wxT("@TIME@"), now.FormatTime());
+
+    str.Replace(wxT("@TITLE@"), title);
+
+    return true;
+}
+
+/*!
+ * wxRichTextPrinting
+ */
+
+wxRichTextPrinting::wxRichTextPrinting(const wxString& name, wxWindow *parentWindow)
+{
+    m_richTextBufferPrinting = NULL;
+    m_richTextBufferPreview = NULL;
+
+    m_parentWindow = parentWindow;
+    m_title = name;
+    m_printData = NULL;
+    
+    m_previewRect = wxRect(wxPoint(100, 100), wxSize(800, 800));
+
+    m_pageSetupData = new wxPageSetupDialogData;
+    m_pageSetupData->EnableMargins(true);
+    m_pageSetupData->SetMarginTopLeft(wxPoint(25, 25));
+    m_pageSetupData->SetMarginBottomRight(wxPoint(25, 25));
+}
+
+wxRichTextPrinting::~wxRichTextPrinting()
+{
+    delete m_printData;
+    delete m_pageSetupData;
+    delete m_richTextBufferPrinting;
+    delete m_richTextBufferPreview;
+}
+
+wxPrintData *wxRichTextPrinting::GetPrintData()
+{
+    if (m_printData == NULL)
+        m_printData = new wxPrintData();
+    return m_printData;
+}
+
+/// Set the rich text buffer pointer, deleting the existing object if present
+void wxRichTextPrinting::SetRichTextBufferPrinting(wxRichTextBuffer* buf)
+{
+    if (m_richTextBufferPrinting)
+    {
+        delete m_richTextBufferPrinting;
+        m_richTextBufferPrinting = NULL;
+    }
+    m_richTextBufferPrinting = buf;
+}
+
+void wxRichTextPrinting::SetRichTextBufferPreview(wxRichTextBuffer* buf)
+{
+    if (m_richTextBufferPreview)
+    {
+        delete m_richTextBufferPreview;
+        m_richTextBufferPreview = NULL;
+    }
+    m_richTextBufferPreview = buf;
+}
+
+bool wxRichTextPrinting::PreviewFile(const wxString& richTextFile)
+{
+    SetRichTextBufferPreview(new wxRichTextBuffer);
+
+    if (!m_richTextBufferPreview->LoadFile(richTextFile))
+    {
+        SetRichTextBufferPreview(NULL);
+        return false;
+    }
+    else
+        SetRichTextBufferPrinting(new wxRichTextBuffer(*m_richTextBufferPreview));
+    
+    wxRichTextPrintout *p1 = CreatePrintout();
+    p1->SetRichTextBuffer(m_richTextBufferPreview);
+
+    wxRichTextPrintout *p2 = CreatePrintout();
+    p2->SetRichTextBuffer(m_richTextBufferPrinting);
+    return DoPreview(p1, p2);
+}
+
+bool wxRichTextPrinting::PreviewBuffer(const wxRichTextBuffer& buffer)
+{
+    SetRichTextBufferPreview(new wxRichTextBuffer(buffer));
+    SetRichTextBufferPrinting(new wxRichTextBuffer(buffer));
+
+    wxRichTextPrintout *p1 = CreatePrintout();
+    p1->SetRichTextBuffer(m_richTextBufferPreview);
+    
+    wxRichTextPrintout *p2 = CreatePrintout();
+    p2->SetRichTextBuffer(m_richTextBufferPrinting);
+
+    return DoPreview(p1, p2);
+}
+
+bool wxRichTextPrinting::PrintFile(const wxString& richTextFile)
+{
+    SetRichTextBufferPrinting(new wxRichTextBuffer);
+
+    if (!m_richTextBufferPrinting->LoadFile(richTextFile))
+    {
+        SetRichTextBufferPrinting(NULL);
+        return false;
+    }
+
+    wxRichTextPrintout *p = CreatePrintout();
+    p->SetRichTextBuffer(m_richTextBufferPrinting);
+
+    bool ret = DoPrint(p);
+    delete p;
+    return ret;
+}
+
+bool wxRichTextPrinting::PrintBuffer(const wxRichTextBuffer& buffer)
+{
+    SetRichTextBufferPrinting(new wxRichTextBuffer(buffer));
+
+    wxRichTextPrintout *p = CreatePrintout();
+    p->SetRichTextBuffer(m_richTextBufferPrinting);
+
+    bool ret = DoPrint(p);
+    delete p;
+    return ret;
+}
+
+bool wxRichTextPrinting::DoPreview(wxRichTextPrintout *printout1, wxRichTextPrintout *printout2)
+{
+    // Pass two printout objects: for preview, and possible printing.
+    wxPrintDialogData printDialogData(*GetPrintData());
+    wxPrintPreview *preview = new wxPrintPreview(printout1, printout2, &printDialogData);
+    if (!preview->Ok())
+    {
+        delete preview;
+        return false;
+    }
+
+    wxPreviewFrame *frame = new wxPreviewFrame(preview, m_parentWindow,
+                                               m_title + _(" Preview"),
+                                               m_previewRect.GetPosition(), m_previewRect.GetSize());
+    frame->Centre(wxBOTH);
+    frame->Initialize();
+    frame->Show(true);
+    return true;
+}
+
+bool wxRichTextPrinting::DoPrint(wxRichTextPrintout *printout)
+{
+    wxPrintDialogData printDialogData(*GetPrintData());
+    wxPrinter printer(&printDialogData);
+
+    if (!printer.Print(m_parentWindow, printout, true))
+    {
+        return false;
+    }
+
+    (*GetPrintData()) = printer.GetPrintDialogData().GetPrintData();
+    return true;
+}
+
+void wxRichTextPrinting::PageSetup()
+{
+    if (!GetPrintData()->Ok())
+    {
+        wxLogError(_("There was a problem during page setup: you may need to set a default printer."));
+        return;
+    }
+
+    m_pageSetupData->SetPrintData(*GetPrintData());
+    wxPageSetupDialog pageSetupDialog(m_parentWindow, m_pageSetupData);
+
+    if (pageSetupDialog.ShowModal() == wxID_OK)
+    {
+        (*GetPrintData()) = pageSetupDialog.GetPageSetupData().GetPrintData();
+        (*m_pageSetupData) = pageSetupDialog.GetPageSetupData();
+    }
+}
+
+wxRichTextPrintout *wxRichTextPrinting::CreatePrintout()
+{
+    wxRichTextPrintout *p = new wxRichTextPrintout(m_title);
+
+    p->SetHeaderFooterData(GetHeaderFooterData());
+    p->SetMargins(10*m_pageSetupData->GetMarginTopLeft().y,
+                    10*m_pageSetupData->GetMarginBottomRight().y,
+                    10*m_pageSetupData->GetMarginTopLeft().x,
+                    10*m_pageSetupData->GetMarginBottomRight().x);
+
+    return p;
+}
+
+/// Set/get header text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT
+void wxRichTextPrinting::SetHeaderText(const wxString& text, wxRichTextOddEvenPage page, wxRichTextPageLocation location)
+{
+    m_headerFooterData.SetHeaderText(text, page, location);
+}
+
+wxString wxRichTextPrinting::GetHeaderText(wxRichTextOddEvenPage page, wxRichTextPageLocation location) const
+{
+    return m_headerFooterData.GetHeaderText(page, location);
+}
+
+/// Set/get footer text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT
+void wxRichTextPrinting::SetFooterText(const wxString& text, wxRichTextOddEvenPage page, wxRichTextPageLocation location)
+{
+    m_headerFooterData.SetFooterText(text, page, location);
+}
+
+wxString wxRichTextPrinting::GetFooterText(wxRichTextOddEvenPage page, wxRichTextPageLocation location) const
+{
+    return m_headerFooterData.GetFooterText(page, location);
+}
+
+/*!
+ * Header/footer data
+ */
+
+IMPLEMENT_CLASS(wxRichTextHeaderFooterData, wxObject)
+
+/// Copy
+void wxRichTextHeaderFooterData::Copy(const wxRichTextHeaderFooterData& data)
+{
+    int i;
+    for (i = 0; i < 12; i++)
+        m_text[i] = data.m_text[i];
+    m_font = data.m_font;
+    m_colour = data.m_colour;
+    m_headerMargin = data.m_headerMargin;
+    m_footerMargin = data.m_footerMargin;
+    m_showOnFirstPage = data.m_showOnFirstPage;
+}
+
+/// Set/get text
+void wxRichTextHeaderFooterData::SetText(const wxString& text, int headerFooter, wxRichTextOddEvenPage page, wxRichTextPageLocation location)
+{
+    int idx = headerFooter + (2 * (int) page) + (4 * (int) location);
+    wxASSERT( idx >= 0 && idx < 12 );
+
+    if (idx >= 0 && idx < 12)
+        m_text[idx] = text;
+}
+
+wxString wxRichTextHeaderFooterData::GetText(int headerFooter, wxRichTextOddEvenPage page, wxRichTextPageLocation location) const
+{
+    int idx = headerFooter + (2 * (int) page) + (4 * (int) location);
+    wxASSERT( idx >= 0 && idx < 12 );
+
+    if (idx >= 0 && idx < 12)
+        return m_text[idx];
+    else
+        return wxEmptyString;
+}
+
+/// Set/get header text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT
+void wxRichTextHeaderFooterData::SetHeaderText(const wxString& text, wxRichTextOddEvenPage page, wxRichTextPageLocation location)
+{
+    if (page == wxRICHTEXT_PAGE_ALL)
+    {
+        SetText(text, 0, wxRICHTEXT_PAGE_ODD, location);
+        SetText(text, 0, wxRICHTEXT_PAGE_EVEN, location);
+    }
+    else
+        SetText(text, 0, page, location);
+}
+
+wxString wxRichTextHeaderFooterData::GetHeaderText(wxRichTextOddEvenPage page, wxRichTextPageLocation location) const
+{
+    return GetText(0, page, location);
+}
+
+/// Set/get footer text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT
+void wxRichTextHeaderFooterData::SetFooterText(const wxString& text, wxRichTextOddEvenPage page, wxRichTextPageLocation location)
+{
+    if (page == wxRICHTEXT_PAGE_ALL)
+    {
+        SetText(text, 1, wxRICHTEXT_PAGE_ODD, location);
+        SetText(text, 1, wxRICHTEXT_PAGE_EVEN, location);
+    }
+    else
+        SetText(text, 1, page, location);
+}
+
+wxString wxRichTextHeaderFooterData::GetFooterText(wxRichTextOddEvenPage page, wxRichTextPageLocation location) const
+{
+    return GetText(1, page, location);
+}
+
+/// Clear all text
+void wxRichTextHeaderFooterData::Clear()
+{
+    int i;
+    for (i = 0; i < 12; i++)
+        m_text[i] = wxEmptyString;
+}
+
+#endif // wxUSE_RICHTEXT & wxUSE_PRINTING_ARCHITECTURE
+