[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] master 783befa 006/156: Add separate class for repre
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] master 783befa 006/156: Add separate class for representing HTML contents |
Date: |
Tue, 30 Jan 2018 17:21:50 -0500 (EST) |
branch: master
commit 783befa028b9b7adde2f676bf6674dae3662e8e3
Author: Vadim Zeitlin <address@hidden>
Commit: Vadim Zeitlin <address@hidden>
Add separate class for representing HTML contents
This is the first step in making HTML generation code more type-safe,
although right now this is not really the case yet, as html_text objects
still have to be created from and converted to strings in many places.
---
group_quote_pdf_gen_wx.cpp | 126 +++++-----------------------
html_text.hpp | 199 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 218 insertions(+), 107 deletions(-)
diff --git a/group_quote_pdf_gen_wx.cpp b/group_quote_pdf_gen_wx.cpp
index fb94998..cc056c3 100644
--- a/group_quote_pdf_gen_wx.cpp
+++ b/group_quote_pdf_gen_wx.cpp
@@ -28,6 +28,7 @@
#include "calendar_date.hpp" // jdn_t()
#include "data_directory.hpp" // AddDataDir()
#include "force_linking.hpp"
+#include "html_text.hpp"
#include "ledger.hpp"
#include "ledger_invariant.hpp"
#include "ledger_text_formats.hpp" // ledger_format()
@@ -59,114 +60,25 @@ LMI_FORCE_LINKING_IN_SITU(group_quote_pdf_generator_wx)
namespace
{
-/// 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.
-
-wxString escape_for_html_elem(std::string const& s)
-{
- wxString const u = wxString::FromUTF8(s.c_str());
-
- wxString z;
- z.reserve(u.length());
- for(auto const& i : u)
- {
- switch(i.GetValue())
- {
- case '<': z += "<" ; break;
- case '>': z += ">" ; break;
- case '&': z += "&"; break;
- default : z += i ;
- }
- }
- return z;
-}
-
-/// 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;
-
-} // namespace tag
-
-template<typename T>
-struct tag_info;
-
-template<>
-struct tag_info<tag::b>
-{
- static char const* get_name() { return "b"; }
- static bool has_end() { return true; }
-};
-
-template<>
-struct tag_info<tag::br>
-{
- static char const* get_name() { return "br"; }
- static bool has_end() { return false; }
-};
-
-} // namespace html
-
-/// Wrap the given text in an HTML tag if it is not empty, otherwise just
-/// return an empty string.
-///
-/// 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>
-wxString wrap_if_not_empty(wxString const& html)
-{
- wxString result;
- if(!html.empty())
- {
- result << '<' << html::tag_info<T>::get_name() << '>' << html;
- if(html::tag_info<T>::has_end())
- {
- result << "</" << html::tag_info<T>::get_name() << '>';
- }
- }
-
- return result;
-}
-
/// Transform 'html' -> '<br><br>html', but return empty string unchanged.
-wxString brbr(std::string const& html)
+html::text brbr(std::string const& html)
{
- return
- wrap_if_not_empty<html::tag::br>
- (wrap_if_not_empty<html::tag::br>
- (escape_for_html_elem(html)
- )
- );
+ return html::text::escape_for_html_elem(html)
+ .wrap_if_not_empty<html::tag::br>()
+ .wrap_if_not_empty<html::tag::br>()
+ ;
}
/// Transform 'html' -> '<br><br><b>html</b>', but return empty string
unchanged.
-wxString brbrb(std::string const& html)
+html::text brbrb(std::string const& html)
{
- return
- wrap_if_not_empty<html::tag::br>
- (wrap_if_not_empty<html::tag::br>
- (wrap_if_not_empty<html::tag::b>
- (escape_for_html_elem(html)
- )
- )
- );
+ 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>()
+ ;
}
/// Generate HTML representation of a field name and value in an HTML table.
@@ -182,9 +94,9 @@ wxString name_value_as_html_table_data
return wxString::Format
("<td nowrap align=\"right\"><b>%s%s </b></td>"
"<td>%s </td>"
- ,escape_for_html_elem(name)
+ ,html::text::escape_for_html_elem(name).as_string()
,(value.empty() ? "" : ":")
- ,escape_for_html_elem(value)
+ ,html::text::escape_for_html_elem(value).as_string()
);
}
@@ -407,7 +319,7 @@ class group_quote_pdf_generator_wx
std::string premium_mode_;
std::string contract_state_;
std::string effective_date_;
- wxString footer_html_;
+ html::text footer_html_;
// Dynamically-determined fields.
std::string elected_riders_;
@@ -995,9 +907,9 @@ void group_quote_pdf_generator_wx::output_document_header
"<td align=\"center\"><i>Prepared By: %s</i></td>"
"</tr>"
"</table>"
- ,escape_for_html_elem(report_data_.company_)
+ ,html::text::escape_for_html_elem(report_data_.company_).as_string()
,wxDateTime::Today().FormatDate()
- ,escape_for_html_elem(report_data_.prepared_by_)
+
,html::text::escape_for_html_elem(report_data_.prepared_by_).as_string()
);
pdf_writer.output_html
@@ -1235,13 +1147,13 @@ void group_quote_pdf_generator_wx::output_footer
*pos_y += vert_skip;
}
- wxString const footer_html = "<p>" + report_data_.footer_html_ + "</p>";
+ auto footer_html = report_data_.footer_html_.wrap<html::tag::p>();
*pos_y += pdf_writer.output_html
(pdf_writer.get_horz_margin()
,*pos_y
,pdf_writer.get_page_width()
- ,footer_html
+ ,std::move(footer_html).as_string()
,output_mode
);
}
diff --git a/html_text.hpp b/html_text.hpp
new file mode 100644
index 0000000..3f6c3c4
--- /dev/null
+++ b/html_text.hpp
@@ -0,0 +1,199 @@
+// 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
- [lmi-commits] [lmi] master fc6d8b7 034/156: Implement the "Columns Headings" page, (continued)
- [lmi-commits] [lmi] master fc6d8b7 034/156: Implement the "Columns Headings" page, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 46788b8 119/156: Start implementing individual private placement illustration kind, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 1287474 114/156: Start private group placement illustration class implementation, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 0f78daf 050/156: Compute the footer size in page_with_footer::pre_render(), Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 6b3b5b2 054/156: Factor out illustration_table_generator from tabular_detail2_page, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 23d26f8 055/156: Factor out reusable page_with_tabular_report class, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master f484622 044/156: Only show numeric summary if not in force, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 67db94b 042/156: Implement more of numeric summary page, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master dbaf3c4 048/156: Render PDF illustrations in two passes, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master de9567e 152/156: Fix misplaced commas in ledger PDF generator code, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 783befa 006/156: Add separate class for representing HTML contents,
Greg Chicares <=
- [lmi-commits] [lmi] master 8c5be11 128/156: Use proper types for get_two_column_header() parameters, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 257ff4b 065/156: Add numeric summary table to PDF illustrations, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master ef38c9d 141/156: Revert the title map related changes, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master b1ab0fa 126/156: Leave a gap between upper footer part and main page contents, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 940198c 059/156: Add numeric summary attachment page, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master b341a2a 061/156: Factor out using_illustration_table helper class, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 1b46585 076/156: Use MarketingNameFootnote & StateMarketingImprimatur on cover page, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 234cd39 012/156: Use custom font sizes to be closer to the existing illustrations, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 956fcca 071/156: Change the second narrative summary page to use a template too, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master dbe5955 015/156: Factor out html_interpolator from pdf_illustration, Greg Chicares, 2018/01/30