[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] master 5e2fc70 007/156: Make HTML generation utiliti
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] master 5e2fc70 007/156: Make HTML generation utilities more type-safe |
Date: |
Tue, 30 Jan 2018 17:21:50 -0500 (EST) |
branch: master
commit 5e2fc7034076dfc92fafca45e0706bce4e928328
Author: Vadim Zeitlin <address@hidden>
Commit: Vadim Zeitlin <address@hidden>
Make HTML generation utilities more type-safe
Add element and void_element classes as well as predefined constants for
tags and attributes.
---
Makefile.am | 2 +
group_quote_pdf_gen_wx.cpp | 182 +++++++++++++++----------
html.cpp | 125 +++++++++++++++++
html.hpp | 326 +++++++++++++++++++++++++++++++++++++++++++++
html_text.hpp | 199 ---------------------------
objects.make | 1 +
6 files changed, 569 insertions(+), 266 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index a7dca3f..9164a0c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -317,6 +317,7 @@ liblmi_common_sources = \
global_settings.cpp \
group_values.cpp \
group_quote_pdf_gen.cpp \
+ html.cpp \
illustrator.cpp \
input.cpp \
input_harmonization.cpp \
@@ -1151,6 +1152,7 @@ noinst_HEADERS = \
group_quote_pdf_gen.hpp \
group_values.hpp \
handle_exceptions.hpp \
+ html.hpp \
icon_monger.hpp \
ieee754.hpp \
ihs_irc7702.hpp \
diff --git a/group_quote_pdf_gen_wx.cpp b/group_quote_pdf_gen_wx.cpp
index cc056c3..66ba8f1 100644
--- a/group_quote_pdf_gen_wx.cpp
+++ b/group_quote_pdf_gen_wx.cpp
@@ -28,7 +28,7 @@
#include "calendar_date.hpp" // jdn_t()
#include "data_directory.hpp" // AddDataDir()
#include "force_linking.hpp"
-#include "html_text.hpp"
+#include "html.hpp"
#include "ledger.hpp"
#include "ledger_invariant.hpp"
#include "ledger_text_formats.hpp" // ledger_format()
@@ -60,25 +60,28 @@ LMI_FORCE_LINKING_IN_SITU(group_quote_pdf_generator_wx)
namespace
{
-/// Transform 'html' -> '<br><br>html', but return empty string unchanged.
+/// Transform 's' -> '<br><br>s', but return empty string unchanged.
-html::text brbr(std::string const& html)
+html::text brbr(std::string const& s)
{
- return html::text::escape_for_html_elem(html)
- .wrap_if_not_empty<html::tag::br>()
- .wrap_if_not_empty<html::tag::br>()
- ;
+ using namespace html;
+
+ return s.empty()
+ ? text()
+ : tag::br + tag::br + text::from(s)
+ ;
}
-/// Transform 'html' -> '<br><br><b>html</b>', but return empty string
unchanged.
+/// Transform 's' -> '<br><br><b>s</b>', but return empty string unchanged.
-html::text brbrb(std::string const& html)
+html::text brbrb(std::string const& s)
{
- return html::text::escape_for_html_elem(html)
- .wrap_if_not_empty<html::tag::b>()
- .wrap_if_not_empty<html::tag::br>()
- .wrap_if_not_empty<html::tag::br>()
- ;
+ using namespace html;
+
+ return s.empty()
+ ? text()
+ : tag::br + tag::br + tag::b(text::from(s))
+ ;
}
/// Generate HTML representation of a field name and value in an HTML table.
@@ -86,18 +89,27 @@ html::text brbrb(std::string const& html)
/// The HTML fragment generated by this function contains two <td> tags with
/// the given contents.
-wxString name_value_as_html_table_data
+html::text name_value_as_html_table_data
(std::string const& name
,std::string const& value
)
{
- return wxString::Format
- ("<td nowrap align=\"right\"><b>%s%s </b></td>"
- "<td>%s </td>"
- ,html::text::escape_for_html_elem(name).as_string()
- ,(value.empty() ? "" : ":")
- ,html::text::escape_for_html_elem(value).as_string()
- );
+ using namespace html;
+
+ auto const nbsp2 = text::nbsp() + text::nbsp();
+
+ return
+ tag::td[attr::nowrap][attr::align("right")]
+ (tag::b
+ (text::from(name))
+ (text::from(value.empty() ? "" : ":"))
+ (nbsp2)
+ )
+ +
+ tag::td
+ (text::from(value))
+ (nbsp2 + nbsp2)
+ ;
}
/// Simple description of a custom field, consisting of a non-empty name and a
@@ -319,7 +331,7 @@ class group_quote_pdf_generator_wx
std::string premium_mode_;
std::string contract_state_;
std::string effective_date_;
- html::text footer_html_;
+ html::text footer_html_;
// Dynamically-determined fields.
std::string elected_riders_;
@@ -873,7 +885,7 @@ void group_quote_pdf_generator_wx::output_image_header
wxDCFontChanger set_bigger_font(pdf_dc, pdf_dc.GetFont().Scaled(1.5));
wxDCTextColourChanger set_white_text(pdf_dc, *wxWHITE);
- // Don't use escape_for_html_elem() here: instead, call
+ // Don't use html::text::from() here: instead, call
// wxString::FromUTF8() directly, e.g., to preserve literal '&'.
wxString const image_text
(wxString::FromUTF8(report_data_.short_product_.c_str())
@@ -895,45 +907,49 @@ void group_quote_pdf_generator_wx::output_document_header
,int* pos_y
)
{
- wxString const title_html = wxString::Format
- ("<table width=\"100%%\">"
- "<tr>"
- "<td align=\"center\"><i><font size=\"+1\">%s</font></i></td>"
- "</tr>"
- "<tr>"
- "<td align=\"center\"><i>Prepared Date: %s</i></td>"
- "</tr>"
- "<tr>"
- "<td align=\"center\"><i>Prepared By: %s</i></td>"
- "</tr>"
- "</table>"
- ,html::text::escape_for_html_elem(report_data_.company_).as_string()
- ,wxDateTime::Today().FormatDate()
-
,html::text::escape_for_html_elem(report_data_.prepared_by_).as_string()
- );
+ using namespace html;
+
+ auto title_html =
+ tag::table[attr::width("100%")]
+ (tag::tr
+ (tag::td[attr::align("center")]
+ (tag::i
+ (tag::font[attr::size("+1")]
+ (text::from(report_data_.company_)
+ )
+ )
+ )
+ )
+ )
+ (tag::tr
+ (tag::td[attr::align("center")]
+ (tag::i
+ (text::from
+ ("Prepared Date: "
+ +wxDateTime::Today().FormatDate().ToStdString()
+ )
+ )
+ )
+ )
+ )
+ (tag::tr
+ (tag::td[attr::align("center")]
+ (tag::i
+ (text::from("Prepared By: " +
report_data_.prepared_by_)
+ )
+ )
+ )
+ );
pdf_writer.output_html
(pdf_writer.get_horz_margin()
,*pos_y
,pdf_writer.get_page_width() / 2
- ,title_html
+ ,title_html.as_html()
);
- // Build the summary table with all the mandatory fields.
- wxString summary_html =
- "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"0\">"
- // This extra top empty row works around a bug in wxHTML
- // table positioning code: it uses the provided ordinate
- // coordinate as a base line of the first table line and
- // not as its top, as it ought to, so without this line
- // the rectangle drawn below wouldn't contain the header.
- "<tr>"
- "<td align=\"center\" colspan=\"4\"> </td>"
- "</tr>"
- "<tr>"
- "<td align=\"center\" colspan=\"4\"><font size=\"+1\">Plan Details
Summary</font></td>"
- "</tr>"
- ;
+ // Build the summary table with all the mandatory fields, starting by
+ // building the (partly) dynamic fields rows part.
// Add fixed fields first, then any additional ones,
// in left-to-right then top-to-bottom order.
@@ -955,24 +971,56 @@ void group_quote_pdf_generator_wx::output_document_header
std::vector<extra_summary_field> const& f = report_data_.extra_fields_;
fields.insert(fields.end(), f.begin(), f.end());
- bool parity = true;
- for(auto const& i : fields)
+ text fields_html;
+ for(std::size_t i = 0; i < fields.size(); i += 2)
{
- summary_html += parity ? "<tr>" : "";
- summary_html += name_value_as_html_table_data(i.name, i.value);
- summary_html += parity ? "" : "</tr>";
- parity = !parity;
+ auto row_html = name_value_as_html_table_data
+ (fields[i].name, fields[i].value
+ )
+ ;
+
+ if(i + 1 < fields.size())
+ {
+ row_html += name_value_as_html_table_data
+ (fields[i + 1].name, fields[i + 1].value
+ )
+ ;
+ }
+
+ fields_html += tag::tr(row_html);
}
- summary_html += parity ? "" : "</tr>";
// Finally close the summary table.
- summary_html += "</table>";
+ auto const summary_html =
+ tag::table[attr::width("100%")]
+ [attr::cellspacing("0")]
+ [attr::cellpadding("0")]
+ // This extra top empty row works around a bug in wxHTML
+ // table positioning code: it uses the provided ordinate
+ // coordinate as a base line of the first table line and
+ // not as its top, as it ought to, so without this line
+ // the rectangle drawn below wouldn't contain the header.
+ (tag::tr
+ (tag::td[attr::align("center")][attr::colspan("4")]
+ (text::nbsp())
+ )
+ )
+ (tag::tr
+ (tag::td[attr::align("center")][attr::colspan("4")]
+ (tag::font[attr::size("+1")]
+ (text::from("Plan Details Summary"))
+ )
+ )
+ )
+ (fields_html
+ )
+ ;
int const summary_height = pdf_writer.output_html
(pdf_writer.get_horz_margin() + pdf_writer.get_page_width() / 2
,*pos_y
,pdf_writer.get_page_width() / 2
- ,summary_html
+ ,summary_html.as_html()
);
// wxHTML tables don't support "frame" attribute, so draw the border around
@@ -1147,13 +1195,13 @@ void group_quote_pdf_generator_wx::output_footer
*pos_y += vert_skip;
}
- auto footer_html = report_data_.footer_html_.wrap<html::tag::p>();
+ auto footer_html = html::tag::p(report_data_.footer_html_);
*pos_y += pdf_writer.output_html
(pdf_writer.get_horz_margin()
,*pos_y
,pdf_writer.get_page_width()
- ,std::move(footer_html).as_string()
+ ,footer_html.as_html()
,output_mode
);
}
diff --git a/html.cpp b/html.cpp
new file mode 100644
index 0000000..e0f0738
--- /dev/null
+++ b/html.cpp
@@ -0,0 +1,125 @@
+// Utilities for representing and generating HTML.
+//
+// Copyright (C) 2017 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+#include "pchfile.hpp"
+
+#include "html.hpp"
+
+namespace html
+{
+
+namespace attr
+{
+
+extern attribute const align ("align");
+extern attribute const cellpadding ("cellpadding");
+extern attribute const cellspacing ("cellspacing");
+extern attribute const colspan ("colspan");
+extern attribute const nowrap ("nowrap");
+extern attribute const size ("size");
+extern attribute const width ("width");
+
+} // namespace attr
+
+namespace tag
+{
+
+extern element const b ("b");
+extern void_element const br ("br");
+extern element const font ("font");
+extern element const i ("i");
+extern element const p ("p");
+extern element const table ("table");
+extern element const td ("td");
+extern element const tr ("tr");
+
+} // namespace tag
+
+std::string attribute::as_string() const
+{
+ std::string s(name_);
+ if(!value_.empty())
+ {
+ s += "=";
+ // TODO: Escape quotes.
+ s += value_;
+ }
+ return s;
+}
+
+namespace detail
+{
+
+std::string any_element::get_start() const
+{
+ std::string s("<");
+ // Extra +1 for the space before attributes, even if it's not needed.
+ s.reserve(1 + std::strlen(name_) + 1 + attributes_.length() + 1);
+ s += name_;
+ if(!attributes_.empty())
+ {
+ s += " ";
+ s += attributes_;
+ }
+ s += ">";
+ return s;
+}
+
+void any_element::update_attributes(attribute const& attr)
+{
+ if(attributes_.empty())
+ {
+ attributes_ = attr.as_string();
+ }
+ else
+ {
+ attributes_ += " ";
+ attributes_ += attr.as_string();
+ }
+}
+
+} // namespace detail
+
+void element::update_contents(std::string&& contents)
+{
+ if(contents_.empty())
+ {
+ contents_ = std::move(contents);
+ }
+ else
+ {
+ contents_ += contents;
+ }
+}
+
+element::operator text() const
+{
+ std::string s(get_start());
+ s.reserve(s.length() + contents_.length() + 2 + std::strlen(name_) + 1);
+ s += contents_;
+ s += "</";
+ s += name_;
+ s += ">";
+
+ return text::from_html(std::move(s));
+}
+
+} // namespace html
diff --git a/html.hpp b/html.hpp
new file mode 100644
index 0000000..ecad033
--- /dev/null
+++ b/html.hpp
@@ -0,0 +1,326 @@
+// Utilities for representing and generating HTML.
+//
+// Copyright (C) 2017 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+#ifndef html_hpp
+#define html_hpp
+
+#include "config.hpp"
+
+#include <string>
+#include <utility> // std::move
+
+/// Namespace for helpers used for HTML generation.
+///
+/// Main idea is to avoid generating HTML using raw strings, which is error
+/// prone and difficult to read and maintain. One source of errors is
+/// forgetting to escape special characters, such as "<" or "&", and html::text
+/// class helps with this by providing from() method doing it automatically.
+///
+/// Another one is forgetting to close a tag (or closing a wrong one) and while
+/// html::text is too low level to help with this, html::element can be used
+/// for structured HTML generation, which guarantees that the result is
+/// well-formed. By using predefined constants in html::tag and html::attr
+/// namespaces, typos in the element names can also be automatically avoided.
+namespace html
+{
+
+/// Represents a piece of text containing HTML.
+///
+/// This is a separate type for type safety, e.g. to avoid passing raw,
+/// unescaped, strings to a function expecting HTML (or, less catastrophically,
+/// but still wrongly, passing already escaped HTML to a function doing
+/// escaping internally).
+///
+/// As it still needs to be converted to a string sooner or later to be really
+/// used, it does provide a conversion -- but it can be used only once.
+class text
+{
+ public:
+ // This type has value semantics.
+ text() = default;
+ text(text const&) = default;
+ text(text&&) = default;
+ text& operator=(text const&) = default;
+ text& operator=(text&&) = default;
+
+ /// Escape special XML characters in the given string, ensuring that it
+ /// appears correctly inside HTML element contents. Notice that we don't
+ /// need to escape quotes here as we never use the result of this function
+ /// inside an HTML attribute, only inside HTML elements.
+ static text from(std::string const& s)
+ {
+ std::string z;
+ z.reserve(s.length());
+ for(auto const& c : s)
+ {
+ switch(c)
+ {
+ case '<': z += "<" ; break;
+ case '>': z += ">" ; break;
+ case '&': z += "&"; break;
+ default : z += c ;
+ }
+ }
+
+ return text{std::move(z)};
+ }
+
+ /// Use the given string with HTML inside it directly. No escaping is done
+ /// by this ctor.
+ static text from_html(std::string s)
+ {
+ return text{std::move(s)};
+ }
+
+ /// Just a symbolic name for a non breaking space HTML entiry.
+ static text nbsp()
+ {
+ return text::from_html(" ");
+ }
+
+ /// Append another text fragment to this one.
+ ///
+ /// This method allows chained invocation for appending more than one
+ /// fragment at once.
+ text& operator+=(text const& t)
+ {
+ m_html += t.m_html;
+
+ return *this;
+ }
+
+ std::string const& as_html() const&
+ {
+ return m_html;
+ }
+
+ std::string&& as_html() &&
+ {
+ return std::move(m_html);
+ }
+
+ private:
+ // This move ctor is private and does not perform any escaping.
+ explicit text(std::string&& html)
+ :m_html{html}
+ {
+ }
+
+ std::string m_html;
+};
+
+/// Represents a single attribute of an HTML element.
+class attribute
+{
+ public:
+ explicit attribute(char const* name)
+ :name_{name}
+ {
+ }
+
+ attribute operator()(std::string value) const
+ {
+ return attribute(name_, std::move(value));
+ }
+
+ std::string as_string() const;
+
+ private:
+ attribute(char const* name, std::string&& value)
+ :name_{name}
+ ,value_{std::move(value)}
+ {
+ }
+
+ char const* const name_;
+ std::string const value_;
+};
+
+namespace detail
+{
+
+class any_element
+{
+ public:
+ /// Ctor should only be used with literal strings as argument.
+ explicit any_element(char const* name)
+ :name_(name)
+ {
+ }
+
+ protected:
+ // Return the opening tag of the element, with attributes, if any.
+ std::string get_start() const;
+
+ // Add the given attribute to our attributes string.
+ void update_attributes(attribute const& attr);
+
+ char const* const name_;
+
+ private:
+ std::string attributes_;
+};
+
+} // namespace detail
+
+/// Represents a normal HTML element which can have content inside it.
+///
+/// This class uses the so called fluent API model in which calls to its
+/// different methods return the object itself and so can be chained together.
+/// For example (assuming an implicit "using namespace html"):
+///
+/// auto para_with_link =
+/// tag::p[attr::align("center")]
+/// (text("Link to "))
+/// (tag::a[attr::href("http://lmi.nongnu.org/")]
+/// (text::from("lmi project page"))
+/// )
+/// ;
+
+class element : private detail::any_element
+{
+ public:
+ /// Ctor should only be used with literal strings as argument.
+ explicit element(char const* name)
+ :detail::any_element(name)
+ {
+ }
+
+ element(element const&) = default;
+ element(element&&) = default;
+
+ /// Add an attribute.
+ element operator[](attribute const& attr) const&
+ {
+ element e{*this};
+ e.update_attributes(attr);
+ return e;
+ }
+
+ element&& operator[](attribute const& attr) &&
+ {
+ update_attributes(attr);
+ return std::move(*this);
+ }
+
+ /// Add inner contents.
+ element operator()(text contents) const&
+ {
+ element e{*this};
+ e.update_contents(std::move(contents).as_html());
+ return e;
+ }
+
+ element&& operator()(text contents) &&
+ {
+ update_contents(std::move(contents).as_html());
+ return std::move(*this);
+ }
+
+ /// Convert to HTML text with this element and its contents.
+ ///
+ /// This implicit conversion operator is not really dangerous as it is
+ /// normal to represent an HTML element as HTML text and it's very
+ /// convenient to have it as it allows to accept either another element or
+ /// text in our own operator() and also use operator+() defined below to
+ /// concatenate HTML elements without having to convert them to text
+ /// beforehand.
+ operator text() const;
+
+ private:
+ void update_contents(std::string&& contents);
+
+ std::string contents_;
+};
+
+/// Represents a void HTML element which can't have anything inside it.
+class void_element : private detail::any_element
+{
+ public:
+ explicit void_element(char const* name)
+ :detail::any_element(name)
+ {
+ }
+
+ void_element(void_element const&) = default;
+ void_element(void_element&&) = default;
+
+ void_element operator[](attribute const& attr) const&
+ {
+ void_element e{*this};
+ e.update_attributes(std::move(attr));
+ return e;
+ }
+
+ void_element&& operator[](attribute const& attr) &&
+ {
+ update_attributes(std::move(attr));
+ return std::move(*this);
+ }
+
+ operator text() const
+ {
+ return text::from_html(get_start());
+ }
+};
+
+/// Namespace for HTML attributes.
+
+namespace attr
+{
+
+extern attribute const align;
+extern attribute const cellpadding;
+extern attribute const cellspacing;
+extern attribute const colspan;
+extern attribute const nowrap;
+extern attribute const size;
+extern attribute const width;
+
+} // namespace attr
+
+/// Namespace for HTML tags.
+
+namespace tag
+{
+
+extern element const b;
+extern void_element const br;
+extern element const font;
+extern element const i;
+extern element const p;
+extern element const table;
+extern element const td;
+extern element const tr;
+
+} // namespace tag
+
+inline
+text operator+(text t1, text const& t2)
+{
+ auto t{std::move(t1)};
+ t += t2;
+ return t;
+}
+
+} // namespace html
+
+#endif // html_hpp
diff --git a/html_text.hpp b/html_text.hpp
deleted file mode 100644
index 3f6c3c4..0000000
--- a/html_text.hpp
+++ /dev/null
@@ -1,199 +0,0 @@
-// Text representing HTML contents.
-//
-// Copyright (C) 2017 Gregory W. Chicares.
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License version 2 as
-// published by the Free Software Foundation.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software Foundation,
-// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
-//
-// http://savannah.nongnu.org/projects/lmi
-// email: <address@hidden>
-// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
-
-#ifndef html_text_hpp
-#define html_text_hpp
-
-#include "config.hpp"
-
-#include <string>
-#include <utility> // std::move
-
-/// Namespace for helpers used for HTML generation.
-namespace html
-{
-
-/// Namespace for the support HTML tags.
-///
-/// Tags are only used as template arguments, so they don't need to be defined,
-/// just declared -- and tag::info below specialized for them.
-
-namespace tag
-{
-
-struct b;
-struct br;
-struct p;
-
-template<typename T>
-struct info;
-
-template<>
-struct info<b>
-{
- static char const* get_name() { return "b"; }
- static bool has_end() { return true; }
-};
-
-template<>
-struct info<br>
-{
- static char const* get_name() { return "br"; }
- static bool has_end() { return false; }
-};
-
-template<>
-struct info<p>
-{
- static char const* get_name() { return "p"; }
- static bool has_end() { return true; }
-};
-
-} // namespace tag
-
-/// Represents a piece of text containing HTML.
-///
-/// This is a separate type for type safety, e.g. to avoid passing raw,
-/// unescaped, strings to a function expecting HTML (or, less catastrophically,
-/// but still wrongly, passing already escaped HTML to a function doing
-/// escaping internally).
-///
-/// As it still needs to be converted to a string sooner or later to be really
-/// used, it does provide a conversion -- but it can be used only once.
-class text
-{
-public:
- // This type has value semantics.
- text() = default;
- text(text const&) = default;
- text(text &&) = default;
- text& operator=(text const&) = default;
- text& operator=(text&&) = default;
-
- /// Escape special XML characters in the given string, ensuring that it
- /// appears correctly inside HTML element contents. Notice that we don't
- /// need to escape quotes here as we never use the result of this function
- /// inside an HTML attribute, only inside HTML elements.
- static text escape_for_html_elem(std::string const& s)
- {
- std::string z;
- z.reserve(s.length());
- for(auto const& c : s)
- {
- switch(c)
- {
- case '<': z += "<" ; break;
- case '>': z += ">" ; break;
- case '&': z += "&"; break;
- default : z += c ;
- }
- }
-
- return text{std::move(z)};
- }
-
- /// Wrap contents of this HTML snippet into the given tag (or just prepend
- /// the tag if it doesn't have the matching end tag).
- template <typename T>
- text wrap() const
- {
- return do_wrap(tag::info<T>::get_name(), tag::info<T>::has_end());
- }
-
- /// Wrap contents of this HTML snippet into the given tag (or just prepend
- /// the tag if it doesn't have the matching end tag), but only if it is not
- /// empty -- otherwise just return empty text.
- ///
- /// For the tags without matching closing tags, such as e.g. "<br>",
- /// wrapping the text means just prepending the tag to it. This is still
- /// done only if the text is not empty.
- template <typename T>
- text wrap_if_not_empty() const
- {
- return m_html.empty() ? text{} : wrap<T>();
- }
-
- /// Append another HTML fragment to this one.
- ///
- /// This method allows chained invocation for appending more than one
- /// fragment at once.
- text& operator+=(text t)
- {
- m_html += std::move(t).m_html;
-
- return *this;
- }
-
- /// Return the string with UTF-8 encoded HTML text of the given object
- /// consuming it in the process.
- ///
- /// Notice that this method can only be called on rvalue references, and so
- /// it can't be used more than once.
- std::string&& as_string() &&
- {
- return std::move(m_html);
- }
-
-private:
- // This move ctor is private and does not perform any escaping.
- text(std::string&& html)
- :m_html{html}
- {
- }
-
- // Type-independent part implementation of public wrap(): having it as a
- // separate function avoids template bloat.
- text do_wrap(char const* outer_tag, bool has_end) const
- {
- std::string z;
- z.reserve(m_html.length() + 10);
-
- z = '<';
- z += outer_tag;
- z += '>';
-
- z += m_html;
-
- if(has_end)
- {
- z += "</";
- z += outer_tag;
- z += '>';
- }
-
- return text{std::move(z)};
- }
-
-
- std::string m_html;
-};
-
-inline
-text operator+(text t1, text t2)
-{
- auto t{t1};
- t += t2;
- return t;
-}
-
-} // namespace html
-
-#endif // html_text_hpp
diff --git a/objects.make b/objects.make
index 70c2649..37dbef0 100644
--- a/objects.make
+++ b/objects.make
@@ -205,6 +205,7 @@ common_common_objects := \
global_settings.o \
group_quote_pdf_gen.o \
group_values.o \
+ html.o \
illustrator.o \
input.o \
input_harmonization.o \
- [lmi-commits] [lmi] master updated (70fb246 -> 408ba18), Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master cfdf224 016/156: Use raw multiline string instead of concatenating several strings, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 1d99e76 008/156: Make pdf_writer_wx::output_html() type safe by taking html::text, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master e43a485 005/156: Further improve wxPdfDocument API encapsulation and reuse, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 7d278ef 011/156: Add footer to the cover page, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master d5d8185 057/156: Add tabular_details page, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master bb64784 013/156: Add a helper allowing to interpolate variables in strings, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master b72ced6 014/156: Replace ad hoc illustration properties with HTML interpolator, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master e7d763d 066/156: Add company logo display to the PDF illustration header, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master f575c94 003/156: Refactor more group_quote_pdf_gen_wx code to allow its reuse, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 5e2fc70 007/156: Make HTML generation utilities more type-safe,
Greg Chicares <=
- [lmi-commits] [lmi] master 7ed2bbf 027/156: Simplify footer generation code by moving font tag outside, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master d7244c1 043/156: Change the colour used for lines and borders, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 41ae40d 089/156: Fix wrong "<br>" tag in the header template, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master acb7aae 049/156: Add numbered_page::get_extra_pages_needed() hook, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master c0d68e1 122/156: Add rate of return pages of the individual placement illustration, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 875ca7d 039/156: Rename StateIsTX ledger variable to StateIsTexas, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 31048c8 047/156: Make wx_table_generator even more customizable, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 10dd0a9 051/156: Factor enum_output_mode into a separate header to allow its reuse, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 8fa972e 052/156: Add "Tabular Detail, continued" page to the PDF illustration, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master ca6e17c 031/156: Add function generating a standard header and use it, Greg Chicares, 2018/01/30