lmi-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[lmi-commits] [lmi] gwc-no-xslfo 28d5b17 04/12: Import 'group_quote_pdf_


From: Greg Chicares
Subject: [lmi-commits] [lmi] gwc-no-xslfo 28d5b17 04/12: Import 'group_quote_pdf_gen_wx.cpp' changes verbatim from vz-no-xslfo
Date: Sat, 27 Jan 2018 04:23:33 -0500 (EST)

branch: gwc-no-xslfo
commit 28d5b1748a7ab848510aa3b488116c5db5ec5298
Author: Gregory W. Chicares <address@hidden>
Commit: Gregory W. Chicares <address@hidden>

    Import 'group_quote_pdf_gen_wx.cpp' changes verbatim from vz-no-xslfo
---
 group_quote_pdf_gen_wx.cpp | 556 +++++++++++++++------------------------------
 1 file changed, 185 insertions(+), 371 deletions(-)

diff --git a/group_quote_pdf_gen_wx.cpp b/group_quote_pdf_gen_wx.cpp
index 4043a99..42b659c 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.hpp"
 #include "ledger.hpp"
 #include "ledger_invariant.hpp"
 #include "ledger_text_formats.hpp"      // ledger_format()
@@ -36,6 +37,7 @@
 #include "miscellany.hpp"               // split_into_lines()
 #include "oecumenic_enumerations.hpp"   // oenum_format_style
 #include "path_utility.hpp"             // fs::path inserter
+#include "pdf_writer_wx.hpp"
 #include "version.hpp"
 #include "wx_table_generator.hpp"
 #include "wx_utility.hpp"               // ConvertDateToWx()
@@ -44,10 +46,7 @@
 #include <boost/filesystem/path.hpp>
 
 #include <wx/datetime.h>
-#include <wx/html/htmlcell.h>
-#include <wx/html/winpars.h>
 #include <wx/image.h>
-#include <wx/pdfdc.h>
 
 #include <cstring>                      // strstr()
 #include <limits>
@@ -61,119 +60,28 @@ LMI_FORCE_LINKING_IN_SITU(group_quote_pdf_generator_wx)
 namespace
 {
 
-enum enum_output_mode
-    {e_output_normal
-    ,e_output_measure_only
-    };
-
-/// 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 += "&lt;" ; break;
-            case '>': z += "&gt;" ; break;
-            case '&': z += "&amp;"; 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.
+/// Transform 's' -> '<br><br>s', but return empty string unchanged.
 
-namespace tag
+html::text brbr(std::string const& s)
 {
+    using namespace html;
 
-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;
+    return s.empty()
+        ? text()
+        : tag::br + tag::br + text::from(s)
+        ;
 }
 
-/// Transform 'html' -> '<br><br>html', but return empty string unchanged.
+/// Transform 's' -> '<br><br><b>s</b>', but return empty string unchanged.
 
-wxString brbr(std::string const& html)
+html::text brbrb(std::string const& s)
 {
-    return
-        wrap_if_not_empty<html::tag::br>
-            (wrap_if_not_empty<html::tag::br>
-                (escape_for_html_elem(html)
-                )
-            );
-}
-
-/// Transform 'html' -> '<br><br><b>html</b>', but return empty string 
unchanged.
+    using namespace html;
 
-wxString 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 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.
@@ -181,18 +89,27 @@ wxString 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&nbsp;&nbsp;</b></td>"
-         "<td>%s&nbsp;&nbsp;&nbsp;&nbsp;</td>"
-        ,escape_for_html_elem(name)
-        ,(value.empty() ? "" : ":")
-        ,escape_for_html_elem(value)
-        );
+    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
@@ -301,100 +218,6 @@ wxImage load_image(char const* file)
     return image;
 }
 
