[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] master 1c398a8 7/7: Implement support for headers fo
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] master 1c398a8 7/7: Implement support for headers for normal PDF illustration pages |
Date: |
Tue, 18 Sep 2018 13:29:31 -0400 (EDT) |
branch: master
commit 1c398a864f52103b63276f2ad13ad89e20f03bc1
Author: Vadim Zeitlin <address@hidden>
Commit: Gregory W. Chicares <address@hidden>
Implement support for headers for normal PDF illustration pages
The <header> tag can now be used in HTML templates to define the
contents of a header which will be repeated on any subsequent physical
pages if the logical page contents doesn't fit into a single one.
Show this tag in action in the "Explanatory Notes" page of reg_d
individual illustration: with sample2ipp its second page correctly
repeats the logo and the title now, while it didn't have them before.
---
ledger_pdf_generator_wx.cpp | 146 +++++++++++++++++++++++++++++++++++++++-----
pdf_writer_wx.cpp | 16 +++--
pdf_writer_wx.hpp | 2 +
reg_d_indiv_notes1.mst | 8 ++-
4 files changed, 149 insertions(+), 23 deletions(-)
diff --git a/ledger_pdf_generator_wx.cpp b/ledger_pdf_generator_wx.cpp
index 53ee2aa..3b69613 100644
--- a/ledger_pdf_generator_wx.cpp
+++ b/ledger_pdf_generator_wx.cpp
@@ -435,6 +435,38 @@ class using_illustration_table
}
};
+// Custom handler for the HTML <header> tag not natively recognized by wxHTML.
+// It also allows to find the container cell corresponding to the header
+// contents later by assigning a unique ID to it.
+static char const* const header_cell_id = "_lmi_page_header_id";
+
+TAG_HANDLER_BEGIN(page_header, "HEADER")
+ TAG_HANDLER_PROC(tag)
+ {
+ auto container = m_WParser->GetContainer();
+
+ // Set a unique ID for this container to allow finding it later.
+ container->SetId(header_cell_id);
+
+ // Use a nested container so that nested tags that close and reopen a
+ // container again close this one, but still remain inside the outer
+ // "header" container which will be detached from the main page HTML in
+ // its entirety.
+ m_WParser->OpenContainer();
+
+ ParseInner(tag);
+
+ // Close both the inner and the outer containers and reopen the
+ // new current one.
+ m_WParser->CloseContainer();
+ m_WParser->CloseContainer();
+ m_WParser->OpenContainer();
+
+ // Return true to indicate that we've parsed the entire tag contents.
+ return true;
+ }
+TAG_HANDLER_END(page_header)
+
// Base class for our custom HTML cells providing a way to pass them
// information about the PDF document being generated and the ledger used to
// generate it.
@@ -782,7 +814,7 @@ class pdf_illustration : protected html_interpolator
// Notice that the upper footer template name can be overridden at the page
// level, the functions here define the default for all illustration pages.
//
- // These functions are used by the pages deriving from page_with_footer.
+ // These functions are used by the pages deriving from page_with_marginals.
virtual std::string get_upper_footer_template_name() const = 0;
virtual std::string get_lower_footer_template_name() const = 0;
@@ -967,14 +999,15 @@ class cover_page : public page
}
};
-// Base class for all pages with a footer.
-class page_with_footer : public page
+// Base class for all pages with a footer and/or header, collectively called
+// "marginals".
+class page_with_marginals : public page
{
public:
using page::page;
- // Override pre_render() to compute footer_top_ which is needed in the
- // derived classes' overridden get_extra_pages_needed().
+ // Override pre_render() to compute page_top_ and footer_top_ which are
+ // needed in the derived classes' overridden get_extra_pages_needed().
void pre_render
(Ledger const& // ledger
,pdf_writer_wx & writer
@@ -983,6 +1016,19 @@ class page_with_footer : public page
auto const frame_horz_margin = writer.get_horz_margin();
auto const frame_width = writer.get_page_width();
+ page_top_ = writer.get_vert_margin();
+
+ if(auto header_html = get_header_html(); header_html)
+ {
+ page_top_ += writer.output_html
+ (frame_horz_margin
+ ,0
+ ,frame_width
+ ,*header_html
+ ,oe_only_measure
+ );
+ }
+
// We implicitly assume here that get_footer_lower_html() result
// doesn't materially depend on the exact value of the page number as
// we don't know its definitive value here yet. In theory, this doesn't
@@ -1029,6 +1075,19 @@ class page_with_footer : public page
auto& pdf_dc = writer.dc();
+ // Render the header, if any.
+ if(auto header_html = get_header_html(); header_html)
+ {
+ writer.output_html
+ (frame_horz_margin
+ ,writer.get_vert_margin()
+ ,frame_width
+ ,*header_html
+ );
+ }
+
+ // Render the footer, consisting of an optional upper and always
+ // present lower part.
auto y = footer_top_;
if(auto const& upper_template = get_upper_footer_template_name();
!upper_template.empty())
@@ -1070,11 +1129,31 @@ class page_with_footer : public page
return footer_top_;
}
+ int get_page_body_top() const
+ {
+ return page_top_;
+ }
+
+ int get_page_body_height() const
+ {
+ return get_footer_top() - get_page_body_top();
+ }
+
private:
// Function to be overridden in the base class which should actually return
// the page number or equivalent string (e.g. "Appendix").
virtual std::string get_page_number() const = 0;
+ // Return non-owning pointer to the cell representing the header contents.
+ // Returns null by default as most pages don't have a header or, at least,
+ // not a header which needs to be repeated on the continuation physical
+ // pages and so one that can't be treated as just being the beginning of
+ // the main page body.
+ virtual wxHtmlContainerCell* get_header_html() const
+ {
+ return nullptr;
+ }
+
// This function forwards to the illustration by default, but can be
// overridden to define a page-specific footer if necessary.
virtual std::string get_upper_footer_template_name() const
@@ -1113,15 +1192,16 @@ class page_with_footer : public page
);
}
+ int page_top_ = 0;
int footer_top_ = 0;
};
// Base class for all pages showing the page number in the footer.
//
-// In addition to actually providing page_with_footer with the correct string
-// to show in the footer, this class implicitly handles the page count by
-// incrementing it whenever a new object of this class is pre-rendered.
-class numbered_page : public page_with_footer
+// In addition to actually providing page_with_marginals with the correct
+// string to show in the footer, this class implicitly handles the page count
+// by incrementing it whenever a new object of this class is pre-rendered.
+class numbered_page : public page_with_marginals
{
public:
// Must be called before creating the first numbered page.
@@ -1134,7 +1214,7 @@ class numbered_page : public page_with_footer
(pdf_illustration const& illustration
,html_interpolator const& interpolate_html
)
- :page_with_footer{illustration, interpolate_html}
+ :page_with_marginals{illustration, interpolate_html}
{
// This assert would fail if start_numbering() hadn't been called
// before creating a numbered page, as it should be.
@@ -1146,7 +1226,7 @@ class numbered_page : public page_with_footer
,pdf_writer_wx & writer
) override
{
- page_with_footer::pre_render(ledger, writer);
+ page_with_marginals::pre_render(ledger, writer);
this_page_number_ = ++last_page_number_;
@@ -1235,7 +1315,7 @@ class standard_page : public numbered_page
) override
{
// Before calling the base class version, parse the HTML to initialize
- // page_body_cell_.
+ // page_body_cell_ and header_cell_.
parse_page_html(writer);
numbered_page::pre_render(ledger, writer);
@@ -1258,7 +1338,7 @@ class standard_page : public numbered_page
writer.output_html
(writer.get_horz_margin()
- ,writer.get_vert_margin()
+ ,get_page_body_top()
,writer.get_page_width()
,*page_body_cell_
,last_page_break
@@ -1271,7 +1351,7 @@ class standard_page : public numbered_page
private:
// Parse HTML page contents once and store the result in page_body_cell_
- // member variable.
+ // and header_cell_ member variables.
//
// Throws if parsing fails.
void parse_page_html(pdf_writer_wx& writer)
@@ -1289,6 +1369,40 @@ class standard_page : public numbered_page
("failed to parse template '" +
std::string{page_template_name_} + "'"
);
}
+
+ // Check if the page has a header tag and extract it from it in this
+ // case. It is not an error if there is no header in this page.
+ for(auto cell = page_body_cell_->GetFirstChild(); cell; cell =
cell->GetNext())
+ {
+ if(cell->GetId() == header_cell_id)
+ {
+ // Detach the cell from the tree to prevent it from being
+ // rendered as part of the page body.
+ page_body_cell_->Detach(cell);
+
+ // And attach it to another HTML document representing just
+ // the header contents.
+ //
+ // Note that we can't just use this cell on its own, we
+ // must let wxHtmlWinParser build the usual structure as
+ // wxHTML relies on having extra cells in its DOM, notably
+ // the wxHtmlFontCell setting the initial document font.
+ wxHtmlWinParser html_parser;
+ writer.initialize_html_parser(html_parser);
+ html_parser.InitParser(wxString{});
+ header_cell_.reset
+
(static_cast<wxHtmlContainerCell*>(html_parser.GetProduct())
+ );
+ header_cell_->InsertCell(cell);
+
+ break;
+ }
+ }
+ }
+
+ wxHtmlContainerCell* get_header_html() const override
+ {
+ return header_cell_.get();
}
int get_extra_pages_needed
@@ -1298,7 +1412,7 @@ class standard_page : public numbered_page
{
page_break_positions_ = writer.paginate_html
(writer.get_page_width()
- ,get_footer_top() - writer.get_vert_margin()
+ ,get_page_body_height()
,*page_body_cell_
);
@@ -1310,6 +1424,7 @@ class standard_page : public numbered_page
char const* const page_template_name_;
std::unique_ptr<wxHtmlContainerCell> page_body_cell_;
+ std::unique_ptr<wxHtmlContainerCell> header_cell_;
std::vector<int> page_break_positions_;
};
@@ -1599,6 +1714,7 @@ TAG_HANDLER_BEGIN(unbreakable_paragraph, "P")
TAG_HANDLER_END(unbreakable_paragraph)
TAGS_MODULE_BEGIN(lmi_illustration)
+ TAGS_MODULE_ADD(page_header)
TAGS_MODULE_ADD(scaled_image)
TAGS_MODULE_ADD(numeric_summary_table)
TAGS_MODULE_ADD(unbreakable_paragraph)
diff --git a/pdf_writer_wx.cpp b/pdf_writer_wx.cpp
index 33957c1..40ddf87 100644
--- a/pdf_writer_wx.cpp
+++ b/pdf_writer_wx.cpp
@@ -101,10 +101,6 @@ pdf_writer_wx::pdf_writer_wx
.FaceName("Helvetica")
);
- // Create an HTML parser to allow easily adding HTML contents to the
output.
- html_parser_.SetDC(&pdf_dc_);
- html_parser_.SetFonts("Helvetica", "Courier", font_sizes.data());
-
// Create the virtual file system object for loading images referenced from
// HTML and interpret relative paths from the data directory.
html_vfs_.reset(new wxFileSystem());
@@ -112,7 +108,9 @@ pdf_writer_wx::pdf_writer_wx
(global_settings::instance().data_directory().string()
,true // argument is a directory, not file path
);
- html_parser_.SetFS(html_vfs_.get());
+
+ // Create an HTML parser to allow easily adding HTML contents to the
output.
+ initialize_html_parser(html_parser_);
}
/// Start a new page in the output PDF document.
@@ -327,6 +325,14 @@ int pdf_writer_wx::output_html
return height;
}
+void pdf_writer_wx::initialize_html_parser(wxHtmlWinParser& html_parser)
+{
+ html_parser.SetDC(&pdf_dc_);
+ DoSetFonts(html_parser, html_font_sizes_);
+
+ html_parser.SetFS(html_vfs_.get());
+}
+
std::unique_ptr<wxHtmlContainerCell> pdf_writer_wx::parse_html(html::text&&
html)
{
return std::unique_ptr<wxHtmlContainerCell>
diff --git a/pdf_writer_wx.hpp b/pdf_writer_wx.hpp
index 3ad97b8..d5ea584 100644
--- a/pdf_writer_wx.hpp
+++ b/pdf_writer_wx.hpp
@@ -108,6 +108,8 @@ class pdf_writer_wx
// Helper methods for working with HTML contents.
+ void initialize_html_parser(wxHtmlWinParser& html_parser);
+
std::unique_ptr<wxHtmlContainerCell> parse_html(html::text&& html);
// Page metrics: the page width and height are the size of the page region
diff --git a/reg_d_indiv_notes1.mst b/reg_d_indiv_notes1.mst
index 2cbf758..58a30ec 100644
--- a/reg_d_indiv_notes1.mst
+++ b/reg_d_indiv_notes1.mst
@@ -19,10 +19,12 @@
snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
}}
-{{! No header on this page, but still use the logo. }}
-<img inv_factor="0.27" src="company_logo.png"></img>
+<header>
+ {{! No header on this page, but still use the logo. }}
+ <img inv_factor="0.27" src="company_logo.png"></img>
-<p align="center"><font size="+1"><b>Explanatory Notes</b></font></p>
+ <p align="center"><font size="+1"><b>Explanatory Notes</b></font></p>
+</header>
<font size="-1">
- [lmi-commits] [lmi] master updated (be7b9ff -> 1c398a8), Greg Chicares, 2018/09/18
- [lmi-commits] [lmi] master f4cf784 3/7: Make standard_supplemental_report ctor helper static, Greg Chicares, 2018/09/18
- [lmi-commits] [lmi] master e3d4471 1/7: Upgrade wx, Greg Chicares, 2018/09/18
- [lmi-commits] [lmi] master d143cbe 5/7: Use C++17 if-with-initializer to properly localize a variable, Greg Chicares, 2018/09/18
- [lmi-commits] [lmi] master 71dc467 4/7: Pass the illustration and interpolator to the page ctor, Greg Chicares, 2018/09/18
- [lmi-commits] [lmi] master a3ee7ce 2/7: Instal wxPdfDocument using Git instead of snapshot download, Greg Chicares, 2018/09/18
- [lmi-commits] [lmi] master b2ec7ac 6/7: Avoid parsing the same HTML more than once in standard pages, Greg Chicares, 2018/09/18
- [lmi-commits] [lmi] master 1c398a8 7/7: Implement support for headers for normal PDF illustration pages,
Greg Chicares <=