From 9db0234abcfad88c95f1839a2eecffb4d9700f5b Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 25 Aug 2018 02:35:20 +0200 Subject: [PATCH 1/6] Allow passing already parsed HTML to wxHtmlDCRenderer This is more efficient when the same HTML is reused multiple times, e.g. for measuring it first and then rendering it. The new function also makes it simpler to parse HTML and manipulate it in some way before measuring and rendering it. --- include/wx/html/htmprint.h | 8 ++++++++ interface/wx/html/htmprint.h | 13 +++++++++++++ src/html/htmprint.cpp | 30 ++++++++++++++++++++++++++---- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/include/wx/html/htmprint.h b/include/wx/html/htmprint.h index 0c206ade6c..830a1d8362 100644 --- a/include/wx/html/htmprint.h +++ b/include/wx/html/htmprint.h @@ -54,6 +54,11 @@ public: // (see wxFileSystem for detailed explanation) void SetHtmlText(const wxString& html, const wxString& basepath = wxEmptyString, bool isdir = true); + // Sets the HTML cell that will be rendered: this is more efficient than + // using text as it allows to parse it only once. Note that the cell will + // be modified by this call. + void SetHtmlCell(wxHtmlContainerCell& cell); + // Sets fonts to be used when displaying HTML page. (if size null then default sizes used). void SetFonts(const wxString& normal_face, const wxString& fixed_face, const int *sizes = NULL); @@ -79,11 +84,14 @@ public: int GetTotalHeight() const; private: + void DoSetHtmlCell(wxHtmlContainerCell* cell); + wxDC *m_DC; wxHtmlWinParser *m_Parser; wxFileSystem *m_FS; wxHtmlContainerCell *m_Cells; int m_Width, m_Height; + bool m_ownsCells; wxDECLARE_NO_COPY_CLASS(wxHtmlDCRenderer); }; diff --git a/interface/wx/html/htmprint.h b/interface/wx/html/htmprint.h index 8e2dfdc9db..1b4a4e861b 100644 --- a/interface/wx/html/htmprint.h +++ b/interface/wx/html/htmprint.h @@ -168,6 +168,19 @@ public: const wxString& basepath = wxEmptyString, bool isdir = true); + /** + Associate the given HTML contents to the renderer. + + This is similar to SetHtmlText(), but is more efficient as the text can + be parsed only once, using wxHtmlParser::Parse(), and then passed to + wxHtmlDCRenderer multiple times or already reused for other purposes. + + Note that @a cell will be modified (e.g. laid out) by this function. + + @since 3.1.2 + */ + void SetHtmlCell(wxHtmlContainerCell& cell); + /** Set size of output rectangle, in pixels. Note that you @b can't change width of the rectangle between calls to Render() ! diff --git a/src/html/htmprint.cpp b/src/html/htmprint.cpp index a3acb7d007..86b0b7f51b 100644 --- a/src/html/htmprint.cpp +++ b/src/html/htmprint.cpp @@ -70,6 +70,7 @@ wxHtmlDCRenderer::wxHtmlDCRenderer() : wxObject() m_DC = NULL; m_Width = m_Height = 0; m_Cells = NULL; + m_ownsCells = false; m_Parser = new wxHtmlWinParser(); m_FS = new wxFileSystem(); m_Parser->SetFS(m_FS); @@ -80,7 +81,9 @@ wxHtmlDCRenderer::wxHtmlDCRenderer() : wxObject() wxHtmlDCRenderer::~wxHtmlDCRenderer() { - if (m_Cells) delete m_Cells; + if ( m_ownsCells ) + delete m_Cells; + if (m_Parser) delete m_Parser; if (m_FS) delete m_FS; } @@ -110,14 +113,33 @@ void wxHtmlDCRenderer::SetHtmlText(const wxString& html, const wxString& basepat wxCHECK_RET( m_DC, "SetDC() must be called before SetHtmlText()" ); wxCHECK_RET( m_Width, "SetSize() must be called before SetHtmlText()" ); - wxDELETE(m_Cells); - m_FS->ChangePathTo(basepath, isdir); - m_Cells = (wxHtmlContainerCell*) m_Parser->Parse(html); + + wxHtmlContainerCell* const cell = (wxHtmlContainerCell*) m_Parser->Parse(html); + wxCHECK_RET( cell, "Failed to parse HTML" ); + + DoSetHtmlCell(cell); + + m_ownsCells = true; +} + +void wxHtmlDCRenderer::DoSetHtmlCell(wxHtmlContainerCell* cell) +{ + if ( m_ownsCells ) + delete m_Cells; + + m_Cells = cell; m_Cells->SetIndent(0, wxHTML_INDENT_ALL, wxHTML_UNITS_PIXELS); m_Cells->Layout(m_Width); } +void wxHtmlDCRenderer::SetHtmlCell(wxHtmlContainerCell& cell) +{ + DoSetHtmlCell(&cell); + + m_ownsCells = false; +} + void wxHtmlDCRenderer::SetFonts(const wxString& normal_face, const wxString& fixed_face, const int *sizes) From 012baf1ff26c08c69bc01fe4ce01df0a794b0b15 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 25 Aug 2018 14:27:58 +0200 Subject: [PATCH 2/6] Remove unnecessary heap allocations in wxHtmlDCRenderer Make m_Parser and m_FS simple objects instead of pointers as it's completely unnecessary to allocate them on the heap here. Note that both wxHtmlWinParser (by explicitly including its header) and wxFileSystem (which is implicitly included via wx/html/htmlpars.h) are fully declared in the header, so using pointers doesn't even help with reducing compilation dependencies. No real changes. --- include/wx/html/htmprint.h | 4 ++-- src/html/htmprint.cpp | 17 ++++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/include/wx/html/htmprint.h b/include/wx/html/htmprint.h index 830a1d8362..554413d44c 100644 --- a/include/wx/html/htmprint.h +++ b/include/wx/html/htmprint.h @@ -87,8 +87,8 @@ private: void DoSetHtmlCell(wxHtmlContainerCell* cell); wxDC *m_DC; - wxHtmlWinParser *m_Parser; - wxFileSystem *m_FS; + wxFileSystem m_FS; + wxHtmlWinParser m_Parser; wxHtmlContainerCell *m_Cells; int m_Width, m_Height; bool m_ownsCells; diff --git a/src/html/htmprint.cpp b/src/html/htmprint.cpp index 86b0b7f51b..2ed4e19256 100644 --- a/src/html/htmprint.cpp +++ b/src/html/htmprint.cpp @@ -71,9 +71,7 @@ wxHtmlDCRenderer::wxHtmlDCRenderer() : wxObject() m_Width = m_Height = 0; m_Cells = NULL; m_ownsCells = false; - m_Parser = new wxHtmlWinParser(); - m_FS = new wxFileSystem(); - m_Parser->SetFS(m_FS); + m_Parser.SetFS(&m_FS); SetStandardFonts(DEFAULT_PRINT_FONT_SIZE); } @@ -83,9 +81,6 @@ wxHtmlDCRenderer::~wxHtmlDCRenderer() { if ( m_ownsCells ) delete m_Cells; - - if (m_Parser) delete m_Parser; - if (m_FS) delete m_FS; } @@ -93,7 +88,7 @@ wxHtmlDCRenderer::~wxHtmlDCRenderer() void wxHtmlDCRenderer::SetDC(wxDC *dc, double pixel_scale, double font_scale) { m_DC = dc; - m_Parser->SetDC(m_DC, pixel_scale, font_scale); + m_Parser.SetDC(m_DC, pixel_scale, font_scale); } @@ -113,9 +108,9 @@ void wxHtmlDCRenderer::SetHtmlText(const wxString& html, const wxString& basepat wxCHECK_RET( m_DC, "SetDC() must be called before SetHtmlText()" ); wxCHECK_RET( m_Width, "SetSize() must be called before SetHtmlText()" ); - m_FS->ChangePathTo(basepath, isdir); + m_FS.ChangePathTo(basepath, isdir); - wxHtmlContainerCell* const cell = (wxHtmlContainerCell*) m_Parser->Parse(html); + wxHtmlContainerCell* const cell = (wxHtmlContainerCell*) m_Parser.Parse(html); wxCHECK_RET( cell, "Failed to parse HTML" ); DoSetHtmlCell(cell); @@ -144,7 +139,7 @@ void wxHtmlDCRenderer::SetHtmlCell(wxHtmlContainerCell& cell) void wxHtmlDCRenderer::SetFonts(const wxString& normal_face, const wxString& fixed_face, const int *sizes) { - m_Parser->SetFonts(normal_face, fixed_face, sizes); + m_Parser.SetFonts(normal_face, fixed_face, sizes); if ( m_Cells ) m_Cells->Layout(m_Width); @@ -155,7 +150,7 @@ void wxHtmlDCRenderer::SetStandardFonts(int size, const wxString& normal_face, const wxString& fixed_face) { - m_Parser->SetStandardFonts(size, normal_face, fixed_face); + m_Parser.SetStandardFonts(size, normal_face, fixed_face); if ( m_Cells ) m_Cells->Layout(m_Width); From 869fb4f2bc662e4340c4797cf44d37af424522e9 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 25 Aug 2018 21:00:11 +0200 Subject: [PATCH 3/6] Remove CppUnit compatibility macros from wxHtmlParser test There was just a single test there, so remove all CppUnit boilerplate which accounted for more than 50% of the file before adding more tests. --- tests/html/htmlparser.cpp | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/tests/html/htmlparser.cpp b/tests/html/htmlparser.cpp index 29c3cb5c1f..4577b5baf0 100644 --- a/tests/html/htmlparser.cpp +++ b/tests/html/htmlparser.cpp @@ -24,37 +24,8 @@ #include "wx/html/winpars.h" -// ---------------------------------------------------------------------------- -// test class -// ---------------------------------------------------------------------------- - -class HtmlParserTestCase : public CppUnit::TestCase -{ -public: - HtmlParserTestCase() { } - -private: - CPPUNIT_TEST_SUITE( HtmlParserTestCase ); - CPPUNIT_TEST( Invalid ); - CPPUNIT_TEST_SUITE_END(); - - void Invalid(); - - wxDECLARE_NO_COPY_CLASS(HtmlParserTestCase); -}; - -// register in the unnamed registry so that these tests are run by default -CPPUNIT_TEST_SUITE_REGISTRATION( HtmlParserTestCase ); - -// also include in its own registry so that these tests can be run alone -CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( HtmlParserTestCase, "HtmlParserTestCase" ); - -// ---------------------------------------------------------------------------- -// tests themselves -// ---------------------------------------------------------------------------- - // Test that parsing invalid HTML simply fails but doesn't crash for example. -void HtmlParserTestCase::Invalid() +TEST_CASE("wxHtmlParser::ParseInvalid", "[html][parser][error]") { class NullParser : public wxHtmlWinParser { From 2895e5c4a10bbd968558f51ca54da54c8574a8e4 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 25 Aug 2018 21:14:40 +0200 Subject: [PATCH 4/6] Document that wxHtmlContainerCell::InsertCell() takes ownership This might have been already clear, but it doesn't hurt to specify it explicitly. --- interface/wx/html/htmlcell.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/wx/html/htmlcell.h b/interface/wx/html/htmlcell.h index 18b05a8a00..406a37b709 100644 --- a/interface/wx/html/htmlcell.h +++ b/interface/wx/html/htmlcell.h @@ -506,6 +506,9 @@ public: /** Inserts a new cell into the container. + + Note that the container takes ownership of the cell and will delete it + when it itself is destroyed. */ void InsertCell(wxHtmlCell* cell); From a59f5932dff3ecf29d7f0f7807886c266bdebb14 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 25 Aug 2018 21:16:55 +0200 Subject: [PATCH 5/6] Add wxHtmlContainerCell::Detach() This allows manipulating the HTML DOM from the outside, e.g. to detach a header element in order to be able to repeat it on all pages. --- include/wx/html/htmlcell.h | 5 +++ interface/wx/html/htmlcell.h | 14 +++++++++ src/html/htmlcell.cpp | 37 ++++++++++++++++++++++ tests/html/htmlparser.cpp | 60 ++++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+) diff --git a/include/wx/html/htmlcell.h b/include/wx/html/htmlcell.h index 282b7cd964..2c99923b80 100644 --- a/include/wx/html/htmlcell.h +++ b/include/wx/html/htmlcell.h @@ -459,6 +459,11 @@ public: // insert cell at the end of m_Cells list void InsertCell(wxHtmlCell *cell); + // Detach a child cell. After calling this method, it's the caller + // responsibility to destroy this cell (possibly by calling InsertCell() + // with it to attach it elsewhere). + void Detach(wxHtmlCell *cell); + // sets horizontal/vertical alignment void SetAlignHor(int al) {m_AlignHor = al; m_LastLayout = -1;} int GetAlignHor() const {return m_AlignHor;} diff --git a/interface/wx/html/htmlcell.h b/interface/wx/html/htmlcell.h index 406a37b709..fe17b29ddd 100644 --- a/interface/wx/html/htmlcell.h +++ b/interface/wx/html/htmlcell.h @@ -473,6 +473,20 @@ public: */ wxHtmlContainerCell(wxHtmlContainerCell* parent); + /** + Detach a child cell. + + Detaching a cell removes it from this container and allows to reattach + it to another one by using InsertCell(). Alternatively, this method can + be used to selectively remove some elements of the HTML document tree + by deleting the cell after calling it. + + @param cell Must be non-null and an immediate child of this cell. + + @since 3.1.2 + */ + void Detach(wxHtmlCell* cell); + /** Returns container's horizontal alignment. */ diff --git a/src/html/htmlcell.cpp b/src/html/htmlcell.cpp index e4392edc35..1768178b65 100644 --- a/src/html/htmlcell.cpp +++ b/src/html/htmlcell.cpp @@ -1159,6 +1159,43 @@ void wxHtmlContainerCell::InsertCell(wxHtmlCell *f) +void wxHtmlContainerCell::Detach(wxHtmlCell *cell) +{ + wxHtmlCell* const firstChild = GetFirstChild(); + if ( cell == firstChild ) + { + m_Cells = cell->GetNext(); + if ( m_LastCell == cell ) + m_LastCell = NULL; + } + else // Not the first child. + { + for ( wxHtmlCell* prev = firstChild;; ) + { + wxHtmlCell* const next = prev->GetNext(); + + // We can't reach the end of the children list without finding this + // cell, normally. + wxCHECK_RET( next, "Detaching cell which is not our child" ); + + if ( cell == next ) + { + prev->SetNext(cell->GetNext()); + if ( m_LastCell == cell ) + m_LastCell = prev; + break; + } + + prev = next; + } + } + + cell->SetParent(NULL); + cell->SetNext(NULL); +} + + + void wxHtmlContainerCell::SetAlign(const wxHtmlTag& tag) { wxString alg; diff --git a/tests/html/htmlparser.cpp b/tests/html/htmlparser.cpp index 4577b5baf0..61042a4616 100644 --- a/tests/html/htmlparser.cpp +++ b/tests/html/htmlparser.cpp @@ -46,4 +46,64 @@ TEST_CASE("wxHtmlParser::ParseInvalid", "[html][parser][error]") p.Parse("