diff --git a/docs/changes.txt b/docs/changes.txt index c1a84c22a5..5bddae6fc2 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -253,6 +253,8 @@ wxHTML: - added SetFonts methods to wxHtmlDCRenderer and wxHtmlPrintout classes - 'sizes' argument to SetFonts can now be NULL to use default font sizes (both by Adrian Philip Look) +- it is now possible to force page break when printing by inserting +
into the markup (Greg Chicares) wxXRC: diff --git a/include/wx/html/htmlcell.h b/include/wx/html/htmlcell.h index eb2b4bee51..fb8d3e5ea6 100644 --- a/include/wx/html/htmlcell.h +++ b/include/wx/html/htmlcell.h @@ -29,6 +29,15 @@ class WXDLLEXPORT wxHtmlLinkInfo; class WXDLLEXPORT wxHtmlCell; class WXDLLEXPORT wxHtmlContainerCell; +// Temporary kludge for backporting html pagebreaks to 2.4.0; +// Do not use, this will NOT be part of wx 2.5! +struct WXDLLEXPORT wxHtmlKludge +{ + int pbreak; + int *known_pagebreaks; + int number_of_pages; +}; + //-------------------------------------------------------------------------------- // wxHtmlCell // Internal data structure. It represents fragments of parsed HTML @@ -109,6 +118,8 @@ public: // Returned value : true if pagebreak was modified, false otherwise // Usage : while (container->AdjustPagebreak(&p)) {} virtual bool AdjustPagebreak(int *pagebreak) const; +// wx 2.5 will use this signature: +// virtual bool AdjustPagebreak(int *pagebreak, int *known_pagebreaks = NULL, int number_of_pages = 0) const; // Sets cell's behaviour on pagebreaks (see AdjustPagebreak). Default // is true - the cell can be split on two pages @@ -190,6 +201,8 @@ public: virtual void Draw(wxDC& dc, int x, int y, int view_y1, int view_y2); virtual void DrawInvisible(wxDC& dc, int x, int y); virtual bool AdjustPagebreak(int *pagebreak) const; +// wx 2.5 will use this signature: +// virtual bool AdjustPagebreak(int *pagebreak, int *known_pagebreaks = NULL, int number_of_pages = 0) const; // insert cell at the end of m_Cells list void InsertCell(wxHtmlCell *cell); diff --git a/include/wx/html/htmprint.h b/include/wx/html/htmprint.h index 843fe299c9..815e8c4f9d 100644 --- a/include/wx/html/htmprint.h +++ b/include/wx/html/htmprint.h @@ -25,6 +25,8 @@ #include "wx/print.h" #include "wx/printdlg.h" +#include // INT_MAX + //-------------------------------------------------------------------------------- // wxHtmlDCRenderer // This class is capable of rendering HTML into specified @@ -39,7 +41,7 @@ public: // Following 3 methods *must* be called before any call to Render: - // Asign DC to this render + // Assign DC to this render void SetDC(wxDC *dc, double pixel_scale = 1.0); // Sets size of output rectangle, in pixels. Note that you *can't* change @@ -57,15 +59,32 @@ public: void SetFonts(wxString normal_face, wxString fixed_face, const int *sizes = NULL); // [x,y] is position of upper-left corner of printing rectangle (see SetSize) - // from is y-coordinate of the very first visible cell + // from is y-coordinate of the very first visible cell + // to is y-coordinate of the next following page break, if any // Returned value is y coordinate of first cell than didn't fit onto page. // Use this value as 'from' in next call to Render in order to print multiple pages // document // If dont_render is TRUE then nothing is rendered into DC and it only counts // pixels and return y coord of the next page // - // CAUTION! Render() changes DC's user scale and does NOT restore it! + // known_pagebreaks and number_of_pages are used only when counting pages; + // otherwise, their default values should be used. Their purpose is to + // support pagebreaks using a subset of CSS2's
. The
handler + // needs to know what pagebreaks have already been set so that it doesn't + // set the same pagebreak twice. + // + // CAUTION! Render() changes DC's user scale and does NOT restore it! int Render(int x, int y, int from = 0, int dont_render = FALSE); +// wx 2.5 will use this signature instead of the preceding: +// int Render(int x, int y, int from = 0, int dont_render = FALSE, int to = INT_MAX, +// int *known_pagebreaks = NULL, int number_of_pages = 0); +// but for this backport we have to get rid of the default arguments +// so that the following signature is distinct from the original. This is +// OK because the implementation never uses those defaults, and user code +// wouldn't know about the extra arguments anyway. The default argument +// on the original signature above still works. + int Render(int x, int y, int from /* = 0 */, int dont_render /* = FALSE */, int to /* = INT_MAX */, + int *known_pagebreaks /* = NULL */, int number_of_pages /* = 0 */); // returns total height of the html document // (compare Render's return value with this) @@ -214,7 +233,7 @@ public: // return page setting data objects. // (You can set their parameters.) -protected: +protected: virtual wxHtmlPrintout *CreatePrintout(); virtual bool DoPreview(wxHtmlPrintout *printout1, wxHtmlPrintout *printout2); virtual bool DoPrint(wxHtmlPrintout *printout); diff --git a/samples/html/printing/test.htm b/samples/html/printing/test.htm index 8cf983737a..3bb5be3a20 100644 --- a/samples/html/printing/test.htm +++ b/samples/html/printing/test.htm @@ -40,6 +40,7 @@ Note (2): the releases described are for wxGTK, wxMSW and wxMotif ports. wxMac c its own development path. Also, minor snapshot releases for specific platforms may be available at dates convenient to the developers.