-/// Output an image at the given scale into the PDF.
-///
-/// The scale specifies how many times the image should be shrunk:
-/// scale > 1 makes the image smaller, while scale < 1 makes it larger.
-///
-/// Updates pos_y by increasing it by the height of the specified
-/// image at the given scale.
-
-void output_image
-    (wxPdfDC&         pdf_dc
-    ,wxImage const&   image
-    ,char const*      image_name
-    ,double           scale
-    ,int              x
-    ,int*             pos_y
-    ,enum_output_mode output_mode = e_output_normal
-    )
-{
-    int const y = wxRound(image.GetHeight() / scale);
-
-    switch(output_mode)
-        {
-        case e_output_normal:
-            {
-            // Use wxPdfDocument API directly as wxDC doesn't provide a way to
-            // set the image scale at PDF level and also because passing via
-            // wxDC wastefully converts wxImage to wxBitmap only to convert it
-            // back to wxImage when embedding it into the PDF.
-            wxPdfDocument* const pdf_doc = pdf_dc.GetPdfDocument();
-            LMI_ASSERT(pdf_doc);
-
-            pdf_doc->SetImageScale(scale);
-            pdf_doc->Image(image_name, image, x, *pos_y);
-            pdf_doc->SetImageScale(1);
-            }
-            break;
-        case e_output_measure_only:
-            // Do nothing.
-            break;
-        default:
-            {
-            alarum() << "Case " << output_mode << " not found." << LMI_FLUSH;
-            }
-        }
-
-    *pos_y += y;
-}
-
-/// Render, or just pretend rendering in order to measure it, the given HTML
-/// contents at the specified position wrapping it at the given width.
-/// Return the height of the output (using this width).
-
-int output_html
-    (wxHtmlWinParser& html_parser
-    ,int x
-    ,int y
-    ,int width
-    ,wxString const& html
-    ,enum_output_mode output_mode = e_output_normal
-    )
-{
-    std::unique_ptr<wxHtmlContainerCell> const cell
-        (static_cast<wxHtmlContainerCell*>(html_parser.Parse(html))
-        );
-    LMI_ASSERT(cell);
-
-    cell->Layout(width);
-    switch(output_mode)
-        {
-        case e_output_normal:
-            {
-            wxHtmlRenderingInfo rendering_info;
-            cell->Draw
-                (*html_parser.GetDC()
-                ,x
-                ,y
-                ,0
-                ,std::numeric_limits<int>::max()
-                ,rendering_info
-                );
-            }
-            break;
-        case e_output_measure_only:
-            // Do nothing.
-            break;
-        default:
-            {
-            alarum() << "Case " << output_mode << " not found." << LMI_FLUSH;
-            }
-        }
-
-    return cell->GetHeight();
-}
-
 enum enum_group_quote_columns
     {e_col_number
     ,e_col_name
@@ -450,52 +273,46 @@ class group_quote_pdf_generator_wx
     void save(std::string const& output_filename) override;
 
   private:
-    // These margins are arbitrary and can be changed to conform to subjective
+    // This value is arbitrary and can be changed to conform to subjective
     // preferences.
-    static int const horz_margin = 24;
-    static int const vert_margin = 36;
-    static int const vert_skip   = 12;
+    static int const vert_skip = 12;
 
     // Ctor is private as it is only used by do_create().
     group_quote_pdf_generator_wx() = default;
 
-    // Generate the PDF once we have all the data.
-    void do_generate_pdf(wxPdfDC& pdf_dc);
-
     // Compute the number of pages needed by the table rows in the output given
     // the space remaining on the first page, the heights of the header, one
     // table row and the footer and the last row position.
     // Remaining space contains the space on the first page on input and is
     // updated with the space remaining on the last page on output.
     int compute_pages_for_table_rows
-        (int* remaining_space
+        (pdf_writer_wx& pdf_writer
+        ,int* remaining_space
         ,int  header_height
         ,int  row_height
         ,int  last_row_y
         );
 
     void output_page_number_and_version
-        (wxPdfDC& pdf_dc
+        (pdf_writer_wx& pdf_writer
         ,int      total_pages
         ,int      current_page
         );
     void output_image_header
-        (wxPdfDC& pdf_dc
+        (pdf_writer_wx& pdf_writer
         ,int*     pos_y
         );
     void output_document_header
-        (wxPdfDC&         pdf_dc
-        ,wxHtmlWinParser& html_parser
+        (pdf_writer_wx&   pdf_writer
         ,int*             pos_y
         );
     void output_aggregate_values
-        (wxPdfDC&            pdf_dc
+        (pdf_writer_wx&      pdf_writer
         ,wx_table_generator& table_gen
         ,int*                pos_y
         );
     void output_footer
-        (wxPdfDC&         pdf_dc
-        ,wxHtmlWinParser& html_parser
+        (pdf_writer_wx&   pdf_writer
         ,int*             pos_y
         ,enum_output_mode output_mode = e_output_normal
         );
@@ -514,7 +331,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_;
@@ -561,24 +378,6 @@ class group_quote_pdf_generator_wx
     };
     totals_data totals_;
 
-    struct page_metrics
-        {
-        page_metrics()
-            :width_(0)
-            {
-            }
-
-        void initialize(wxDC const& dc)
-            {
-            total_size_ = dc.GetSize();
-            width_ = total_size_.x - 2 * horz_margin;
-            }
-
-        wxSize total_size_;
-        int width_;
-        };
-    page_metrics page_;
-
     int row_num_              {0};
     int individual_selection_ {99};
 };
@@ -858,59 +657,20 @@ void group_quote_pdf_generator_wx::add_ledger(Ledger 
const& ledger)
 
 void group_quote_pdf_generator_wx::save(std::string const& output_filename)
 {
-    // Create a wxPrintData object just to describe the paper to use.
-    wxPrintData print_data;
-    print_data.SetOrientation(wxLANDSCAPE);
-    print_data.SetPaperId(wxPAPER_LETTER);
-    print_data.SetFilename(output_filename);
-
-    wxPdfDC pdf_dc(print_data);
-    page_.initialize(pdf_dc);
-    do_generate_pdf(pdf_dc);
-    pdf_dc.EndDoc();
-}
-
-void group_quote_pdf_generator_wx::do_generate_pdf(wxPdfDC& pdf_dc)
-{
-    // Ensure that the output is independent of the current display resolution:
-    // it seems that this is only the case with the PDF map mode and wxDC mode
-    // different from wxMM_TEXT.
-    pdf_dc.SetMapModeStyle(wxPDF_MAPMODESTYLE_PDF);
-
-    // For simplicity, use points for everything: font sizers are expressed in
-    // them anyhow, so it's convenient to use them for everything else too.
-    pdf_dc.SetMapMode(wxMM_POINTS);
-
-    pdf_dc.StartDoc(wxString()); // Argument is not used.
-    pdf_dc.StartPage();
-
-    // Use a standard PDF Helvetica font (without embedding any custom fonts in
-    // the generated file, the only other realistic choice is Times New Roman).
-    pdf_dc.SetFont
-        (wxFontInfo(8).Family(wxFONTFAMILY_SWISS).FaceName("Helvetica")
-        );
-
-    // Create an HTML parser to allow easily adding HTML contents to the 
output.
-    wxHtmlWinParser html_parser(nullptr);
-    html_parser.SetDC(&pdf_dc);
-    html_parser.SetStandardFonts
-        (pdf_dc.GetFont().GetPointSize()
-        ,"Helvetica"
-        ,"Courier"
-        );
+    pdf_writer_wx pdf_writer(output_filename, wxLANDSCAPE);
 
     int pos_y = 0;
 
-    output_image_header(pdf_dc, &pos_y);
+    output_image_header(pdf_writer, &pos_y);
     pos_y += 2 * vert_skip;
 
-    output_document_header(pdf_dc, html_parser, &pos_y);
+    output_document_header(pdf_writer, &pos_y);
     pos_y += 2 * vert_skip;
 
     wx_table_generator table_gen
-        (pdf_dc
-        ,horz_margin
-        ,page_.width_
+        (pdf_writer.dc()
+        ,pdf_writer.get_horz_margin()
+        ,pdf_writer.get_page_width()
         );
 
     // Some of the table columns don't need to be shown if all the values in
@@ -976,21 +736,22 @@ void 
group_quote_pdf_generator_wx::do_generate_pdf(wxPdfDC& pdf_dc)
         table_gen.add_column(header, cd.widest_text_);
         }
 
-    output_aggregate_values(pdf_dc, table_gen, &pos_y);
+    output_aggregate_values(pdf_writer, table_gen, &pos_y);
 
     int const y_before_header = pos_y;
     table_gen.output_header(&pos_y);
     int const header_height = pos_y - y_before_header;
 
     int y_after_footer = pos_y;
-    output_footer(pdf_dc, html_parser, &y_after_footer, e_output_measure_only);
+    output_footer(pdf_writer, &y_after_footer, e_output_measure_only);
     int const footer_height = y_after_footer - pos_y;
 
-    int const last_row_y = page_.total_size_.y - vert_margin;
+    int const last_row_y = pdf_writer.get_page_bottom();
     int remaining_space = last_row_y - pos_y;
 
     int total_pages = compute_pages_for_table_rows
-        (&remaining_space
+        (pdf_writer
+        ,&remaining_space
         ,header_height
         ,table_gen.row_height()
         ,last_row_y
@@ -1013,38 +774,39 @@ void 
group_quote_pdf_generator_wx::do_generate_pdf(wxPdfDC& pdf_dc)
 
         if(last_row_y <= pos_y)
             {
-            output_page_number_and_version(pdf_dc, total_pages, current_page);
+            output_page_number_and_version(pdf_writer, total_pages, 
current_page);
 
             current_page++;
-            pdf_dc.StartPage();
+            pdf_writer.dc().StartPage();
 
-            pos_y = vert_margin;
+            pos_y = pdf_writer.get_vert_margin();
             table_gen.output_header(&pos_y);
             }
         }
 
     if(footer_on_its_own_page)
         {
-        output_page_number_and_version(pdf_dc, total_pages, current_page);
+        output_page_number_and_version(pdf_writer, total_pages, current_page);
 
         current_page++;
-        pdf_dc.StartPage();
+        pdf_writer.dc().StartPage();
 
-        pos_y = vert_margin;
+        pos_y = pdf_writer.get_vert_margin();
         }
     else
         {
         pos_y += 2 * vert_skip;
         }
 
-    output_footer(pdf_dc, html_parser, &pos_y);
+    output_footer(pdf_writer, &pos_y);
 
     LMI_ASSERT(current_page == total_pages);
-    output_page_number_and_version(pdf_dc, total_pages, current_page);
+    output_page_number_and_version(pdf_writer, total_pages, current_page);
 }
 
 int group_quote_pdf_generator_wx::compute_pages_for_table_rows
-    (int* remaining_space
+    (pdf_writer_wx& pdf_writer
+    ,int* remaining_space
     ,int header_height
     ,int row_height
     ,int last_row_y
@@ -1060,7 +822,8 @@ int 
group_quote_pdf_generator_wx::compute_pages_for_table_rows
         // rest of them.
         remaining_rows -= max_rows_on_first_page;
 
-        int const page_area_y = last_row_y - vert_margin - header_height;
+        int const first_row_y = pdf_writer.get_vert_margin() + header_height;
+        int const page_area_y = last_row_y - first_row_y;
         int const rows_per_page = page_area_y / row_height;
         total_pages += (remaining_rows + rows_per_page - 1) / rows_per_page;
         *remaining_space = page_area_y;
@@ -1073,18 +836,20 @@ int 
group_quote_pdf_generator_wx::compute_pages_for_table_rows
 }
 
 void group_quote_pdf_generator_wx::output_page_number_and_version
-    (wxPdfDC& pdf_dc
+    (pdf_writer_wx& pdf_writer
     ,int total_pages
     ,int current_page
     )
 {
     wxRect const footer_area
-        (horz_margin
-        ,page_.total_size_.y - vert_margin
-        ,page_.width_
-        ,vert_margin
+        (pdf_writer.get_horz_margin()
+        ,pdf_writer.get_page_bottom()
+        ,pdf_writer.get_page_width()
+        ,pdf_writer.get_vert_margin()
         );
 
+    auto& pdf_dc = pdf_writer.dc();
+
     pdf_dc.DrawLabel
         (wxString::Format("System version: %s", LMI_VERSION)
         ,footer_area
@@ -1099,7 +864,7 @@ void 
group_quote_pdf_generator_wx::output_page_number_and_version
 }
 
 void group_quote_pdf_generator_wx::output_image_header
-    (wxPdfDC& pdf_dc
+    (pdf_writer_wx& pdf_writer
     ,int* pos_y
     )
 {
@@ -1110,15 +875,17 @@ void group_quote_pdf_generator_wx::output_image_header
         }
 
     // Set the scale to fit the image to the document width.
-    double const
-        scale = static_cast<double>(banner_image.GetWidth()) / 
page_.total_size_.x;
+    double const image_width = banner_image.GetWidth();
+    double const scale = image_width / pdf_writer.get_total_width();
     int const pos_top = *pos_y;
-    output_image(pdf_dc, banner_image, "banner", scale, 0, pos_y);
+    pdf_writer.output_image(banner_image, "banner", scale, 0, pos_y);
+
+    auto& pdf_dc = pdf_writer.dc();
 
     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())
@@ -1128,7 +895,7 @@ void group_quote_pdf_generator_wx::output_image_header
     pdf_dc.DrawLabel
         (image_text
         ,wxRect
-            (wxPoint(horz_margin, (pos_top + *pos_y) / 2),
+            (wxPoint(pdf_writer.get_horz_margin(), (pos_top + *pos_y) / 2),
              pdf_dc.GetMultiLineTextExtent(image_text)
             )
         ,wxALIGN_CENTER_HORIZONTAL
@@ -1136,45 +903,53 @@ void group_quote_pdf_generator_wx::output_image_header
 }
 
 void group_quote_pdf_generator_wx::output_document_header
-    (wxPdfDC& pdf_dc
-    ,wxHtmlWinParser& html_parser
+    (pdf_writer_wx& pdf_writer
     ,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>"
-        ,escape_for_html_elem(report_data_.company_)
-        ,wxDateTime::Today().FormatDate()
-        ,escape_for_html_elem(report_data_.prepared_by_)
+    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
         );
 
-    output_html(html_parser, horz_margin, *pos_y, page_.width_ / 2, 
title_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\">&nbsp;</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.
@@ -1196,34 +971,66 @@ 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 = output_html
-        (html_parser
-        ,horz_margin + page_.width_ / 2
+    int const summary_height = pdf_writer.output_html
+        (pdf_writer.get_horz_margin() + pdf_writer.get_page_width() / 2
         ,*pos_y
-        ,page_.width_ / 2
+        ,pdf_writer.get_page_width() / 2
         ,summary_html
         );
 
     // wxHTML tables don't support "frame" attribute, so draw the border around
     // the table manually.
+    auto& pdf_dc = pdf_writer.dc();
     pdf_dc.SetBrush(*wxTRANSPARENT_BRUSH);
     pdf_dc.DrawRectangle
-        (horz_margin + page_.width_ / 2
+        (pdf_writer.get_horz_margin() + pdf_writer.get_page_width() / 2
         ,*pos_y
-        ,page_.width_ / 2
+        ,pdf_writer.get_page_width() / 2
         ,summary_height
         );
 
@@ -1231,7 +1038,7 @@ void group_quote_pdf_generator_wx::output_document_header
 }
 
 void group_quote_pdf_generator_wx::output_aggregate_values
-    (wxPdfDC& pdf_dc
+    (pdf_writer_wx& pdf_writer
     ,wx_table_generator& table_gen
     ,int* pos_y
     )
@@ -1248,6 +1055,8 @@ void group_quote_pdf_generator_wx::output_aggregate_values
     table_gen.output_vert_separator(e_col_number, y);
     table_gen.output_vert_separator(e_col_number, y_next);
 
+    auto& pdf_dc = pdf_writer.dc();
+
     // Render "Census" in bold.
     wxDCFontChanger set_bold_font(pdf_dc, pdf_dc.GetFont().Bold());
     pdf_dc.DrawLabel
@@ -1364,8 +1173,7 @@ void group_quote_pdf_generator_wx::output_aggregate_values
 }
 
 void group_quote_pdf_generator_wx::output_footer
-    (wxPdfDC& pdf_dc
-    ,wxHtmlWinParser& html_parser
+    (pdf_writer_wx& pdf_writer
     ,int* pos_y
     ,enum_output_mode output_mode
     )
@@ -1375,18 +1183,24 @@ void group_quote_pdf_generator_wx::output_footer
         {
         // Arbitrarily scale down the logo by a factor of 2 to avoid making it
         // too big.
-        output_image(pdf_dc, logo_image, "company_logo", 2.0, horz_margin, 
pos_y, output_mode);
+        pdf_writer.output_image
+            (logo_image
+            ,"company_logo"
+            ,2.0
+            ,pdf_writer.get_horz_margin()
+            ,pos_y
+            ,output_mode
+            );
 
         *pos_y += vert_skip;
         }
 
-    wxString const footer_html = "<p>" + report_data_.footer_html_ + "</p>";
+    auto footer_html = html::tag::p(report_data_.footer_html_);
 
-    *pos_y += output_html
-        (html_parser
-        ,horz_margin
+    *pos_y += pdf_writer.output_html
+        (pdf_writer.get_horz_margin()
         ,*pos_y
-        ,page_.width_
+        ,pdf_writer.get_page_width()
         ,footer_html
         ,output_mode
         );



reply via email to

[Prev in Thread] Current Thread [Next in Thread]