+


Schedule

@@ -120,7 +121,7 @@ combined base/GUI library for GUI applications only.

-


To-Do List
+
To-Do List

diff --git a/src/html/htmlcell.cpp b/src/html/htmlcell.cpp index e7d104e8c0..ab59343ca7 100644 --- a/src/html/htmlcell.cpp +++ b/src/html/htmlcell.cpp @@ -68,6 +68,14 @@ void wxHtmlCell::OnMouseClick(wxWindow *parent, int x, int y, +// wx 2.5 will use this signature: +// bool wxHtmlCell::AdjustPagebreak(int *pagebreak, int* WXUNUSED(known_pagebreaks), int WXUNUSED(number_of_pages)) const +// +// Workaround to backport html pagebreaks to 2.4.0: +// Actually, we're passing a pointer to struct wxHtmlKludge, casting +// that pointer to an int* . We don't need to do anything special +// here because that struct's first element is an int* to 'pagebreak'. +// Other struct members can be ignored because they'd be unused anyway. bool wxHtmlCell::AdjustPagebreak(int *pagebreak) const { if ((!m_CanLiveOnPagebreak) && @@ -215,9 +223,19 @@ int wxHtmlContainerCell::GetIndentUnits(int ind) const +// wx 2.5 will use this signature: +// bool wxHtmlContainerCell::AdjustPagebreak(int *pagebreak, int* known_pagebreaks, int number_of_pages) const +// +// Workaround to backport html pagebreaks to 2.4.0: +// Actually, we're passing a pointer to struct wxHtmlKludge, casting +// that pointer to an int* . We don't need to do anything special +// here because that struct's first element is an int* to 'pagebreak'. +// Other struct members aren't used here and can be ignored. bool wxHtmlContainerCell::AdjustPagebreak(int *pagebreak) const { if (!m_CanLiveOnPagebreak) +// wx 2.5 will use this call: +// return wxHtmlCell::AdjustPagebreak(pagebreak, known_pagebreaks, number_of_pages); return wxHtmlCell::AdjustPagebreak(pagebreak); else @@ -226,14 +244,24 @@ bool wxHtmlContainerCell::AdjustPagebreak(int *pagebreak) const bool rt = FALSE; int pbrk = *pagebreak - m_PosY; + // Temporary kludge for backporting html pagebreaks to 2.4.0; + // remove in 2.4.1 . + wxHtmlKludge kludge = *(wxHtmlKludge*)pagebreak; + kludge.pbreak = pbrk; + while (c) { - if (c->AdjustPagebreak(&pbrk)) +// wx 2.5 will use this call: +// if (c->AdjustPagebreak(&pbrk, known_pagebreaks, number_of_pages)) + if (c->AdjustPagebreak((int*)&kludge)) rt = TRUE; c = c->GetNext(); } if (rt) + { + pbrk = kludge.pbreak; *pagebreak = pbrk + m_PosY; + } return rt; } } diff --git a/src/html/htmprint.cpp b/src/html/htmprint.cpp index 54d66d3a8b..ef08a8f8c5 100644 --- a/src/html/htmprint.cpp +++ b/src/html/htmprint.cpp @@ -99,17 +99,42 @@ void wxHtmlDCRenderer::SetFonts(wxString normal_face, wxString fixed_face, m_Parser->SetFonts(normal_face, fixed_face, sizes); if (m_DC == NULL && m_Cells != NULL) m_Cells->Layout(m_Width); } - - +// Backport note: this signature will be replaced in wx 2.5. +// It just forwards to the new function for backward binary compatibility. int wxHtmlDCRenderer::Render(int x, int y, int from, int dont_render) +{ + return Render(x, y, from, dont_render, INT_MAX, NULL, 0); +} + +int wxHtmlDCRenderer::Render(int x, int y, int from, int dont_render, int to, int *known_pagebreaks, int number_of_pages) { int pbreak, hght; if (m_Cells == NULL || m_DC == NULL) return 0; pbreak = (int)(from + m_Height); - while (m_Cells->AdjustPagebreak(&pbreak)) {} + + // Temporary kludge for backporting html pagebreaks to 2.4.0; + // remove in 2.4.1 . + wxHtmlKludge kludge; + kludge.pbreak = pbreak; + kludge.known_pagebreaks = known_pagebreaks; + kludge.number_of_pages = number_of_pages; + + while (m_Cells->AdjustPagebreak((int*)&kludge)) {} +// wx 2.5 will use this: +// while (m_Cells->AdjustPagebreak(&pbreak, known_pagebreaks, number_of_pages)) {} + + pbreak = kludge.pbreak; + // We don't need to copy back + // kludge.number_of_pages or + // kludge.known_pagebreaks + // because their values aren't changed by AdjustPagebreak(). + // Thus, known_pagebreaks probably ought to be const. + hght = pbreak - from; + if(to < hght) + hght = to; if (!dont_render) { @@ -336,7 +361,7 @@ void wxHtmlPrintout::CountPages() { pos = m_Renderer->Render((int)( ppmm_h * m_MarginLeft), (int) (ppmm_v * (m_MarginTop + (m_HeaderHeight == 0 ? 0 : m_MarginSpace)) + m_HeaderHeight), - pos, TRUE); + pos, TRUE, INT_MAX, m_PageBreaks, m_NumPages); m_PageBreaks[++m_NumPages] = pos; } while (pos < m_Renderer->GetTotalHeight()); } @@ -370,7 +395,11 @@ void wxHtmlPrintout::RenderPage(wxDC *dc, int page) m_Renderer->Render((int) (ppmm_h * m_MarginLeft), (int) (ppmm_v * (m_MarginTop + (m_HeaderHeight == 0 ? 0 : m_MarginSpace)) + m_HeaderHeight), - m_PageBreaks[page-1]); + m_PageBreaks[page-1], FALSE, m_PageBreaks[page]-m_PageBreaks[page-1] +// Backporting note: we need to specify every argument for backporting (see +// include/wx/htmprint.h), but wx 2.5 will be able to default the +// last two arguments here. + ,NULL, 0); m_RendererHdr->SetDC(dc, (double)ppiPrinterY / (double)ppiScreenY); if (m_Headers[page % 2] != wxEmptyString) diff --git a/src/html/m_layout.cpp b/src/html/m_layout.cpp index e042da6e6a..638837ac7c 100644 --- a/src/html/m_layout.cpp +++ b/src/html/m_layout.cpp @@ -31,6 +31,118 @@ FORCE_LINK_ME(m_layout) +#include // bsearch() + +//----------------------------------------------------------------------------- +// wxHtmlPageBreakCell +//----------------------------------------------------------------------------- + +// Since html isn't a page-layout language, it doesn't support page +// page breaks directly--that requires CSS2 support. But a page-break +// facility is handy, and has been requested more than once on the +// mailing lists. This wxHtml tag handler implements just enough of +// CSS2 to support a page break by recognizing only +//

+// +// wxHtml maintains page breaks in wxHtmlPrintout::m_PageBreaks. The +// tag handler below adds appropriate offsets to that array member. +// wxHtmlDCRenderer::Render() accesses that array and makes a new page +// begin after each page-break tag. + +// The page-break handler does all its work in AdjustPagebreak(). For +// all tag handlers, that function adjusts the page-break position. +// For other tags, it determines whether the html element can fit on +// the remainder of the page; if it cannot fit, but must not be split, +// then the function moves the page break provided in the argument up, +// and returns 'true' to inform the caller that the argument was +// modified. +// +// Due to its special purpose, the page-break facility differs from +// other tags. It takes up no space, but it behaves as though there is +// never enough room to fit it on the remainder of the page--it always +// forces a page break. Therefore, unlike other elements that trigger +// a page break, it would never 'fit' on the following page either. +// Therefore it's necessary to compare each pagebreak candidate to the +// array wxHtmlPrintout::m_PageBreaks of pagebreaks already set, and +// set a new one only if it's not in that array. + +class WXDLLEXPORT wxHtmlPageBreakCell : public wxHtmlCell +{ + public: + wxHtmlPageBreakCell() {} + +// wx 2.5 will use this signature: +// bool AdjustPagebreak(int* pagebreak, int* known_pagebreaks = NULL, int number_of_pages = 0) const; + bool AdjustPagebreak(int* pagebreak) const; + + private: + DECLARE_NO_COPY_CLASS(wxHtmlPageBreakCell) +}; + +// Comparison routine for bsearch into an int* array of pagebreaks. +static int integer_compare(void const* i0, void const* i1) +{ + return *(int*)i0 - *(int*)i1; +} + +// wx 2.5 will use this signature: +// bool wxHtmlContainerCell::AdjustPagebreak(int *pagebreak, int* known_pagebreaks, int number_of_pages) const +// +// Workaround to backport html pagebreaks to 2.4.0: +// Actually, we're passing a pointer to struct wxHtmlKludge, casting +// that pointer to an int* . We don't need to do anything special +// here because that struct's first element is an int* to 'pagebreak'. +// Other struct members are addressed by casting that int* back to +// wxHtmlKludge*; they don't get modified, so we don't have to pass +// them back to the caller. +bool wxHtmlPageBreakCell::AdjustPagebreak(int* pagebreak) const +{ + // Workaround to backport html pagebreaks to 2.4.0: + wxHtmlKludge* kludge = (wxHtmlKludge*)pagebreak; + int* known_pagebreaks = kludge->known_pagebreaks; + int number_of_pages = kludge->number_of_pages; + + // When we are counting pages, 'known_pagebreaks' is non-NULL. + // That's the only time we change 'pagebreak'. Otherwise, pages + // were already counted, 'known_pagebreaks' is NULL, and we don't + // do anything except return FALSE. + // + // We also simply return FALSE if the 'pagebreak' argument is + // less than (vertically above) or the same as the current + // vertical position. Otherwise we'd be setting a pagebreak above + // the current cell, which is incorrect, or duplicating a + // pagebreak that has already been set. + if(NULL == known_pagebreaks || *pagebreak <= m_PosY) + { + return FALSE; + } + + // m_PosY is only the vertical offset from the parent. The pagebreak + // required here is the total page offset, so m_PosY must be added + // to the parent's offset and height. + int total_height = m_PosY + GetParent()->GetPosY() + GetParent()->GetHeight(); + + // Search the array of pagebreaks to see whether we've already set + // a pagebreak here. The standard bsearch() function is appropriate + // because the array of pagebreaks through known_pagebreaks[number_of_pages] + // is known to be sorted in strictly increasing order. '1 + number_of_pages' + // is used as a bsearch() argument because the array contains a leading + // zero plus one element for each page. + int* where = (int*) bsearch(&total_height, known_pagebreaks, + 1 + number_of_pages, sizeof(int), + integer_compare); + // Add a pagebreak only if there isn't one already set here. + if(NULL != where) + { + return FALSE; + } + else + { + *pagebreak = m_PosY; + return TRUE; + } +} + TAG_HANDLER_BEGIN(P, "P") TAG_HANDLER_PROC(tag) @@ -110,34 +222,57 @@ TAG_HANDLER_BEGIN(DIV, "DIV") TAG_HANDLER_PROC(tag) { - int old = m_WParser->GetAlign(); - wxHtmlContainerCell *c = m_WParser->GetContainer(); - if (c->GetFirstCell() != NULL) + if(tag.HasParam("STYLE")) { - m_WParser->CloseContainer(); - m_WParser->OpenContainer(); - c = m_WParser->GetContainer(); - c->SetAlign(tag); - m_WParser->SetAlign(c->GetAlignHor()); + if(tag.GetParam("STYLE").IsSameAs(wxString("PAGE-BREAK-BEFORE:ALWAYS"), FALSE)) + { + m_WParser->CloseContainer(); + m_WParser->OpenContainer()->InsertCell(new wxHtmlPageBreakCell); + m_WParser->CloseContainer(); + m_WParser->OpenContainer(); + return FALSE; + } + else + { + // Treat other STYLE parameters here when they're supported. + return FALSE; + } + } + else if(tag.HasParam("ALIGN")) + { + int old = m_WParser->GetAlign(); + wxHtmlContainerCell *c = m_WParser->GetContainer(); + if (c->GetFirstCell() != NULL) + { + m_WParser->CloseContainer(); + m_WParser->OpenContainer(); + c = m_WParser->GetContainer(); + c->SetAlign(tag); + m_WParser->SetAlign(c->GetAlignHor()); + } + else + { + c->SetAlign(tag); + m_WParser->SetAlign(c->GetAlignHor()); + } + + ParseInner(tag); + + m_WParser->SetAlign(old); + if (c->GetFirstCell() != NULL) + { + m_WParser->CloseContainer(); + m_WParser->OpenContainer(); + } + else + c->SetAlignHor(old); + + return TRUE; } else { - c->SetAlign(tag); - m_WParser->SetAlign(c->GetAlignHor()); + return FALSE; } - - ParseInner(tag); - - m_WParser->SetAlign(old); - if (c->GetFirstCell() != NULL) - { - m_WParser->CloseContainer(); - m_WParser->OpenContainer(); - } - else - c->SetAlignHor(old); - - return TRUE; } TAG_HANDLER_END(DIV)