differential code coverage report with master
Current view: top level - source/detail/serialization - xlsx_consumer.cpp (source / functions) Coverage Total Hit UBC CBC
Current: coverage.info Lines: 84.5 % 1858 1570 288 1570
Current Date: 2025-12-15 23:01:28 Functions: 84.1 % 88 74 14 74
Baseline: coverage_master.info Branches: 75.7 % 4630 3505 2250 7010
Baseline Date: 2025-12-15 23:01:27

             Branch data    TLA  Line data    Source code
       1                 :                : // Copyright (c) 2014-2022 Thomas Fussell
       2                 :                : // Copyright (c) 2024-2025 xlnt-community
       3                 :                : //
       4                 :                : // Permission is hereby granted, free of charge, to any person obtaining a copy
       5                 :                : // of this software and associated documentation files (the "Software"), to deal
       6                 :                : // in the Software without restriction, including without limitation the rights
       7                 :                : // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8                 :                : // copies of the Software, and to permit persons to whom the Software is
       9                 :                : // furnished to do so, subject to the following conditions:
      10                 :                : //
      11                 :                : // The above copyright notice and this permission notice shall be included in
      12                 :                : // all copies or substantial portions of the Software.
      13                 :                : //
      14                 :                : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15                 :                : // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16                 :                : // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17                 :                : // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18                 :                : // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19                 :                : // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      20                 :                : // THE SOFTWARE
      21                 :                : //
      22                 :                : // @license: http://www.opensource.org/licenses/mit-license.php
      23                 :                : // @author: see AUTHORS file
      24                 :                : 
      25                 :                : #include <cassert>
      26                 :                : #include <cctype>
      27                 :                : #include <sstream>
      28                 :                : #include <unordered_map>
      29                 :                : 
      30                 :                : #include <xlnt/cell/cell.hpp>
      31                 :                : #include <xlnt/cell/comment.hpp>
      32                 :                : #include <xlnt/cell/hyperlink.hpp>
      33                 :                : #include <xlnt/drawing/spreadsheet_drawing.hpp>
      34                 :                : #include <xlnt/packaging/manifest.hpp>
      35                 :                : #include <xlnt/utils/optional.hpp>
      36                 :                : #include <xlnt/utils/path.hpp>
      37                 :                : #include <xlnt/workbook/workbook.hpp>
      38                 :                : #include <xlnt/worksheet/selection.hpp>
      39                 :                : #include <xlnt/worksheet/worksheet.hpp>
      40                 :                : #include <detail/constants.hpp>
      41                 :                : #include <detail/header_footer/header_footer_code.hpp>
      42                 :                : #include <detail/implementations/workbook_impl.hpp>
      43                 :                : #include <detail/serialization/custom_value_traits.hpp>
      44                 :                : #include <detail/serialization/defined_name.hpp>
      45                 :                : #include <detail/serialization/serialisation_helpers.hpp>
      46                 :                : #include <detail/serialization/vector_streambuf.hpp>
      47                 :                : #include <detail/serialization/xlsx_consumer.hpp>
      48                 :                : #include <detail/serialization/zstream.hpp>
      49                 :                : #include <detail/limits.hpp>
      50                 :                : #include <detail/serialization/parsers.hpp>
      51                 :                : 
      52                 :                : namespace {
      53                 :                : /// string_equal
      54                 :                : /// for comparison between std::string and string literals
      55                 :                : /// improves on std::string::operator==(char*) by knowing the length ahead of time
      56                 :                : template <size_t N>
      57                 :CBC       24423 : inline bool string_arr_loop_equal(const std::string &lhs, const char (&rhs)[N])
      58                 :                : {
      59         [ +  + ]:          81348 :     for (size_t i = 0; i < N - 1; ++i)
      60                 :                :     {
      61         [ +  + ]:          66367 :         if (lhs[i] != rhs[i])
      62                 :                :         {
      63                 :           9442 :             return false;
      64                 :                :         }
      65                 :                :     }
      66                 :          14981 :     return true;
      67                 :                : }
      68                 :                : 
      69                 :                : template <size_t N>
      70                 :          62096 : inline bool string_equal(const std::string &lhs, const char (&rhs)[N])
      71                 :                : {
      72         [ +  + ]:          62096 :     if (lhs.size() != N - 1)
      73                 :                :     {
      74                 :          37673 :         return false;
      75                 :                :     }
      76                 :                :     // split function to assist with inlining of the size check
      77                 :          24423 :     return string_arr_loop_equal(lhs, rhs);
      78                 :                : }
      79                 :                : 
      80                 :          84338 : xml::qname &qn(const std::string &namespace_, const std::string &name)
      81                 :                : {
      82                 :                :     using qname_map = std::unordered_map<std::string, xml::qname>;
      83   [ +  +  +  - ]:          84338 :     static auto memo = std::unordered_map<std::string, qname_map>();
      84                 :                : 
      85                 :          84338 :     auto &ns_memo = memo[namespace_];
      86                 :                : 
      87      [ +  +  + ]:          84338 :     if (ns_memo.find(name) == ns_memo.end())
      88                 :                :     {
      89      [ +  +  + ]:            146 :         return ns_memo.emplace(name, xml::qname(xlnt::constants::ns(namespace_), name)).first->second;
      90                 :                :     }
      91                 :                : 
      92                 :          84192 :     return ns_memo[name];
      93                 :                : }
      94                 :                : 
      95                 :                : /// <summary>
      96                 :                : /// Returns true if bool_string represents a true xsd:boolean.
      97                 :                : /// </summary>
      98                 :           6951 : bool is_true(const std::string &bool_string)
      99                 :                : {
     100   [ +  +  +  +  :           6951 :     if (bool_string == "1" || bool_string == "true")
                   +  + ]
     101                 :                :     {
     102                 :           3009 :         return true;
     103                 :                :     }
     104                 :                : 
     105                 :                : #ifdef THROW_ON_INVALID_XML
     106                 :                :     if (bool_string == "0" || bool_string == "false")
     107                 :                :     {
     108                 :                :         return false;
     109                 :                :     }
     110                 :                : 
     111                 :                :     throw xlnt::exception("xsd:boolean should be one of: 0, 1, true, or false, found " + bool_string);
     112                 :                : #else
     113                 :                : 
     114                 :           3942 :     return false;
     115                 :                : #endif
     116                 :                : }
     117                 :                : 
     118                 :              4 : bool is_valid_reference (const std::string &reference_string)
     119                 :                : {
     120                 :              4 :     return reference_string != "#REF!";
     121                 :                : }
     122                 :                : 
     123                 :                : using style_id_pair = std::pair<xlnt::detail::style_impl, std::size_t>;
     124                 :                : 
     125                 :                : /// <summary>
     126                 :                : /// Try to find given xfid value in the styles vector and, if succeeded, set's the optional style.
     127                 :                : /// </summary>
     128                 :            439 : void set_style_by_xfid(const std::vector<style_id_pair> &styles,
     129                 :                :     std::size_t xfid, xlnt::optional<std::string> &style)
     130                 :                : {
     131         [ +  + ]:           3128 :     for (auto &item : styles)
     132                 :                :     {
     133         [ +  + ]:           2689 :         if (item.second == xfid)
     134                 :                :         {
     135            [ + ]:            415 :             style = item.first.name;
     136                 :                :         }
     137                 :                :     }
     138                 :            439 : }
     139                 :                : 
     140                 :                : // <sheetData> element
     141                 :                : struct Sheet_Data
     142                 :                : {
     143                 :                :     std::vector<std::pair<xlnt::row_properties, xlnt::row_t>> parsed_rows;
     144                 :                :     std::vector<xlnt::detail::Cell> parsed_cells;
     145                 :                : };
     146                 :                : 
     147                 :           1466 : xlnt::cell_type type_from_string(const std::string &str)
     148                 :                : {
     149         [ +  + ]:           1466 :     if (string_equal(str, "s"))
     150                 :                :     {
     151                 :           1447 :         return xlnt::cell::type::shared_string;
     152                 :                :     }
     153         [ +  + ]:             19 :     else if (string_equal(str, "n"))
     154                 :                :     {
     155                 :              2 :         return xlnt::cell::type::number;
     156                 :                :     }
     157         [ -  + ]:             17 :     else if (string_equal(str, "b"))
     158                 :                :     {
     159                 :UBC           0 :         return xlnt::cell::type::boolean;
     160                 :                :     }
     161         [ -  + ]:CBC          17 :     else if (string_equal(str, "e"))
     162                 :                :     {
     163                 :UBC           0 :         return xlnt::cell::type::error;
     164                 :                :     }
     165         [ +  + ]:CBC          17 :     else if (string_equal(str, "inlineStr"))
     166                 :                :     {
     167                 :              2 :         return xlnt::cell::type::inline_string;
     168                 :                :     }
     169         [ +  - ]:             15 :     else if (string_equal(str, "str"))
     170                 :                :     {
     171                 :             15 :         return xlnt::cell::type::formula_string;
     172                 :                :     }
     173                 :UBC           0 :     return xlnt::cell::type::shared_string;
     174                 :                : }
     175                 :                : 
     176                 :CBC        2035 : xlnt::detail::Cell parse_cell(xlnt::row_t row_arg, xml::parser *parser, std::unordered_map<std::string, std::string> &array_formulae, std::unordered_map<int, std::string> &shared_formulae)
     177                 :                : {
     178                 :           2035 :     xlnt::detail::Cell c;
     179      [ +  +  + ]:           6392 :     for (auto &attr : parser->attribute_map())
     180                 :                :     {
     181      [ +  +  + ]:           4357 :         if (string_equal(attr.first.name(), "r"))
     182                 :                :         {
     183                 :           2035 :             c.ref = xlnt::detail::Cell_Reference(row_arg, attr.second.value);
     184                 :                :         }
     185      [ +  +  + ]:           2322 :         else if (string_equal(attr.first.name(), "t"))
     186                 :                :         {
     187            [ + ]:           1466 :             c.type = type_from_string(attr.second.value);
     188                 :                :         }
     189      [ +  +  + ]:            856 :         else if (string_equal(attr.first.name(), "s"))
     190                 :                :         {
     191            [ + ]:            852 :             xlnt::detail::parse(attr.second.value, c.style_index);
     192                 :                :         }
     193      [ +  +  - ]:              4 :         else if (string_equal(attr.first.name(), "ph"))
     194                 :                :         {
     195            [ + ]:              4 :             c.is_phonetic = is_true(attr.second.value);
     196                 :                :         }
     197      [ #  #  # ]:UBC           0 :         else if (string_equal(attr.first.name(), "cm"))
     198                 :                :         {
     199            [ # ]:              0 :             xlnt::detail::parse(attr.second.value, c.cell_metadata_idx);
     200                 :                :         }
     201                 :                :     }
     202                 :CBC        2035 :     int level = 1; // nesting level
     203                 :                :         // 1 == <c>
     204                 :                :         // 2 == <v>/<f>
     205                 :                :         // 3 == <is><t>
     206                 :                :         // exit loop at </c>
     207         [ +  + ]:           9260 :     while (level > 0)
     208                 :                :     {
     209            [ + ]:           7225 :         xml::parser::event_type e = parser->next();
     210   [ +  +  +  - ]:           7225 :         switch (e)
     211                 :                :         {
     212                 :           1584 :         case xml::parser::start_element: {
     213   [ +  +  +  +  :           1682 :             if (string_equal(parser->name(), "f") && parser->attribute_present("t"))
          +  +  +  +  +  
          +  +  +  +  -  
                -  -  - ]
     214                 :                :             {
     215                 :                :                 // Skip shared formulas with a ref attribute because it indicates that this
     216                 :                :                 // is the master cell which will be handled in the xml::parser::characters case.
     217   [ +  +  +  +  :             45 :                 if (parser->attribute("t") == "shared" && !parser->attribute_present("ref"))
          +  +  +  +  +  
          +  +  +  +  +  
          -  +  -  +  +  
          -  -  -  -  -  
                -  -  - ]
     218                 :                :                 {
     219         [ +  + ]:              8 :                     auto shared_index = parser->attribute<int>("si");
     220         [ +  + ]:              4 :                     c.formula_string = shared_formulae[shared_index];
     221                 :                :                 }
     222                 :                :             }
     223                 :           1584 :             ++level;
     224                 :           1584 :             break;
     225                 :                :         }
     226                 :           3619 :         case xml::parser::end_element: {
     227                 :           3619 :             --level;
     228                 :           3619 :             break;
     229                 :                :         }
     230                 :           2022 :         case xml::parser::characters: {
     231                 :                :             // only want the characters inside one of the nested tags
     232                 :                :             // without this a lot of formatting whitespace can get added
     233         [ +  + ]:           2022 :             if (level == 2)
     234                 :                :             {
     235                 :                :                 // <v> -> numeric values
     236      [ +  +  + ]:           1576 :                 if (string_equal(parser->name(), "v"))
     237                 :                :                 {
     238            [ + ]:           1531 :                     c.value += std::move(parser->value());
     239                 :                :                 }
     240                 :                :                 // <f> formula
     241      [ +  +  - ]:             45 :                 else if (string_equal(parser->name(), "f"))
     242                 :                :                 {
     243            [ + ]:             45 :                     c.formula_string += std::move(parser->value());
     244                 :                : 
     245   [ +  +  +  + ]:            135 :                     if (parser->attribute_present("t"))
     246                 :                :                     {
     247      [ +  +  + ]:             14 :                         auto formula_ref = parser->attribute("ref");
     248      [ +  +  + ]:             14 :                         auto formula_type = parser->attribute("t");
     249      [ +  +  + ]:              7 :                         if (formula_type == "shared")
     250                 :                :                         {
     251         [ +  + ]:              4 :                             auto shared_index = parser->attribute<int>("si");
     252         [ +  + ]:              2 :                             shared_formulae[shared_index] = c.formula_string;
     253                 :                :                         }
     254      [ +  +  - ]:              5 :                         else if (formula_type == "array")
     255                 :                :                         {
     256         [ +  + ]:              5 :                             array_formulae[formula_ref] = c.formula_string;
     257                 :                :                         }
     258                 :              7 :                     }
     259                 :                :                 }
     260                 :                :             }
     261         [ +  + ]:            446 :             else if (level == 3)
     262                 :                :             {
     263                 :                :                 // <is><t> -> inline string
     264      [ +  +  - ]:              2 :                 if (string_equal(parser->name(), "t"))
     265                 :                :                 {
     266            [ + ]:              2 :                     c.value += std::move(parser->value());
     267                 :                :                 }
     268                 :                :             }
     269                 :           2022 :             break;
     270                 :                :         }
     271                 :UBC           0 :         case xml::parser::start_namespace_decl:
     272                 :                :         case xml::parser::end_namespace_decl:
     273                 :                :         case xml::parser::start_attribute:
     274                 :                :         case xml::parser::end_attribute:
     275                 :                :         case xml::parser::eof:
     276                 :                :         default: {
     277         [ #  # ]:              0 :             throw xlnt::exception("unexpected XML parsing event");
     278                 :                :         }
     279                 :                :         }
     280                 :                :         // Prevents unhandled exceptions from being triggered.
     281            [ + ]:CBC        7225 :         parser->attribute_map();
     282                 :                :     }
     283                 :           2035 :     return c;
     284                 :UBC           0 : }
     285                 :                : 
     286                 :                : // <row> inside <sheetData> element
     287                 :CBC        2251 : std::pair<xlnt::row_properties, int> parse_row(xml::parser *parser, std::vector<xlnt::detail::Cell> &parsed_cells, std::unordered_map<std::string, std::string> &array_formulae, std::unordered_map<int, std::string> &shared_formulae)
     288                 :                : {
     289                 :           2251 :     std::pair<xlnt::row_properties, int> props;
     290      [ +  +  + ]:          10434 :     for (auto &attr : parser->attribute_map())
     291                 :                :     {
     292      [ +  +  + ]:           8183 :         if (string_equal(attr.first.name(), "dyDescent"))
     293                 :                :         {
     294            [ + ]:            476 :             props.first.dy_descent = xlnt::detail::deserialise(attr.second.value);
     295                 :                :         }
     296      [ +  +  + ]:           7707 :         else if (string_equal(attr.first.name(), "spans"))
     297                 :                :         {
     298            [ + ]:            527 :             props.first.spans = attr.second.value;
     299                 :                :         }
     300      [ +  +  + ]:           7180 :         else if (string_equal(attr.first.name(), "ht"))
     301                 :                :         {
     302            [ + ]:           1760 :             props.first.height = xlnt::detail::deserialise(attr.second.value);
     303                 :                :         }
     304      [ +  +  + ]:           5420 :         else if (string_equal(attr.first.name(), "s"))
     305                 :                :         {
     306                 :             21 :             size_t style = 0;
     307      [ +  +  - ]:             21 :             if (xlnt::detail::parse(attr.second.value, style) == std::errc())
     308                 :                :             {
     309            [ + ]:             21 :                 props.first.style = style;
     310                 :                :             }
     311                 :                :         }
     312      [ +  +  + ]:           5399 :         else if (string_equal(attr.first.name(), "hidden"))
     313                 :                :         {
     314            [ + ]:            336 :             props.first.hidden = is_true(attr.second.value);
     315                 :                :         }
     316      [ +  +  + ]:           5063 :         else if (string_equal(attr.first.name(), "customFormat"))
     317                 :                :         {
     318            [ + ]:            697 :             props.first.custom_format = is_true(attr.second.value);
     319                 :                :         }
     320      [ +  -  + ]:           4366 :         else if (string_equal(attr.first.name(), "ph"))
     321                 :                :         {
     322            [ # ]:UBC           0 :             is_true(attr.second.value);
     323                 :                :         }
     324      [ +  +  + ]:CBC        4366 :         else if (string_equal(attr.first.name(), "r"))
     325                 :                :         {
     326            [ + ]:           2251 :             xlnt::detail::parse(attr.second.value, props.second);
     327                 :                :         }
     328      [ +  +  + ]:           2115 :         else if (string_equal(attr.first.name(), "customHeight"))
     329                 :                :         {
     330            [ + ]:           1463 :             props.first.custom_height = is_true(attr.second.value);
     331                 :                :         }
     332                 :                :     }
     333                 :                : 
     334                 :           2251 :     int level = 1;
     335         [ +  + ]:           6893 :     while (level > 0)
     336                 :                :     {
     337            [ + ]:           4642 :         xml::parser::event_type e = parser->next();
     338   [ +  +  +  - ]:           4642 :         switch (e)
     339                 :                :         {
     340                 :           2035 :         case xml::parser::start_element: {
     341         [ +  + ]:           2035 :             parsed_cells.push_back(parse_cell(static_cast<xlnt::row_t>(props.second), parser, array_formulae, shared_formulae));
     342                 :           2035 :             break;
     343                 :                :         }
     344                 :           2251 :         case xml::parser::end_element: {
     345                 :           2251 :             --level;
     346                 :           2251 :             break;
     347                 :                :         }
     348                 :            356 :         case xml::parser::characters: {
     349                 :                :             // ignore whitespace
     350                 :            356 :             break;
     351                 :                :         }
     352                 :UBC           0 :         case xml::parser::start_namespace_decl:
     353                 :                :         case xml::parser::start_attribute:
     354                 :                :         case xml::parser::end_namespace_decl:
     355                 :                :         case xml::parser::end_attribute:
     356                 :                :         case xml::parser::eof:
     357                 :                :         default: {
     358         [ #  # ]:              0 :             throw xlnt::exception("unexpected XML parsing event");
     359                 :                :         }
     360                 :                :         }
     361                 :                :     }
     362                 :CBC        2251 :     return props;
     363                 :UBC           0 : }
     364                 :                : 
     365                 :                : // <sheetData> inside <worksheet> element
     366                 :CBC         125 : Sheet_Data parse_sheet_data(xml::parser *parser, std::unordered_map<std::string, std::string> &array_formulae, std::unordered_map<int, std::string> &shared_formulae)
     367                 :                : {
     368                 :            125 :     Sheet_Data sheet_data;
     369                 :            125 :     int level = 1; // nesting level
     370                 :                :         // 1 == <sheetData>
     371                 :                :         // 2 == <row>
     372                 :                : 
     373         [ +  + ]:           2501 :     while (level > 0)
     374                 :                :     {
     375            [ + ]:           2376 :         xml::parser::event_type e = parser->next();
     376   [ +  +  -  - ]:           2376 :         switch (e)
     377                 :                :         {
     378                 :           2251 :         case xml::parser::start_element: {
     379         [ +  + ]:           2251 :             sheet_data.parsed_rows.push_back(parse_row(parser, sheet_data.parsed_cells, array_formulae, shared_formulae));
     380                 :           2251 :             break;
     381                 :                :         }
     382                 :            125 :         case xml::parser::end_element: {
     383                 :            125 :             --level;
     384                 :            125 :             break;
     385                 :                :         }
     386                 :UBC           0 :         case xml::parser::characters: {
     387                 :                :             // ignore, whitespace formatting normally
     388                 :              0 :             break;
     389                 :                :         }
     390                 :              0 :         case xml::parser::start_namespace_decl:
     391                 :                :         case xml::parser::start_attribute:
     392                 :                :         case xml::parser::end_namespace_decl:
     393                 :                :         case xml::parser::end_attribute:
     394                 :                :         case xml::parser::eof:
     395                 :                :         default: {
     396         [ #  # ]:              0 :             throw xlnt::exception("unexpected XML parsing event");
     397                 :                :         }
     398                 :                :         }
     399                 :                :     }
     400                 :CBC         125 :     return sheet_data;
     401                 :UBC           0 : }
     402                 :                : 
     403                 :                : } // namespace
     404                 :                : 
     405                 :                : /*
     406                 :                : class parsing_context
     407                 :                : {
     408                 :                : public:
     409                 :                :     parsing_context(xlnt::detail::zip_file_reader &archive, const std::string &filename)
     410                 :                :         : parser_(stream_, filename)
     411                 :                :     {
     412                 :                :     }
     413                 :                : 
     414                 :                :     xml::parser &parser();
     415                 :                : 
     416                 :                : private:
     417                 :                :     std::istream stream_;
     418                 :                :     xml::parser parser_;
     419                 :                : };
     420                 :                : */
     421                 :                : 
     422                 :                : namespace xlnt {
     423                 :                : namespace detail {
     424                 :                : 
     425                 :CBC         103 : xlsx_consumer::xlsx_consumer(workbook &target)
     426                 :            103 :     : target_(target),
     427                 :            103 :       parser_(nullptr)
     428                 :                : {
     429                 :            103 : }
     430                 :                : 
     431                 :            103 : xlsx_consumer::~xlsx_consumer()
     432                 :                : {
     433         [ +  + ]:            103 :     if (target_.impl().stylesheet_.is_set())
     434                 :                :     {
     435                 :                :         // re-enable garbage collection, but do not run the garbage collection immediately, to allow a successful roundtrip without losing non-used formats.
     436                 :             86 :         target_.impl().stylesheet_.get().garbage_collection_enabled = true;
     437                 :                :     }
     438                 :            103 : }
     439                 :                : 
     440                 :             94 : void xlsx_consumer::read(std::istream &source)
     441                 :                : {
     442      [ +  -  - ]:             94 :     archive_.reset(new izstream(source));
     443                 :             94 :     populate_workbook(false);
     444                 :             93 : }
     445                 :                : 
     446                 :              3 : void xlsx_consumer::open(std::istream &source)
     447                 :                : {
     448      [ +  -  - ]:              3 :     archive_.reset(new izstream(source));
     449                 :              3 :     populate_workbook(true);
     450                 :              3 : }
     451                 :                : 
     452                 :            145 : cell xlsx_consumer::read_cell()
     453                 :                : {
     454            [ + ]:            145 :     return cell(streaming_cell_.get());
     455                 :                : }
     456                 :                : 
     457                 :            125 : void xlsx_consumer::read_worksheet(const std::string &rel_id)
     458                 :                : {
     459            [ + ]:            125 :     read_worksheet_begin(rel_id);
     460                 :                : 
     461         [ +  - ]:            125 :     if (!streaming_)
     462                 :                :     {
     463                 :            125 :         read_worksheet_sheetdata();
     464            [ + ]:            125 :         read_worksheet_end(rel_id);
     465                 :                :     }
     466                 :            125 : }
     467                 :                : 
     468                 :            128 : void read_defined_names(worksheet ws, std::vector<defined_name> defined_names)
     469                 :                : {
     470         [ +  + ]:            156 :     for (auto &name : defined_names)
     471                 :                :     {
     472      [ +  +  + ]:             28 :         if (name.sheet_id != ws.id() - 1)
     473                 :                :         {
     474                 :             21 :             continue;
     475                 :                :         }
     476                 :                : 
     477      [ +  +  + ]:              7 :         if (name.name == "_xlnm.Print_Titles")
     478                 :                :         {
     479                 :                :             // Basic print titles parser
     480                 :                :             // A print title definition looks like "'Sheet3'!$B:$E,'Sheet3'!$2:$4"
     481                 :                :             // There are three cases: columns only, rows only, and both (separated by a comma).
     482                 :                :             // For this reason, we loop up to two times parsing each component.
     483                 :                :             // Titles may be quoted (with single quotes) or unquoted. We ignore them for now anyways.
     484                 :                :             // References are always absolute.
     485                 :                :             // Move this into a separate function if it needs to be used in other places.
     486                 :              3 :             auto i = std::size_t(0);
     487         [ +  - ]:              4 :             for (auto count = 0; count < 2; count++)
     488                 :                :             {
     489                 :                :                 // Split into components based on "!", ":", and "," characters
     490                 :              4 :                 auto j = i;
     491                 :              4 :                 i = name.value.find('!', j);
     492            [ + ]:              4 :                 auto title = name.value.substr(j, i - j);
     493                 :              4 :                 j = i + 2; // skip "!$"
     494                 :              4 :                 i = name.value.find(':', j);
     495            [ + ]:              4 :                 auto from = name.value.substr(j, i - j);
     496                 :              4 :                 j = i + 2; // skip ":$"
     497                 :              4 :                 i = name.value.find(',', j);
     498            [ + ]:              4 :                 auto to = name.value.substr(j, i - j);
     499                 :                : 
     500                 :                :                 // Apply to the worksheet
     501         [ +  + ]:              4 :                 if (isalpha(from.front())) // alpha=>columns
     502                 :                :                 {
     503      [ +  +  + ]:              2 :                     ws.print_title_cols(from, to);
     504                 :                :                 }
     505                 :                :                 else // numeric=>rows
     506                 :                :                 {
     507                 :              2 :                     row_t start = 0;
     508                 :              2 :                     row_t end = 0;
     509                 :                : 
     510   [ +  +  -  +  :              4 :                     if (detail::parse(from, start) == std::errc() &&
                      - ]
     511      [ +  +  - ]:              2 :                         detail::parse(to, end) == std::errc())
     512                 :                :                     {
     513            [ + ]:              2 :                         ws.print_title_rows(start, end);
     514                 :                :                     }
     515                 :                :                 }
     516                 :                : 
     517                 :                :                 // Check for end condition
     518         [ +  + ]:              4 :                 if (i == std::string::npos)
     519                 :                :                 {
     520                 :              3 :                     break;
     521                 :                :                 }
     522                 :                : 
     523                 :              1 :                 i++; // skip "," for next iteration
     524   [ +  +  +  +  :             10 :             }
                   +  + ]
     525                 :                :         }
     526      [ +  +  + ]:              4 :         else if (name.name == "_xlnm._FilterDatabase")
     527                 :                :         {
     528                 :              1 :             auto i = name.value.find("!");
     529            [ + ]:              1 :             auto ref = name.value.substr(i + 1);
     530      [ +  +  - ]:              1 :             if (is_valid_reference(ref))
     531                 :                :             {
     532            [ + ]:              1 :                 ws.auto_filter(ref);
     533                 :                :             }
     534                 :              1 :         }
     535      [ +  +  - ]:              3 :         else if (name.name == "_xlnm.Print_Area")
     536                 :                :         {
     537                 :              3 :             auto i = name.value.find("!");
     538            [ + ]:              3 :             auto ref = name.value.substr(i + 1);
     539      [ +  +  + ]:              3 :             if (is_valid_reference(ref))
     540                 :                :             {
     541            [ + ]:              2 :                 ws.print_area(ref);
     542                 :                :             }
     543                 :              3 :         }
     544                 :                :     }
     545                 :            128 : }
     546                 :                : 
     547                 :            128 : std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id)
     548                 :                : {
     549   [ +  +  +  -  :            128 :     if (streaming_ && streaming_cell_ == nullptr)
                   +  + ]
     550                 :                :     {
     551   [ +  +  -  - ]:              3 :         streaming_cell_.reset(new detail::cell_impl());
     552                 :                :     }
     553                 :                : 
     554                 :            128 :     array_formulae_.clear();
     555                 :            128 :     shared_formulae_.clear();
     556                 :                : 
     557            [ + ]:            128 :     auto title = std::find_if(target_.d_->sheet_title_rel_id_map_.begin(),
     558                 :            128 :         target_.d_->sheet_title_rel_id_map_.end(),
     559                 :            203 :         [&](const std::pair<std::string, std::string> &p) {
     560                 :            203 :             return p.second == rel_id;
     561            [ + ]:            128 :         })->first;
     562                 :                : 
     563            [ + ]:            128 :     auto ws = worksheet(current_worksheet_);
     564                 :                : 
     565   [ +  +  +  + ]:            512 :     expect_start_element(qn("spreadsheetml", "worksheet"), xml::content::complex); // CT_Worksheet
     566   [ +  +  +  +  :            512 :     skip_attributes({qn("mc", "Ignorable")});
             +  +  +  -  
                      - ]
     567                 :                : 
     568      [ +  +  + ]:            128 :     read_defined_names(ws, defined_names_);
     569                 :                : 
     570   [ +  +  +  +  :           2456 :     while (in_element(qn("spreadsheetml", "worksheet")))
                   +  - ]
     571                 :                :     {
     572            [ + ]:            582 :         auto current_worksheet_element = expect_start_element(xml::content::complex);
     573                 :                : 
     574   [ +  +  +  +  :           2328 :         if (current_worksheet_element == qn("spreadsheetml", "sheetPr")) // CT_SheetPr 0-1
                      + ]
     575                 :                :         {
     576                 :             35 :             sheet_pr props;
     577   [ +  +  -  + ]:            105 :             if (parser().attribute_present("syncHorizontal"))
     578                 :                :             { // optional, boolean, false
     579         [ #  # ]:UBC           0 :                 props.sync_horizontal.set(parser().attribute<bool>("syncHorizontal"));
     580                 :                :             }
     581   [ +  +  -  + ]:CBC         105 :             if (parser().attribute_present("syncVertical"))
     582                 :                :             { // optional, boolean, false
     583         [ #  # ]:UBC           0 :                 props.sync_vertical.set(parser().attribute<bool>("syncVertical"));
     584                 :                :             }
     585   [ +  +  -  + ]:CBC         105 :             if (parser().attribute_present("syncRef"))
     586                 :                :             { // optional, ST_Ref, false
     587      [ #  #  # ]:UBC           0 :                 props.sync_ref.set(cell_reference(parser().attribute("syncRef")));
     588                 :                :             }
     589   [ +  +  -  + ]:CBC         105 :             if (parser().attribute_present("transitionEvaluation"))
     590                 :                :             { // optional, boolean, false
     591         [ #  # ]:UBC           0 :                 props.transition_evaluation.set(parser().attribute<bool>("transitionEvaluation"));
     592                 :                :             }
     593   [ +  +  -  + ]:CBC         105 :             if (parser().attribute_present("transitionEntry"))
     594                 :                :             { // optional, boolean, false
     595         [ #  # ]:UBC           0 :                 props.transition_entry.set(parser().attribute<bool>("transitionEntry"));
     596                 :                :             }
     597   [ +  +  -  + ]:CBC         105 :             if (parser().attribute_present("published"))
     598                 :                :             { // optional, boolean, true
     599         [ #  # ]:UBC           0 :                 props.published.set(parser().attribute<bool>("published"));
     600                 :                :             }
     601   [ +  +  +  + ]:CBC         105 :             if (parser().attribute_present("codeName"))
     602                 :                :             { // optional, string
     603         [ +  + ]:              9 :                 props.code_name.set(parser().attribute<std::string>("codeName"));
     604                 :                :             }
     605   [ +  +  +  + ]:            105 :             if (parser().attribute_present("filterMode"))
     606                 :                :             { // optional, boolean, false
     607         [ +  + ]:             60 :                 props.filter_mode.set(parser().attribute<bool>("filterMode"));
     608                 :                :             }
     609   [ +  +  -  + ]:            105 :             if (parser().attribute_present("enableFormatConditionsCalculation"))
     610                 :                :             { // optional, boolean, true
     611         [ #  # ]:UBC           0 :                 props.enable_format_condition_calculation.set(parser().attribute<bool>("enableFormatConditionsCalculation"));
     612                 :                :             }
     613            [ + ]:CBC          35 :             ws.d_->sheet_properties_.set(props);
     614      [ +  +  + ]:             71 :             while (in_element(current_worksheet_element))
     615                 :                :             {
     616            [ + ]:             36 :                 auto sheet_pr_child_element = expect_start_element(xml::content::simple);
     617                 :                : 
     618   [ +  +  +  -  :            144 :                 if (sheet_pr_child_element == qn("spreadsheetml", "tabColor")) // CT_Color 0-1
                      + ]
     619                 :                :                 {
     620            [ # ]:UBC           0 :                     read_color();
     621                 :                :                 }
     622   [ +  +  +  +  :CBC         144 :                 else if (sheet_pr_child_element == qn("spreadsheetml", "outlinePr")) // CT_OutlinePr 0-1
                      + ]
     623                 :                :                 {
     624         [ +  + ]:             14 :                     skip_attribute("applyStyles"); // optional, boolean, false
     625         [ +  + ]:             14 :                     skip_attribute("summaryBelow"); // optional, boolean, true
     626         [ +  + ]:             14 :                     skip_attribute("summaryRight"); // optional, boolean, true
     627         [ +  + ]:             14 :                     skip_attribute("showOutlineSymbols"); // optional, boolean, true
     628                 :                :                 }
     629   [ +  +  +  +  :            116 :                 else if (sheet_pr_child_element == qn("spreadsheetml", "pageSetUpPr")) // CT_PageSetUpPr 0-1
                      - ]
     630                 :                :                 {
     631         [ +  + ]:             58 :                     skip_attribute("autoPageBreaks"); // optional, boolean, true
     632         [ +  + ]:             58 :                     skip_attribute("fitToPage"); // optional, boolean, false
     633                 :                :                 }
     634                 :                :                 else
     635                 :                :                 {
     636            [ # ]:UBC           0 :                     unexpected_element(sheet_pr_child_element);
     637                 :                :                 }
     638                 :                : 
     639            [ + ]:CBC          36 :                 expect_end_element(sheet_pr_child_element);
     640                 :             36 :             }
     641                 :             35 :         }
     642   [ +  +  +  +  :           2188 :         else if (current_worksheet_element == qn("spreadsheetml", "dimension")) // CT_SheetDimension 0-1
                      + ]
     643                 :                :         {
     644            [ + ]:            127 :             skip_remaining_content(current_worksheet_element);
     645                 :                :         }
     646   [ +  +  +  +  :           1680 :         else if (current_worksheet_element == qn("spreadsheetml", "sheetViews")) // CT_SheetViews 0-1
                      + ]
     647                 :                :         {
     648      [ +  +  + ]:            232 :             while (in_element(current_worksheet_element))
     649                 :                :             {
     650   [ +  +  +  + ]:            464 :                 expect_start_element(qn("spreadsheetml", "sheetView"), xml::content::complex); // CT_SheetView 1+
     651                 :                : 
     652                 :            116 :                 sheet_view new_view;
     653         [ +  + ]:            232 :                 new_view.id(parser().attribute<std::size_t>("workbookViewId"));
     654                 :                : 
     655   [ +  +  +  + ]:            348 :                 if (parser().attribute_present("showGridLines")) // default="true"
     656                 :                :                 {
     657      [ +  +  + ]:             72 :                     new_view.show_grid_lines(is_true(parser().attribute("showGridLines")));
     658                 :                :                 }
     659   [ +  +  +  + ]:            348 :                 if (parser().attribute_present("topLeftCell"))
     660                 :                :                 {
     661      [ +  +  + ]:             69 :                     new_view.top_left_cell(cell_reference(parser().attribute("topLeftCell")));
     662                 :                :                 }
     663                 :                : 
     664   [ +  +  +  + ]:            348 :                 if (parser().attribute_present("defaultGridColor")) // default="true"
     665                 :                :                 {
     666      [ +  +  + ]:             63 :                     new_view.default_grid_color(is_true(parser().attribute("defaultGridColor")));
     667                 :                :                 }
     668                 :                : 
     669   [ +  +  -  +  :            348 :                 if (parser().attribute_present("view")
             +  -  -  -  
                      - ]
     670   [ +  +  +  +  :            278 :                     && parser().attribute("view") != "normal")
          +  +  +  +  +  
          +  +  +  +  -  
             -  -  -  - ]
     671                 :                :                 {
     672   [ +  +  +  -  :             12 :                     new_view.type(parser().attribute("view") == "pageBreakPreview"
                      + ]
     673                 :                :                             ? sheet_view_type::page_break_preview
     674                 :                :                             : sheet_view_type::page_layout);
     675                 :                :                 }
     676                 :                : 
     677   [ +  +  +  + ]:            348 :                 if (parser().attribute_present("zoomScale"))
     678                 :                :                 {
     679         [ +  + ]:             60 :                     new_view.zoom_scale(parser().attribute<int>("zoomScale"));
     680                 :                :                 }
     681                 :                : 
     682   [ +  +  -  +  :            348 :                 if (parser().attribute_present("tabSelected")
             +  -  -  -  
                      - ]
     683   [ +  +  +  +  :            402 :                     && is_true(parser().attribute("tabSelected")))
          +  +  +  +  +  
          +  +  +  +  -  
             -  -  -  - ]
     684                 :                :                 {
     685         [ +  + ]:             78 :                     target_.d_->view_.get().active_tab = ws.id() - 1;
     686                 :                :                 }
     687                 :                : 
     688         [ +  + ]:            232 :                 skip_attributes({"windowProtection", "showFormulas", "showRowColHeaders", "showZeros", "rightToLeft", "showRuler", "showOutlineSymbols", "showWhiteSpace",
     689                 :                :                     "view", "topLeftCell", "colorId", "zoomScaleNormal", "zoomScaleSheetLayoutView",
     690                 :                :                     "zoomScalePageLayoutView"});
     691                 :                : 
     692   [ +  +  +  +  :            880 :                 while (in_element(qn("spreadsheetml", "sheetView")))
                   +  + ]
     693                 :                :                 {
     694            [ + ]:             75 :                     auto sheet_view_child_element = expect_start_element(xml::content::simple);
     695                 :                : 
     696   [ +  +  +  +  :            300 :                     if (sheet_view_child_element == qn("spreadsheetml", "pane")) // CT_Pane 0-1
                      + ]
     697                 :                :                     {
     698            [ + ]:              3 :                         pane new_pane;
     699                 :                : 
     700   [ +  +  +  - ]:              9 :                         if (parser().attribute_present("topLeftCell"))
     701                 :                :                         {
     702      [ +  +  + ]:              9 :                             new_pane.top_left_cell = cell_reference(parser().attribute("topLeftCell"));
     703                 :                :                         }
     704                 :                : 
     705   [ +  +  +  + ]:              9 :                         if (parser().attribute_present("xSplit"))
     706                 :                :                         {
     707      [ +  +  + ]:              6 :                             new_pane.x_split = parser().attribute<column_t::index_t>("xSplit");
     708                 :                :                         }
     709                 :                : 
     710   [ +  +  +  - ]:              9 :                         if (parser().attribute_present("ySplit"))
     711                 :                :                         {
     712         [ +  + ]:              9 :                             new_pane.y_split = parser().attribute<row_t>("ySplit");
     713                 :                :                         }
     714                 :                : 
     715   [ +  +  +  - ]:              9 :                         if (parser().attribute_present("activePane"))
     716                 :                :                         {
     717         [ +  + ]:              9 :                             new_pane.active_pane = parser().attribute<pane_corner>("activePane");
     718                 :                :                         }
     719                 :                : 
     720   [ +  +  +  - ]:              9 :                         if (parser().attribute_present("state"))
     721                 :                :                         {
     722         [ +  + ]:              9 :                             new_pane.state = parser().attribute<pane_state>("state");
     723                 :                :                         }
     724                 :                : 
     725                 :              3 :                         new_view.pane(new_pane);
     726                 :              3 :                     }
     727   [ +  +  +  +  :            288 :                     else if (sheet_view_child_element == qn("spreadsheetml", "selection")) // CT_Selection 0-4
                      - ]
     728                 :                :                     {
     729                 :             72 :                         selection current_selection;
     730                 :                : 
     731   [ +  +  +  + ]:            216 :                         if (parser().attribute_present("activeCell"))
     732                 :                :                         {
     733      [ +  +  + ]:            207 :                             current_selection.active_cell(parser().attribute("activeCell"));
     734                 :                :                         }
     735                 :                : 
     736   [ +  +  +  - ]:            216 :                         if (parser().attribute_present("sqref"))
     737                 :                :                         {
     738      [ +  +  + ]:            216 :                             current_selection.sqref(parser().attribute("sqref"));
     739                 :                :                         }
     740                 :                : 
     741   [ +  +  +  + ]:            216 :                         if (parser().attribute_present("pane"))
     742                 :                :                         {
     743         [ +  + ]:             63 :                             current_selection.pane(parser().attribute<pane_corner>("pane"));
     744                 :                :                         }
     745                 :                : 
     746            [ + ]:             72 :                         new_view.add_selection(current_selection);
     747                 :                : 
     748            [ + ]:             72 :                         skip_remaining_content(sheet_view_child_element);
     749                 :             72 :                     }
     750   [ #  #  #  #  :UBC           0 :                     else if (sheet_view_child_element == qn("spreadsheetml", "pivotSelection")) // CT_PivotSelection 0-4
                      # ]
     751                 :                :                     {
     752            [ # ]:              0 :                         skip_remaining_content(sheet_view_child_element);
     753                 :                :                     }
     754   [ #  #  #  #  :              0 :                     else if (sheet_view_child_element == qn("spreadsheetml", "extLst")) // CT_ExtensionList 0-1
                      # ]
     755                 :                :                     {
     756            [ # ]:              0 :                         skip_remaining_content(sheet_view_child_element);
     757                 :                :                     }
     758                 :                :                     else
     759                 :                :                     {
     760            [ # ]:              0 :                         unexpected_element(sheet_view_child_element);
     761                 :                :                     }
     762                 :                : 
     763            [ + ]:CBC          75 :                     expect_end_element(sheet_view_child_element);
     764                 :             75 :                 }
     765                 :                : 
     766   [ +  +  +  + ]:            348 :                 expect_end_element(qn("spreadsheetml", "sheetView"));
     767                 :                : 
     768            [ + ]:            116 :                 ws.d_->views_.push_back(new_view);
     769                 :            116 :             }
     770                 :                :         }
     771   [ +  +  +  +  :           1216 :         else if (current_worksheet_element == qn("spreadsheetml", "sheetFormatPr")) // CT_SheetFormatPr 0-1
                      + ]
     772                 :                :         {
     773   [ +  +  +  + ]:            381 :             if (parser().attribute_present("baseColWidth"))
     774                 :                :             {
     775                 :             83 :                 ws.d_->format_properties_.base_col_width =
     776      [ +  +  + ]:            249 :                     xlnt::detail::deserialise(parser().attribute("baseColWidth"));
     777                 :                :             }
     778   [ +  +  +  + ]:            381 :             if (parser().attribute_present("defaultColWidth"))
     779                 :                :             {
     780                 :             20 :                 ws.d_->format_properties_.default_column_width =
     781      [ +  +  + ]:             60 :                     xlnt::detail::deserialise(parser().attribute("defaultColWidth"));
     782                 :                :             }
     783   [ +  +  +  - ]:            381 :             if (parser().attribute_present("defaultRowHeight"))
     784                 :                :             {
     785                 :            254 :                 ws.d_->format_properties_.default_row_height =
     786      [ +  +  + ]:            381 :                     xlnt::detail::deserialise(parser().attribute("defaultRowHeight"));
     787                 :                :             }
     788                 :                : 
     789   [ +  +  +  +  :            635 :             if (parser().attribute_present(qn("x14ac", "dyDescent")))
                   +  + ]
     790                 :                :             {
     791                 :             61 :                 ws.d_->format_properties_.dy_descent =
     792   [ +  +  +  +  :            305 :                     xlnt::detail::deserialise(parser().attribute(qn("x14ac", "dyDescent")));
                      + ]
     793                 :                :             }
     794                 :                : 
     795            [ + ]:            127 :             skip_attributes();
     796                 :                :         }
     797   [ +  +  +  +  :            708 :         else if (current_worksheet_element == qn("spreadsheetml", "cols")) // CT_Cols 0+
                      + ]
     798                 :                :         {
     799   [ +  +  +  +  :            737 :             while (in_element(qn("spreadsheetml", "cols")))
                   +  + ]
     800                 :                :             {
     801   [ +  +  +  + ]:            492 :                 expect_start_element(qn("spreadsheetml", "col"), xml::content::simple);
     802                 :                : 
     803   [ +  +  +  +  :            861 :                 skip_attributes(std::vector<std::string>{"collapsed", "outlineLevel"});
          +  +  +  -  -  
             -  -  -  - ]
     804                 :                : 
     805                 :            123 :                 column_t::index_t min = 0;
     806                 :            123 :                 column_t::index_t max = 0;
     807      [ +  +  + ]:            246 :                 bool ok = detail::parse(parser().attribute("min"), min) == std::errc();
     808   [ +  +  +  +  :            246 :                 ok = detail::parse(parser().attribute("max"), max) == std::errc() && ok;
          -  +  -  +  -  
          +  -  -  -  -  
                      - ]
     809                 :                : 
     810                 :                : #ifdef THROW_ON_INVALID_XML
     811                 :                :                 if (!ok)
     812                 :                :                 {
     813                 :                :                     throw xlnt::invalid_parameter("spreadsheetml invalid min/max");
     814                 :                :                 }
     815                 :                : #endif
     816                 :                : 
     817                 :                :                 // avoid uninitialised warnings in GCC by using a lambda to make the conditional initialisation
     818                 :            123 :                 optional<double> width = [this](xml::parser &p) -> xlnt::optional<double> {
     819   [ +  +  +  - ]:            246 :                     if (p.attribute_present("width"))
     820                 :                :                     {
     821      [ +  +  + ]:            246 :                         return (xlnt::detail::deserialise(p.attribute("width")) * 7 - 5) / 7;
     822                 :                :                     }
     823                 :UBC           0 :                     return xlnt::optional<double>();
     824            [ + ]:CBC         123 :                 }(parser());
     825                 :                :                 // avoid uninitialised warnings in GCC by using a lambda to make the conditional initialisation
     826                 :            123 :                 optional<std::size_t> column_style = [](xml::parser &p) -> xlnt::optional<std::size_t> {
     827   [ +  +  +  + ]:            246 :                     if (p.attribute_present("style"))
     828                 :                :                     {
     829         [ +  + ]:            156 :                         return p.attribute<std::size_t>("style");
     830                 :                :                     }
     831                 :             45 :                     return xlnt::optional<std::size_t>();
     832            [ + ]:            123 :                 }(parser());
     833                 :                : 
     834         [ +  + ]:            246 :                 auto custom = parser().attribute_present("customWidth")
     835   [ +  +  +  +  :            450 :                     ? is_true(parser().attribute("customWidth"))
          +  +  +  -  -  
                   -  - ]
     836         [ +  + ]:            123 :                     : false;
     837         [ +  + ]:            246 :                 auto hidden = parser().attribute_present("hidden")
     838   [ +  +  +  +  :            310 :                     ? is_true(parser().attribute("hidden"))
          +  +  +  -  -  
                   -  - ]
     839         [ +  + ]:            123 :                     : false;
     840         [ +  + ]:            369 :                 auto best_fit = parser().attribute_present("bestFit")
     841   [ +  +  +  +  :            268 :                     ? is_true(parser().attribute("bestFit"))
          +  +  +  -  -  
                   -  - ]
     842         [ +  + ]:            123 :                     : false;
     843                 :                : 
     844   [ +  +  +  + ]:            369 :                 expect_end_element(qn("spreadsheetml", "col"));
     845                 :                : 
     846         [ +  + ]:          92898 :                 for (auto column = min; column <= max; column++)
     847                 :                :                 {
     848                 :          92775 :                     column_properties props;
     849                 :                : 
     850         [ +  - ]:          92775 :                     if (width.is_set())
     851                 :                :                     {
     852         [ +  + ]:          92775 :                         props.width = width.get();
     853                 :                :                     }
     854                 :                : 
     855         [ +  + ]:          92775 :                     if (column_style.is_set())
     856                 :                :                     {
     857         [ +  + ]:          92705 :                         props.style = column_style.get();
     858                 :                :                     }
     859                 :                : 
     860                 :          92775 :                     props.hidden = hidden;
     861                 :          92775 :                     props.custom_width = custom;
     862                 :          92775 :                     props.best_fit = best_fit;
     863         [ +  + ]:          92775 :                     ws.add_column_properties(column, props);
     864                 :          92775 :                 }
     865                 :            123 :             }
     866                 :                :         }
     867   [ +  +  +  +  :            512 :         else if (current_worksheet_element == qn("spreadsheetml", "sheetData")) // CT_SheetData 1
                      - ]
     868                 :                :         {
     869                 :            128 :             return title;
     870                 :                :         }
     871                 :                : 
     872            [ + ]:            454 :         expect_end_element(current_worksheet_element);
     873         [ +  + ]:            582 :     }
     874                 :                : 
     875                 :UBC           0 :     return title;
     876   [ +  -  -  -  :CBC         256 : }
                      - ]
     877                 :                : 
     878                 :            125 : void xlsx_consumer::read_worksheet_sheetdata()
     879                 :                : {
     880   [ +  +  +  -  :            500 :     if (stack_.back() != qn("spreadsheetml", "sheetData"))
                      + ]
     881                 :                :     {
     882                 :UBC           0 :         return;
     883                 :                :     }
     884                 :                : 
     885            [ + ]:CBC         125 :     auto ws_data = parse_sheet_data(parser_, array_formulae_, shared_formulae_);
     886                 :                :     // NOTE: parse->construct are seperated here and could easily be threaded
     887                 :                :     // with a SPSC queue for what is likely to be an easy performance win
     888         [ +  + ]:           2376 :     for (auto &row : ws_data.parsed_rows)
     889                 :                :     {
     890            [ + ]:           2251 :         current_worksheet_->row_properties_.emplace(row.second, std::move(row.first));
     891                 :                :     }
     892            [ + ]:            125 :     auto impl = detail::cell_impl();
     893         [ +  + ]:           2160 :     for (Cell &cell : ws_data.parsed_cells)
     894                 :                :     {
     895                 :           2035 :         impl.parent_ = current_worksheet_;
     896            [ + ]:           2035 :         impl.column_ = cell.ref.column;
     897                 :           2035 :         impl.row_ = cell.ref.row;
     898         [ +  + ]:           2035 :         detail::cell_impl *ws_cell_impl = &current_worksheet_->cell_map_.emplace(cell_reference(impl.column_, impl.row_), std::move(impl)).first->second;
     899         [ +  + ]:           2035 :         if (cell.style_index != -1)
     900                 :                :         {
     901         [ +  + ]:            852 :             ws_cell_impl->format_ = target_.format(static_cast<size_t>(cell.style_index)).d_;
     902                 :                :         }
     903                 :           2035 :         if (cell.cell_metadata_idx != -1)
     904                 :                :         {
     905                 :                :         }
     906                 :           2035 :         ws_cell_impl->phonetics_visible_ = cell.is_phonetic;
     907         [ +  + ]:           2035 :         if (!cell.formula_string.empty())
     908                 :                :         {
     909      [ -  +  - ]:             49 :             ws_cell_impl->formula_ = cell.formula_string[0] == '=' ? cell.formula_string.substr(1) : std::move(cell.formula_string);
     910                 :                :         }
     911         [ +  + ]:           2035 :         if (!cell.value.empty())
     912                 :                :         {
     913                 :           1533 :             ws_cell_impl->type_ = cell.type;
     914   [ -  +  +  +  :           1533 :             switch (cell.type)
                +  -  - ]
     915                 :                :             {
     916                 :UBC           0 :             case cell::type::boolean: {
     917      [ #  #  # ]:              0 :                 ws_cell_impl->value_numeric_ = is_true(cell.value) ? 1.0 : 0.0;
     918                 :              0 :                 break;
     919                 :                :             }
     920                 :CBC          69 :             case cell::type::empty:
     921                 :                :             case cell::type::number:
     922                 :                :             case cell::type::date: {
     923            [ + ]:             69 :                 ws_cell_impl->value_numeric_ = xlnt::detail::deserialise(cell.value);
     924                 :             69 :                 break;
     925                 :                :             }
     926                 :           1447 :             case cell::type::shared_string: {
     927                 :           1447 :                 long long value = -1;
     928      [ +  +  - ]:           1447 :                 if (xlnt::detail::parse(cell.value, value) == std::errc())
     929                 :                :                 {
     930                 :           1447 :                     ws_cell_impl->value_numeric_ = static_cast<double>(value);
     931                 :                :                 }
     932                 :           1447 :                 break;
     933                 :                :             }
     934                 :              2 :             case cell::type::inline_string: {
     935         [ +  + ]:              2 :                 ws_cell_impl->value_text_ = std::move(cell.value);
     936                 :              2 :                 break;
     937                 :                :             }
     938                 :             15 :             case cell::type::formula_string: {
     939         [ +  + ]:             15 :                 ws_cell_impl->value_text_ = std::move(cell.value);
     940                 :             15 :                 break;
     941                 :                :             }
     942                 :UBC           0 :             case cell::type::error: {
     943            [ # ]:              0 :                 ws_cell_impl->value_text_.plain_text(cell.value, false);
     944                 :              0 :                 break;
     945                 :                :             }
     946                 :                :             }
     947                 :                :         }
     948                 :                :     }
     949                 :CBC         125 :     stack_.pop_back();
     950                 :                : 
     951                 :                : 
     952                 :            125 : }
     953                 :                : 
     954                 :            126 : worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id)
     955                 :                : {
     956            [ + ]:            126 :     auto &manifest = target_.manifest();
     957                 :                : 
     958      [ +  +  + ]:            126 :     const auto workbook_rel = manifest.relationship(path("/"), relationship_type::office_document);
     959      [ +  +  + ]:            126 :     const auto sheet_rel = manifest.relationship(workbook_rel.target().path(), rel_id);
     960   [ +  +  +  +  :            126 :     path sheet_path(sheet_rel.source().path().parent().append(sheet_rel.target().path()));
                   +  + ]
     961            [ + ]:            126 :     auto hyperlinks = manifest.relationships(sheet_path, xlnt::relationship_type::hyperlink);
     962                 :                : 
     963            [ + ]:            126 :     auto ws = worksheet(current_worksheet_);
     964                 :                : 
     965   [ +  +  +  +  :           1626 :     while (in_element(qn("spreadsheetml", "worksheet")))
                   +  + ]
     966                 :                :     {
     967            [ + ]:            249 :         auto current_worksheet_element = expect_start_element(xml::content::complex);
     968                 :                : 
     969   [ +  +  +  -  :            996 :         if (current_worksheet_element == qn("spreadsheetml", "sheetCalcPr")) // CT_SheetCalcPr 0-1
                      + ]
     970                 :                :         {
     971            [ # ]:UBC           0 :             skip_remaining_content(current_worksheet_element);
     972                 :                :         }
     973   [ +  +  +  +  :CBC         996 :         else if (current_worksheet_element == qn("spreadsheetml", "sheetProtection")) // CT_SheetProtection 0-1
                      + ]
     974                 :                :         {
     975            [ + ]:              2 :             skip_remaining_content(current_worksheet_element);
     976                 :                :         }
     977   [ +  +  +  -  :            988 :         else if (current_worksheet_element == qn("spreadsheetml", "protectedRanges")) // CT_ProtectedRanges 0-1
                      + ]
     978                 :                :         {
     979            [ # ]:UBC           0 :             skip_remaining_content(current_worksheet_element);
     980                 :                :         }
     981   [ +  +  +  -  :CBC         988 :         else if (current_worksheet_element == qn("spreadsheetml", "scenarios")) // CT_Scenarios 0-1
                      + ]
     982                 :                :         {
     983            [ # ]:UBC           0 :             skip_remaining_content(current_worksheet_element);
     984                 :                :         }
     985   [ +  +  +  +  :CBC         988 :         else if (current_worksheet_element == qn("spreadsheetml", "autoFilter")) // CT_AutoFilter 0-1
                      + ]
     986                 :                :         {
     987   [ +  +  +  + ]:              2 :             ws.auto_filter(xlnt::range_reference(parser().attribute("ref")));
     988                 :                :             // auto filter complex
     989            [ + ]:              1 :             skip_remaining_content(current_worksheet_element);
     990                 :                :         }
     991   [ +  +  +  -  :            984 :         else if (current_worksheet_element == qn("spreadsheetml", "sortState")) // CT_SortState 0-1
                      + ]
     992                 :                :         {
     993            [ # ]:UBC           0 :             skip_remaining_content(current_worksheet_element);
     994                 :                :         }
     995   [ +  +  +  -  :CBC         984 :         else if (current_worksheet_element == qn("spreadsheetml", "dataConsolidate")) // CT_DataConsolidate 0-1
                      + ]
     996                 :                :         {
     997            [ # ]:UBC           0 :             skip_remaining_content(current_worksheet_element);
     998                 :                :         }
     999   [ +  +  +  -  :CBC         984 :         else if (current_worksheet_element == qn("spreadsheetml", "customSheetViews")) // CT_CustomSheetViews 0-1
                      + ]
    1000                 :                :         {
    1001            [ # ]:UBC           0 :             skip_remaining_content(current_worksheet_element);
    1002                 :                :         }
    1003   [ +  +  +  +  :CBC         984 :         else if (current_worksheet_element == qn("spreadsheetml", "mergeCells")) // CT_MergeCells 0-1
                      + ]
    1004                 :                :         {
    1005            [ + ]:              2 :             parser().attribute_map();
    1006                 :                : 
    1007   [ +  +  +  +  :             18 :             while (in_element(qn("spreadsheetml", "mergeCells")))
                   +  + ]
    1008                 :                :             {
    1009   [ +  +  +  + ]:              8 :                 expect_start_element(qn("spreadsheetml", "mergeCell"), xml::content::simple);
    1010   [ +  +  +  + ]:              6 :                 ws.merge_cells(range_reference(parser().attribute("ref")));
    1011   [ +  +  +  + ]:              8 :                 expect_end_element(qn("spreadsheetml", "mergeCell"));
    1012                 :                :             }
    1013                 :                :         }
    1014   [ +  +  +  +  :            976 :         else if (current_worksheet_element == qn("spreadsheetml", "phoneticPr")) // CT_PhoneticPr 0-1
                      + ]
    1015                 :                :         {
    1016      [ +  +  + ]:             24 :             phonetic_pr phonetic_properties(parser().attribute<std::uint32_t>("fontId"));
    1017   [ +  +  +  + ]:             36 :             if (parser().attribute_present("type"))
    1018                 :                :             {
    1019   [ +  +  +  + ]:             30 :                 phonetic_properties.type(phonetic_pr::type_from_string(parser().attribute("type")));
    1020                 :                :             }
    1021   [ +  +  -  + ]:             36 :             if (parser().attribute_present("alignment"))
    1022                 :                :             {
    1023   [ #  #  #  # ]:UBC           0 :                 phonetic_properties.alignment(phonetic_pr::alignment_from_string(parser().attribute("alignment")));
    1024                 :                :             }
    1025                 :CBC          12 :             current_worksheet_->phonetic_properties_.set(phonetic_properties);
    1026                 :             12 :         }
    1027   [ +  +  +  -  :            928 :         else if (current_worksheet_element == qn("spreadsheetml", "conditionalFormatting")) // CT_ConditionalFormatting 0+
                      + ]
    1028                 :                :         {
    1029            [ # ]:UBC           0 :             skip_remaining_content(current_worksheet_element);
    1030                 :                :         }
    1031   [ +  +  +  -  :CBC         928 :         else if (current_worksheet_element == qn("spreadsheetml", "dataValidations")) // CT_DataValidations 0-1
                      + ]
    1032                 :                :         {
    1033            [ # ]:UBC           0 :             skip_remaining_content(current_worksheet_element);
    1034                 :                :         }
    1035   [ +  +  +  +  :CBC         928 :         else if (current_worksheet_element == qn("spreadsheetml", "hyperlinks")) // CT_Hyperlinks 0-1
                      + ]
    1036                 :                :         {
    1037      [ +  +  + ]:             51 :             while (in_element(current_worksheet_element))
    1038                 :                :             {
    1039                 :                :                 // CT_Hyperlink
    1040   [ +  +  +  + ]:            144 :                 expect_start_element(qn("spreadsheetml", "hyperlink"), xml::content::simple);
    1041                 :                : 
    1042   [ +  +  +  + ]:             72 :                 auto cell = ws.cell(parser().attribute("ref"));
    1043                 :                : 
    1044   [ +  +  +  +  :            180 :                 if (parser().attribute_present(qn("r", "id")))
                   +  + ]
    1045                 :                :                 {
    1046   [ +  +  +  +  :            116 :                     auto hyperlink_rel_id = parser().attribute(qn("r", "id"));
                      + ]
    1047            [ + ]:             29 :                     auto hyperlink_rel = std::find_if(hyperlinks.begin(), hyperlinks.end(),
    1048                 :             50 :                         [&](const relationship &r) { return r.id() == hyperlink_rel_id; });
    1049                 :                : 
    1050         [ +  - ]:             29 :                     if (hyperlink_rel != hyperlinks.end())
    1051                 :                :                     {
    1052   [ +  +  +  + ]:             29 :                         auto url = hyperlink_rel->target().path().string();
    1053                 :                : 
    1054      [ +  +  - ]:             29 :                         if (cell.has_value())
    1055                 :                :                         {
    1056         [ +  + ]:             29 :                             cell.hyperlink(url, cell.value<std::string>());
    1057                 :                :                         }
    1058                 :                :                         else
    1059                 :                :                         {
    1060         [ #  # ]:UBC           0 :                             cell.hyperlink(url);
    1061                 :                :                         }
    1062                 :CBC          29 :                     }
    1063                 :             29 :                 }
    1064   [ +  +  +  - ]:             21 :                 else if (parser().attribute_present("location"))
    1065                 :                :                 {
    1066            [ + ]:              7 :                     auto hyperlink = hyperlink_impl();
    1067                 :                : 
    1068      [ +  +  + ]:             14 :                     auto location = parser().attribute("location");
    1069                 :              7 :                     hyperlink.relationship = relationship("", relationship_type::hyperlink,
    1070   [ +  +  +  +  :             42 :                         uri(""), uri(location), target_mode::internal);
                      + ]
    1071                 :                : 
    1072   [ +  +  +  - ]:             21 :                     if (parser().attribute_present("display"))
    1073                 :                :                     {
    1074      [ +  +  + ]:             21 :                         hyperlink.display = parser().attribute("display");
    1075                 :                :                     }
    1076                 :                : 
    1077   [ +  +  -  + ]:             21 :                     if (parser().attribute_present("tooltip"))
    1078                 :                :                     {
    1079      [ #  #  # ]:UBC           0 :                         hyperlink.tooltip = parser().attribute("tooltip");
    1080                 :                :                     }
    1081                 :                : 
    1082            [ + ]:CBC           7 :                     cell.d_->hyperlink_ = hyperlink;
    1083                 :              7 :                 }
    1084                 :                : 
    1085   [ +  +  +  + ]:            144 :                 expect_end_element(qn("spreadsheetml", "hyperlink"));
    1086                 :                :             }
    1087                 :                :         }
    1088   [ +  +  +  +  :            868 :         else if (current_worksheet_element == qn("spreadsheetml", "printOptions")) // CT_PrintOptions 0-1
                      + ]
    1089                 :                :         {
    1090                 :             23 :             print_options opts;
    1091   [ +  +  +  + ]:             69 :             if (parser().attribute_present("gridLines"))
    1092                 :                :             {
    1093         [ +  + ]:             57 :                 opts.print_grid_lines.set(parser().attribute<bool>("gridLines"));
    1094                 :                :             }
    1095   [ +  +  +  + ]:             69 :             if (parser().attribute_present("gridLinesSet"))
    1096                 :                :             {
    1097         [ +  + ]:             57 :                 opts.grid_lines_set.set(parser().attribute<bool>("gridLinesSet"));
    1098                 :                :             }
    1099   [ +  +  +  + ]:             69 :             if (parser().attribute_present("headings"))
    1100                 :                :             {
    1101         [ +  + ]:             57 :                 opts.print_headings.set(parser().attribute<bool>("headings"));
    1102                 :                :             }
    1103   [ +  +  +  + ]:             69 :             if (parser().attribute_present("horizontalCentered"))
    1104                 :                :             {
    1105         [ +  + ]:             66 :                 opts.horizontal_centered.set(parser().attribute<bool>("horizontalCentered"));
    1106                 :                :             }
    1107   [ +  +  +  + ]:             69 :             if (parser().attribute_present("verticalCentered"))
    1108                 :                :             {
    1109         [ +  + ]:             57 :                 opts.vertical_centered.set(parser().attribute<bool>("verticalCentered"));
    1110                 :                :             }
    1111                 :             23 :             ws.d_->print_options_.set(opts);
    1112            [ + ]:             23 :             skip_remaining_content(current_worksheet_element);
    1113                 :             23 :         }
    1114   [ +  +  +  +  :            776 :         else if (current_worksheet_element == qn("spreadsheetml", "pageMargins")) // CT_PageMargins 0-1
                      + ]
    1115                 :                :         {
    1116            [ + ]:            108 :             page_margins margins;
    1117                 :                : 
    1118   [ +  +  +  + ]:            216 :             margins.top(xlnt::detail::deserialise(parser().attribute("top")));
    1119   [ +  +  +  + ]:            216 :             margins.bottom(xlnt::detail::deserialise(parser().attribute("bottom")));
    1120   [ +  +  +  + ]:            216 :             margins.left(xlnt::detail::deserialise(parser().attribute("left")));
    1121   [ +  +  +  + ]:            216 :             margins.right(xlnt::detail::deserialise(parser().attribute("right")));
    1122   [ +  +  +  + ]:            216 :             margins.header(xlnt::detail::deserialise(parser().attribute("header")));
    1123   [ +  +  +  + ]:            216 :             margins.footer(xlnt::detail::deserialise(parser().attribute("footer")));
    1124                 :                : 
    1125            [ + ]:            108 :             ws.page_margins(margins);
    1126                 :                :         }
    1127   [ +  +  +  +  :            344 :         else if (current_worksheet_element == qn("spreadsheetml", "pageSetup")) // CT_PageSetup 0-1
                      + ]
    1128                 :                :         {
    1129            [ + ]:             36 :             page_setup setup;
    1130   [ +  +  +  - ]:            108 :             if (parser().attribute_present("orientation"))
    1131                 :                :             {
    1132         [ +  + ]:            108 :                 setup.orientation_.set(parser().attribute<orientation>("orientation"));
    1133                 :                :             }
    1134   [ +  +  +  + ]:            108 :             if (parser().attribute_present("horizontalDpi"))
    1135                 :                :             {
    1136         [ +  + ]:             90 :                 setup.horizontal_dpi_.set(parser().attribute<std::size_t>("horizontalDpi"));
    1137                 :                :             }
    1138   [ +  +  +  + ]:            108 :             if (parser().attribute_present("verticalDpi"))
    1139                 :                :             {
    1140         [ +  + ]:             96 :                 setup.vertical_dpi_.set(parser().attribute<std::size_t>("verticalDpi"));
    1141                 :                :             }
    1142   [ +  +  +  + ]:            108 :             if (parser().attribute_present("paperSize"))
    1143                 :                :             {
    1144      [ +  +  + ]:             84 :                 setup.paper_size(static_cast<xlnt::paper_size>(parser().attribute<std::size_t>("paperSize")));
    1145                 :                :             }
    1146   [ +  +  +  + ]:            108 :             if (parser().attribute_present("scale"))
    1147                 :                :             {
    1148      [ +  +  + ]:             72 :                 setup.scale(parser().attribute<double>("scale"));
    1149                 :                :             }
    1150   [ +  +  +  +  :            180 :             if (parser().attribute_present(qn("r", "id")))
                   +  + ]
    1151                 :                :             {
    1152   [ +  +  +  +  :             25 :                 setup.rel_id(parser().attribute(qn("r", "id")));
                      + ]
    1153                 :                :             }
    1154            [ + ]:             36 :             ws.page_setup(setup);
    1155            [ + ]:             36 :             skip_remaining_content(current_worksheet_element);
    1156                 :             36 :         }
    1157   [ +  +  +  +  :            200 :         else if (current_worksheet_element == qn("spreadsheetml", "headerFooter")) // CT_HeaderFooter 0-1
                      + ]
    1158                 :                :         {
    1159                 :             29 :             header_footer hf;
    1160                 :                : 
    1161   [ +  +  +  -  :             87 :             hf.align_with_margins(!parser().attribute_present("alignWithMargins")
             -  -  -  - ]
    1162   [ +  +  +  +  :             93 :                 || is_true(parser().attribute("alignWithMargins")));
          +  +  -  +  +  
          +  +  +  +  -  
             -  -  -  - ]
    1163   [ +  +  +  -  :             87 :             hf.scale_with_doc(!parser().attribute_present("alignWithMargins")
             -  -  -  - ]
    1164   [ +  +  +  +  :             93 :                 || is_true(parser().attribute("alignWithMargins")));
          +  +  -  +  +  
          +  +  +  +  -  
             -  -  -  - ]
    1165   [ +  +  -  -  :             58 :             auto different_odd_even = parser().attribute_present("differentOddEven")
                -  -  - ]
    1166   [ +  +  +  +  :             96 :                 && is_true(parser().attribute("differentOddEven"));
          +  +  -  +  +  
          +  +  +  +  -  
             -  -  -  - ]
    1167   [ +  +  -  -  :             58 :             auto different_first = parser().attribute_present("differentFirst")
                -  -  - ]
    1168   [ +  +  +  +  :             96 :                 && is_true(parser().attribute("differentFirst"));
          +  +  -  +  +  
          +  +  +  +  -  
             -  -  -  - ]
    1169                 :                : 
    1170                 :             29 :             optional<std::array<optional<rich_text>, 3>> odd_header;
    1171                 :             29 :             optional<std::array<optional<rich_text>, 3>> odd_footer;
    1172                 :             29 :             optional<std::array<optional<rich_text>, 3>> even_header;
    1173                 :             29 :             optional<std::array<optional<rich_text>, 3>> even_footer;
    1174                 :             29 :             optional<std::array<optional<rich_text>, 3>> first_header;
    1175                 :             29 :             optional<std::array<optional<rich_text>, 3>> first_footer;
    1176                 :                : 
    1177                 :                :             using xlnt::detail::decode_header_footer;
    1178                 :                : 
    1179      [ +  +  + ]:             79 :             while (in_element(current_worksheet_element))
    1180                 :                :             {
    1181            [ + ]:             50 :                 auto current_hf_element = expect_start_element(xml::content::simple);
    1182                 :                : 
    1183   [ +  +  +  +  :            200 :                 if (current_hf_element == qn("spreadsheetml", "oddHeader"))
                      + ]
    1184                 :                :                 {
    1185      [ +  +  + ]:             24 :                     odd_header = decode_header_footer(read_text());
    1186                 :                :                 }
    1187   [ +  +  +  +  :            104 :                 else if (current_hf_element == qn("spreadsheetml", "oddFooter"))
                      - ]
    1188                 :                :                 {
    1189      [ +  +  + ]:             26 :                     odd_footer = decode_header_footer(read_text());
    1190                 :                :                 }
    1191   [ #  #  #  #  :UBC           0 :                 else if (current_hf_element == qn("spreadsheetml", "evenHeader"))
                      # ]
    1192                 :                :                 {
    1193      [ #  #  # ]:              0 :                     even_header = decode_header_footer(read_text());
    1194                 :                :                 }
    1195   [ #  #  #  #  :              0 :                 else if (current_hf_element == qn("spreadsheetml", "evenFooter"))
                      # ]
    1196                 :                :                 {
    1197      [ #  #  # ]:              0 :                     even_footer = decode_header_footer(read_text());
    1198                 :                :                 }
    1199   [ #  #  #  #  :              0 :                 else if (current_hf_element == qn("spreadsheetml", "firstHeader"))
                      # ]
    1200                 :                :                 {
    1201      [ #  #  # ]:              0 :                     first_header = decode_header_footer(read_text());
    1202                 :                :                 }
    1203   [ #  #  #  #  :              0 :                 else if (current_hf_element == qn("spreadsheetml", "firstFooter"))
                      # ]
    1204                 :                :                 {
    1205      [ #  #  # ]:              0 :                     first_footer = decode_header_footer(read_text());
    1206                 :                :                 }
    1207                 :                :                 else
    1208                 :                :                 {
    1209            [ # ]:              0 :                     unexpected_element(current_hf_element);
    1210                 :                :                 }
    1211                 :                : 
    1212            [ + ]:CBC          50 :                 expect_end_element(current_hf_element);
    1213                 :             50 :             }
    1214                 :                : 
    1215         [ +  + ]:            116 :             for (std::size_t i = 0; i < 3; ++i)
    1216                 :                :             {
    1217         [ +  + ]:            145 :                 auto loc = i == 0 ? header_footer::location::left
    1218         [ +  + ]:             58 :                                   : i == 1 ? header_footer::location::center : header_footer::location::right;
    1219                 :                : 
    1220         [ -  + ]:             87 :                 if (different_odd_even)
    1221                 :                :                 {
    1222                 :UBC           0 :                     if (odd_header.is_set()
    1223   [ #  #  #  # ]:              0 :                         && odd_header.get().at(i).is_set()
    1224         [ #  # ]:              0 :                         && even_header.is_set()
    1225   [ #  #  #  #  :              0 :                         && even_header.get().at(i).is_set())
             #  #  #  # ]
    1226                 :                :                     {
    1227   [ #  #  #  #  :              0 :                         hf.odd_even_header(loc, odd_header.get().at(i).get(), even_header.get().at(i).get());
                #  #  # ]
    1228                 :                :                     }
    1229                 :                : 
    1230                 :              0 :                     if (odd_footer.is_set()
    1231   [ #  #  #  # ]:              0 :                         && odd_footer.get().at(i).is_set()
    1232         [ #  # ]:              0 :                         && even_footer.is_set()
    1233   [ #  #  #  #  :              0 :                         && even_footer.get().at(i).is_set())
             #  #  #  # ]
    1234                 :                :                     {
    1235   [ #  #  #  #  :              0 :                         hf.odd_even_footer(loc, odd_footer.get().at(i).get(), even_footer.get().at(i).get());
                #  #  # ]
    1236                 :                :                     }
    1237                 :                :                 }
    1238                 :                :                 else
    1239                 :                :                 {
    1240   [ +  +  +  +  :CBC          87 :                     if (odd_header.is_set() && odd_header.get().at(i).is_set())
             +  +  +  + ]
    1241                 :                :                     {
    1242   [ +  +  +  + ]:             24 :                         hf.header(loc, odd_header.get().at(i).get());
    1243                 :                :                     }
    1244                 :                : 
    1245   [ +  +  +  +  :             87 :                     if (odd_footer.is_set() && odd_footer.get().at(i).is_set())
             +  +  +  + ]
    1246                 :                :                     {
    1247   [ +  +  +  + ]:             26 :                         hf.footer(loc, odd_footer.get().at(i).get());
    1248                 :                :                     }
    1249                 :                :                 }
    1250                 :                : 
    1251                 :                :                 if (different_first)
    1252                 :                :                 {
    1253                 :                :                 }
    1254                 :                :             }
    1255                 :                : 
    1256            [ + ]:             29 :             ws.header_footer(hf);
    1257                 :             29 :         }
    1258   [ +  +  +  -  :             84 :         else if (current_worksheet_element == qn("spreadsheetml", "rowBreaks")) // CT_PageBreak 0-1
                      + ]
    1259                 :                :         {
    1260   [ #  #  #  #  :UBC           0 :             auto count = parser().attribute_present("count") ? parser().attribute<std::size_t>("count") : 0;
          #  #  #  #  #  
             #  #  #  #  
                      # ]
    1261         [ #  # ]:              0 :             auto manual_break_count = parser().attribute_present("manualBreakCount")
    1262   [ #  #  #  #  :              0 :                 ? parser().attribute<std::size_t>("manualBreakCount")
          #  #  #  #  #  
                      # ]
    1263         [ #  # ]:              0 :                 : 0;
    1264                 :                : 
    1265   [ #  #  #  #  :              0 :             while (in_element(qn("spreadsheetml", "rowBreaks")))
                   #  # ]
    1266                 :                :             {
    1267   [ #  #  #  # ]:              0 :                 expect_start_element(qn("spreadsheetml", "brk"), xml::content::simple);
    1268                 :                : 
    1269   [ #  #  #  # ]:              0 :                 if (parser().attribute_present("id"))
    1270                 :                :                 {
    1271      [ #  #  # ]:              0 :                     ws.page_break_at_row(parser().attribute<row_t>("id"));
    1272                 :              0 :                     --count;
    1273                 :                :                 }
    1274                 :                : 
    1275   [ #  #  #  #  :              0 :                 if (parser().attribute_present("man") && is_true(parser().attribute("man")))
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
    1276                 :                :                 {
    1277                 :              0 :                     --manual_break_count;
    1278                 :                :                 }
    1279                 :                : 
    1280         [ #  # ]:              0 :                 skip_attributes({"min", "max", "pt"});
    1281   [ #  #  #  # ]:              0 :                 expect_end_element(qn("spreadsheetml", "brk"));
    1282                 :                :             }
    1283                 :                :         }
    1284   [ +  +  +  -  :CBC          84 :         else if (current_worksheet_element == qn("spreadsheetml", "colBreaks")) // CT_PageBreak 0-1
                      + ]
    1285                 :                :         {
    1286   [ #  #  #  #  :UBC           0 :             auto count = parser().attribute_present("count") ? parser().attribute<std::size_t>("count") : 0;
          #  #  #  #  #  
             #  #  #  #  
                      # ]
    1287         [ #  # ]:              0 :             auto manual_break_count = parser().attribute_present("manualBreakCount")
    1288   [ #  #  #  #  :              0 :                 ? parser().attribute<std::size_t>("manualBreakCount")
          #  #  #  #  #  
                      # ]
    1289         [ #  # ]:              0 :                 : 0;
    1290                 :                : 
    1291   [ #  #  #  #  :              0 :             while (in_element(qn("spreadsheetml", "colBreaks")))
                   #  # ]
    1292                 :                :             {
    1293   [ #  #  #  # ]:              0 :                 expect_start_element(qn("spreadsheetml", "brk"), xml::content::simple);
    1294                 :                : 
    1295   [ #  #  #  # ]:              0 :                 if (parser().attribute_present("id"))
    1296                 :                :                 {
    1297   [ #  #  #  # ]:              0 :                     ws.page_break_at_column(parser().attribute<column_t::index_t>("id"));
    1298                 :              0 :                     --count;
    1299                 :                :                 }
    1300                 :                : 
    1301   [ #  #  #  #  :              0 :                 if (parser().attribute_present("man") && is_true(parser().attribute("man")))
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
    1302                 :                :                 {
    1303                 :              0 :                     --manual_break_count;
    1304                 :                :                 }
    1305                 :                : 
    1306         [ #  # ]:              0 :                 skip_attributes({"min", "max", "pt"});
    1307   [ #  #  #  # ]:              0 :                 expect_end_element(qn("spreadsheetml", "brk"));
    1308                 :                :             }
    1309                 :                :         }
    1310   [ +  +  +  -  :CBC          84 :         else if (current_worksheet_element == qn("spreadsheetml", "customProperties")) // CT_CustomProperties 0-1
                      + ]
    1311                 :                :         {
    1312            [ # ]:UBC           0 :             skip_remaining_content(current_worksheet_element);
    1313                 :                :         }
    1314   [ +  +  +  -  :CBC          84 :         else if (current_worksheet_element == qn("spreadsheetml", "cellWatches")) // CT_CellWatches 0-1
                      + ]
    1315                 :                :         {
    1316            [ # ]:UBC           0 :             skip_remaining_content(current_worksheet_element);
    1317                 :                :         }
    1318   [ +  +  +  -  :CBC          84 :         else if (current_worksheet_element == qn("spreadsheetml", "ignoredErrors")) // CT_IgnoredErrors 0-1
                      + ]
    1319                 :                :         {
    1320            [ # ]:UBC           0 :             skip_remaining_content(current_worksheet_element);
    1321                 :                :         }
    1322   [ +  +  +  -  :CBC          84 :         else if (current_worksheet_element == qn("spreadsheetml", "smartTags")) // CT_SmartTags 0-1
                      + ]
    1323                 :                :         {
    1324            [ # ]:UBC           0 :             skip_remaining_content(current_worksheet_element);
    1325                 :                :         }
    1326   [ +  +  +  +  :CBC          84 :         else if (current_worksheet_element == qn("spreadsheetml", "drawing")) // CT_Drawing 0-1
                      + ]
    1327                 :                :         {
    1328   [ +  +  +  +  :             15 :             if (parser().attribute_present(qn("r", "id")))
                   +  - ]
    1329                 :                :             {
    1330   [ +  +  +  +  :             12 :                 auto drawing_rel_id = parser().attribute(qn("r", "id"));
                      + ]
    1331            [ + ]:              3 :                 ws.d_->drawing_rel_id_ = drawing_rel_id;
    1332                 :              3 :             }
    1333                 :                :         }
    1334   [ +  +  +  +  :             72 :         else if (current_worksheet_element == qn("spreadsheetml", "legacyDrawing"))
                      + ]
    1335                 :                :         {
    1336            [ + ]:             14 :             skip_remaining_content(current_worksheet_element);
    1337                 :                :         }
    1338   [ +  +  +  +  :             16 :         else if (current_worksheet_element == qn("spreadsheetml", "extLst"))
                      - ]
    1339                 :                :         {
    1340            [ + ]:              4 :             ext_list extensions(parser(), current_worksheet_element.namespace_());
    1341            [ + ]:              4 :             ws.d_->extension_list_.set(extensions);
    1342                 :              4 :         }
    1343                 :                :         else
    1344                 :                :         {
    1345            [ # ]:UBC           0 :             unexpected_element(current_worksheet_element);
    1346                 :                :         }
    1347                 :                : 
    1348            [ + ]:CBC         249 :         expect_end_element(current_worksheet_element);
    1349                 :            249 :     }
    1350                 :                : 
    1351   [ +  +  +  + ]:            378 :     expect_end_element(qn("spreadsheetml", "worksheet"));
    1352                 :                : 
    1353      [ +  +  + ]:            126 :     if (manifest.has_relationship(sheet_path, xlnt::relationship_type::comments))
    1354                 :                :     {
    1355   [ +  +  +  -  :             84 :         auto comments_part = manifest.canonicalize({workbook_rel, sheet_rel,
                      - ]
    1356            [ + ]:             14 :             manifest.relationship(sheet_path, xlnt::relationship_type::comments)});
    1357                 :                : 
    1358                 :             14 :         auto receive = xml::parser::receive_default;
    1359            [ + ]:             14 :         auto comments_part_streambuf = archive_->open(comments_part);
    1360            [ + ]:             14 :         std::istream comments_part_stream(comments_part_streambuf.get());
    1361         [ +  + ]:             14 :         xml::parser parser(comments_part_stream, comments_part.string(), receive);
    1362                 :             14 :         parser_ = &parser;
    1363                 :                : 
    1364         [ +  + ]:             14 :         read_comments(ws);
    1365                 :                : 
    1366      [ +  +  - ]:             14 :         if (manifest.has_relationship(sheet_path, xlnt::relationship_type::vml_drawing))
    1367                 :                :         {
    1368   [ +  +  +  -  :             84 :             auto vml_drawings_part = manifest.canonicalize({workbook_rel, sheet_rel,
                      - ]
    1369            [ + ]:             14 :                 manifest.relationship(sheet_path, xlnt::relationship_type::vml_drawing)});
    1370                 :                : 
    1371            [ + ]:             14 :             auto vml_drawings_part_streambuf = archive_->open(comments_part);
    1372            [ + ]:             14 :             std::istream vml_drawings_part_stream(comments_part_streambuf.get());
    1373         [ +  + ]:             14 :             xml::parser vml_parser(vml_drawings_part_stream, vml_drawings_part.string(), receive);
    1374                 :             14 :             parser_ = &vml_parser;
    1375                 :                : 
    1376            [ + ]:             14 :             read_vml_drawings(ws);
    1377                 :             14 :         }
    1378                 :             14 :     }
    1379                 :                : 
    1380      [ +  +  + ]:            126 :     if (manifest.has_relationship(sheet_path, xlnt::relationship_type::drawings))
    1381                 :                :     {
    1382   [ +  +  +  -  :             18 :         auto drawings_part = manifest.canonicalize({workbook_rel, sheet_rel,
                      - ]
    1383            [ + ]:              3 :             manifest.relationship(sheet_path, xlnt::relationship_type::drawings)});
    1384                 :                : 
    1385                 :              3 :         auto receive = xml::parser::receive_default;
    1386            [ + ]:              3 :         auto drawings_part_streambuf = archive_->open(drawings_part);
    1387            [ + ]:              3 :         std::istream drawings_part_stream(drawings_part_streambuf.get());
    1388         [ +  + ]:              3 :         xml::parser parser(drawings_part_stream, drawings_part.string(), receive);
    1389                 :              3 :         parser_ = &parser;
    1390                 :                : 
    1391         [ +  + ]:              3 :         read_drawings(ws, drawings_part);
    1392                 :              3 :     }
    1393                 :                : 
    1394      [ +  +  + ]:            126 :     if (manifest.has_relationship(sheet_path, xlnt::relationship_type::printer_settings))
    1395                 :                :     {
    1396   [ +  +  +  +  :             25 :         read_part({workbook_rel, sheet_rel,
                   -  - ]
    1397                 :                :             manifest.relationship(sheet_path,
    1398                 :                :                 relationship_type::printer_settings)});
    1399                 :                :     }
    1400                 :                : 
    1401      [ +  +  + ]:            131 :     for (auto array_formula : array_formulae_)
    1402                 :                :     {
    1403   [ +  +  +  +  :             14 :         for (auto row : ws.range(array_formula.first))
             +  +  +  + ]
    1404                 :                :         {
    1405   [ +  +  +  +  :             18 :             for (auto cell : row)
                +  +  + ]
    1406                 :                :             {
    1407            [ + ]:              9 :                 cell.formula(array_formula.second);
    1408                 :                :             }
    1409                 :              5 :         }
    1410                 :              5 :     }
    1411                 :                : 
    1412                 :            252 :     return ws;
    1413   [ +  +  +  +  :            162 : }
          +  +  +  +  +  
          +  +  +  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
             -  -  -  - ]
    1414                 :                : 
    1415                 :         209604 : xml::parser &xlsx_consumer::parser()
    1416                 :                : {
    1417                 :         209604 :     return *parser_;
    1418                 :                : }
    1419                 :                : 
    1420                 :            147 : bool xlsx_consumer::has_cell()
    1421                 :                : {
    1422            [ + ]:            147 :     auto ws = worksheet(current_worksheet_);
    1423                 :                : 
    1424                 :            147 :     while (streaming_cell_ // we're not at the end of the file
    1425   [ +  -  +  +  :            935 :            && !in_element(qn("spreadsheetml", "row"))) // we're at the end of a row, or between rows
          +  +  +  +  +  
          -  +  -  +  -  
          +  -  +  +  -  
          -  -  -  -  -  
                   -  - ]
    1426                 :                :     {
    1427            [ + ]:             42 :         if (parser().peek() == xml::parser::event_type::end_element
    1428   [ +  +  +  +  :            240 :             && stack_.back() == qn("spreadsheetml", "row"))
          +  +  -  +  +  
          +  +  +  +  +  
          +  +  +  -  -  
          -  -  -  -  -  
                      - ]
    1429                 :                :         {
    1430                 :                :             // We're at the end of a row.
    1431   [ +  +  +  + ]:            156 :             expect_end_element(qn("spreadsheetml", "row"));
    1432                 :                :             // ... and keep parsing.
    1433                 :                :         }
    1434                 :                : 
    1435            [ + ]:             42 :         if (parser().peek() == xml::parser::event_type::end_element
    1436   [ +  +  +  +  :             92 :             && stack_.back() == qn("spreadsheetml", "sheetData"))
          +  +  -  +  +  
          +  +  +  +  +  
          +  +  +  -  -  
          -  -  -  -  -  
                      - ]
    1437                 :                :         {
    1438                 :                :             // End of sheet. Mark it by setting streaming_cell_ to nullptr, so we never get here again.
    1439   [ +  +  +  + ]:              6 :             expect_end_element(qn("spreadsheetml", "sheetData"));
    1440                 :              2 :             streaming_cell_.reset(nullptr);
    1441                 :              2 :             break;
    1442                 :                :         }
    1443                 :                : 
    1444   [ +  +  +  + ]:            160 :         expect_start_element(qn("spreadsheetml", "row"), xml::content::complex); // CT_Row
    1445                 :             40 :         row_t row_index = 0;
    1446      [ +  +  + ]:             80 :         bool ok = detail::parse(parser().attribute("r"), row_index) == std::errc();
    1447                 :                : 
    1448                 :             40 :         if (!ok)
    1449                 :                :         {
    1450                 :                : #ifdef THROW_ON_INVALID_XML
    1451                 :                :             throw xlnt::invalid_parameter();
    1452                 :                : #endif
    1453                 :                :         }
    1454            [ + ]:             40 :         auto &row_properties = ws.row_properties(row_index);
    1455                 :                : 
    1456   [ +  +  +  + ]:            120 :         if (parser().attribute_present("ht"))
    1457                 :                :         {
    1458      [ +  +  + ]:             21 :             row_properties.height = xlnt::detail::deserialise(parser().attribute("ht"));
    1459                 :                :         }
    1460                 :                : 
    1461   [ +  +  -  + ]:            120 :         if (parser().attribute_present("customHeight"))
    1462                 :                :         {
    1463      [ #  #  # ]:UBC           0 :             row_properties.custom_height = is_true(parser().attribute("customHeight"));
    1464                 :                :         }
    1465                 :                : 
    1466   [ +  +  -  +  :CBC         120 :         if (parser().attribute_present("hidden") && is_true(parser().attribute("hidden")))
          -  -  -  -  -  
          -  +  -  +  +  
          -  +  -  -  +  
          -  -  -  -  -  
                -  -  - ]
    1467                 :                :         {
    1468                 :UBC           0 :             row_properties.hidden = true;
    1469                 :                :         }
    1470                 :                : 
    1471   [ +  +  +  +  :CBC         200 :         if (parser().attribute_present(qn("x14ac", "dyDescent")))
                   +  + ]
    1472                 :                :         {
    1473   [ +  +  +  +  :            175 :             row_properties.dy_descent = xlnt::detail::deserialise(parser().attribute(qn("x14ac", "dyDescent")));
                      + ]
    1474                 :                :         }
    1475                 :                : 
    1476   [ +  +  +  + ]:            120 :         if (parser().attribute_present("spans"))
    1477                 :                :         {
    1478      [ +  +  + ]:            105 :             row_properties.spans = parser().attribute("spans");
    1479                 :                :         }
    1480                 :                : 
    1481         [ +  + ]:            120 :         skip_attributes({"customFormat", "s", "customFont",
    1482                 :                :             "outlineLevel", "collapsed", "thickTop", "thickBot",
    1483                 :                :             "ph"});
    1484                 :                :     }
    1485                 :                : 
    1486         [ +  + ]:            147 :     if (!streaming_cell_)
    1487                 :                :     {
    1488                 :                :         // We're at the end of the worksheet
    1489                 :              2 :         return false;
    1490                 :                :     }
    1491                 :                : 
    1492   [ +  +  +  + ]:            580 :     expect_start_element(qn("spreadsheetml", "c"), xml::content::complex);
    1493                 :                : 
    1494         [ -  + ]:            145 :     assert(streaming_);
    1495   [ +  +  -  - ]:            145 :     streaming_cell_.reset(new detail::cell_impl()); // Clean cell state - otherwise it might contain information from the previously streamed cell.
    1496            [ + ]:            145 :     auto cell = xlnt::cell(streaming_cell_.get());
    1497      [ +  +  + ]:            290 :     auto reference = cell_reference(parser().attribute("r"));
    1498                 :            145 :     cell.d_->parent_ = current_worksheet_;
    1499         [ +  + ]:            145 :     cell.d_->column_ = reference.column_index();
    1500            [ + ]:            145 :     cell.d_->row_ = reference.row();
    1501                 :                : 
    1502   [ +  +  -  + ]:            435 :     if (parser().attribute_present("ph"))
    1503                 :                :     {
    1504         [ #  # ]:UBC           0 :         cell.d_->phonetics_visible_ = parser().attribute<bool>("ph");
    1505                 :                :     }
    1506                 :                : 
    1507         [ +  + ]:CBC         290 :     auto has_type = parser().attribute_present("t");
    1508   [ +  -  +  +  :            290 :     auto type = has_type ? parser().attribute("t") : "n";
          +  -  -  +  +  
          -  +  -  -  -  
             -  -  -  - ]
    1509                 :                : 
    1510   [ +  +  +  + ]:            435 :     if (parser().attribute_present("s"))
    1511                 :                :     {
    1512                 :             32 :         size_t s = 0;
    1513      [ +  +  + ]:             64 :         bool ok = detail::parse(parser().attribute("s"), s) == std::errc();
    1514                 :                : 
    1515                 :             32 :         if (!ok)
    1516                 :                :         {
    1517                 :                : #ifdef THROW_ON_INVALID_XML
    1518                 :                :             throw xlnt::invalid_parameter();
    1519                 :                : #endif
    1520                 :                :         }
    1521                 :                : 
    1522         [ +  + ]:             32 :         cell.format(target_.format(s));
    1523                 :                :     }
    1524                 :                : 
    1525                 :            145 :     auto has_value = false;
    1526                 :            145 :     auto value_string = std::string();
    1527                 :            145 :     auto formula_string = std::string();
    1528                 :                : 
    1529   [ +  +  +  +  :           1305 :     while (in_element(qn("spreadsheetml", "c")))
                   +  + ]
    1530                 :                :     {
    1531            [ + ]:            145 :         auto current_element = expect_start_element(xml::content::mixed);
    1532                 :                : 
    1533   [ +  +  +  +  :            580 :         if (current_element == qn("spreadsheetml", "v")) // s:ST_Xstring
                      + ]
    1534                 :                :         {
    1535                 :            144 :             has_value = true;
    1536            [ + ]:            144 :             value_string = read_text();
    1537                 :                :         }
    1538   [ +  +  +  -  :              4 :         else if (current_element == qn("spreadsheetml", "f")) // CT_CellFormula
                      + ]
    1539                 :                :         {
    1540                 :UBC           0 :             auto has_shared_formula = false;
    1541                 :              0 :             auto has_array_formula = false;
    1542                 :              0 :             auto is_master_cell = false;
    1543                 :              0 :             auto shared_formula_index = 0;
    1544            [ # ]:              0 :             auto formula_range = range_reference();
    1545                 :                : 
    1546   [ #  #  #  # ]:              0 :             if (parser().attribute_present("t"))
    1547                 :                :             {
    1548      [ #  #  # ]:              0 :                 auto formula_type = parser().attribute("t");
    1549      [ #  #  # ]:              0 :                 if (formula_type == "shared")
    1550                 :                :                 {
    1551                 :              0 :                     has_shared_formula = true;
    1552         [ #  # ]:              0 :                     shared_formula_index = parser().attribute<int>("si");
    1553   [ #  #  #  # ]:              0 :                     if (parser().attribute_present("ref"))
    1554                 :                :                     {
    1555                 :              0 :                         is_master_cell = true;
    1556                 :                :                     }
    1557                 :                :                 }
    1558      [ #  #  # ]:              0 :                 else if (formula_type == "array")
    1559                 :                :                 {
    1560                 :              0 :                     has_array_formula = true;
    1561      [ #  #  # ]:              0 :                     formula_range = range_reference(parser().attribute("ref"));
    1562                 :              0 :                     is_master_cell = true;
    1563                 :                :                 }
    1564                 :              0 :             }
    1565                 :                : 
    1566         [ #  # ]:              0 :             skip_attributes({"aca", "dt2D", "dtr", "del1", "del2", "r1",
    1567                 :                :                 "r2", "ca", "bx"});
    1568                 :                : 
    1569            [ # ]:              0 :             formula_string = read_text();
    1570                 :                : 
    1571         [ #  # ]:              0 :             if (is_master_cell)
    1572                 :                :             {
    1573         [ #  # ]:              0 :                 if (has_shared_formula)
    1574                 :                :                 {
    1575         [ #  # ]:              0 :                     shared_formulae_[shared_formula_index] = formula_string;
    1576                 :                :                 }
    1577         [ #  # ]:              0 :                 else if (has_array_formula)
    1578                 :                :                 {
    1579      [ #  #  # ]:              0 :                     array_formulae_[formula_range.to_string()] = formula_string;
    1580                 :                :                 }
    1581                 :                :             }
    1582         [ #  # ]:              0 :             else if (has_shared_formula)
    1583                 :                :             {
    1584            [ # ]:              0 :                 auto shared_formula = shared_formulae_.find(shared_formula_index);
    1585         [ #  # ]:              0 :                 if (shared_formula != shared_formulae_.end())
    1586                 :                :                 {
    1587            [ # ]:              0 :                     formula_string = shared_formula->second;
    1588                 :                :                 }
    1589                 :                :             }
    1590                 :                :         }
    1591   [ +  +  +  +  :CBC           4 :         else if (current_element == qn("spreadsheetml", "is")) // CT_Rst
                      - ]
    1592                 :                :         {
    1593   [ +  +  +  + ]:              4 :             expect_start_element(qn("spreadsheetml", "t"), xml::content::simple);
    1594                 :              1 :             has_value = true;
    1595            [ + ]:              1 :             value_string = read_text();
    1596   [ +  +  +  + ]:              4 :             expect_end_element(qn("spreadsheetml", "t"));
    1597                 :                :         }
    1598                 :                :         else
    1599                 :                :         {
    1600            [ # ]:UBC           0 :             unexpected_element(current_element);
    1601                 :                :         }
    1602                 :                : 
    1603            [ + ]:CBC         145 :         expect_end_element(current_element);
    1604                 :            145 :     }
    1605                 :                : 
    1606   [ +  +  +  + ]:            435 :     expect_end_element(qn("spreadsheetml", "c"));
    1607                 :                : 
    1608         [ -  + ]:            145 :     if (!formula_string.empty())
    1609                 :                :     {
    1610            [ # ]:UBC           0 :         cell.formula(formula_string);
    1611                 :                :     }
    1612                 :                : 
    1613         [ +  - ]:CBC         145 :     if (has_value)
    1614                 :                :     {
    1615      [ +  -  + ]:            145 :         if (type == "str")
    1616                 :                :         {
    1617         [ #  # ]:UBC           0 :             cell.d_->value_text_ = value_string;
    1618            [ # ]:              0 :             cell.data_type(cell::type::formula_string);
    1619                 :                :         }
    1620      [ +  +  + ]:CBC         145 :         else if (type == "inlineStr")
    1621                 :                :         {
    1622         [ +  + ]:              1 :             cell.d_->value_text_ = value_string;
    1623            [ + ]:              1 :             cell.data_type(cell::type::inline_string);
    1624                 :                :         }
    1625      [ +  +  - ]:            144 :         else if (type == "s")
    1626                 :                :         {
    1627            [ + ]:            144 :             cell.d_->value_numeric_ = xlnt::detail::deserialise(value_string);
    1628            [ + ]:            144 :             cell.data_type(cell::type::shared_string);
    1629                 :                :         }
    1630      [ #  #  # ]:UBC           0 :         else if (type == "b") // boolean
    1631                 :                :         {
    1632         [ #  # ]:              0 :             cell.value(is_true(value_string));
    1633                 :                :         }
    1634      [ #  #  # ]:              0 :         else if (type == "n") // numeric
    1635                 :                :         {
    1636         [ #  # ]:              0 :             cell.value(xlnt::detail::deserialise(value_string));
    1637                 :                :         }
    1638   [ #  #  #  #  :              0 :         else if (!value_string.empty() && value_string[0] == '#')
                   #  # ]
    1639                 :                :         {
    1640            [ # ]:              0 :             cell.error(value_string);
    1641                 :                :         }
    1642                 :                :     }
    1643                 :                : 
    1644                 :CBC         145 :     return true;
    1645                 :            145 : }
    1646                 :                : 
    1647                 :           1194 : std::vector<relationship> xlsx_consumer::read_relationships(const path &part)
    1648                 :                : {
    1649   [ +  +  +  +  :           5970 :     const auto part_rels_path = part.parent().append("_rels").append(part.filename() + ".rels").relative_to(path("/"));
             +  +  +  +  
                      + ]
    1650                 :                : 
    1651                 :           1194 :     std::vector<xlnt::relationship> relationships;
    1652      [ +  +  + ]:           1194 :     if (!archive_->has_file(part_rels_path)) return relationships;
    1653                 :                : 
    1654            [ + ]:            240 :     auto rels_streambuf = archive_->open(part_rels_path);
    1655            [ + ]:            240 :     std::istream rels_stream(rels_streambuf.get());
    1656         [ +  + ]:            240 :     xml::parser parser(rels_stream, part_rels_path.string());
    1657                 :            240 :     parser_ = &parser;
    1658                 :                : 
    1659   [ +  +  +  + ]:            960 :     expect_start_element(qn("relationships", "Relationships"), xml::content::complex);
    1660                 :                : 
    1661   [ +  +  +  +  :           4376 :     while (in_element(qn("relationships", "Relationships")))
                   +  + ]
    1662                 :                :     {
    1663   [ +  +  +  + ]:           3970 :         expect_start_element(qn("relationships", "Relationship"), xml::content::simple);
    1664                 :                : 
    1665         [ +  + ]:           1588 :         const auto target_mode = parser.attribute_present("TargetMode")
    1666   [ +  +  +  +  :           1648 :             ? parser.attribute<xlnt::target_mode>("TargetMode")
          +  +  -  -  -  
                      - ]
    1667         [ +  + ]:            794 :             : xlnt::target_mode::internal;
    1668      [ +  +  + ]:            794 :         auto target = xlnt::uri(parser.attribute("Target"));
    1669                 :                : 
    1670   [ +  +  +  +  :            794 :         if (target.path().is_absolute() && target_mode == xlnt::target_mode::internal)
             +  -  +  + ]
    1671                 :                :         {
    1672   [ +  +  +  +  :             18 :             target = uri(target.path().relative_to(path(part.string()).resolve(path("/"))).string());
             +  +  +  +  
                      + ]
    1673                 :                :         }
    1674                 :                : 
    1675      [ +  +  + ]:            794 :         relationships.emplace_back(parser.attribute("Id"),
    1676         [ +  + ]:           1588 :             parser.attribute<xlnt::relationship_type>("Type"),
    1677         [ +  + ]:           1588 :             xlnt::uri(part.string()), target, target_mode);
    1678                 :                : 
    1679   [ +  +  +  + ]:           2382 :         expect_end_element(qn("relationships", "Relationship"));
    1680                 :            794 :     }
    1681                 :                : 
    1682   [ +  +  +  + ]:            720 :     expect_end_element(qn("relationships", "Relationships"));
    1683                 :            240 :     parser_ = nullptr;
    1684                 :                : 
    1685                 :            240 :     return relationships;
    1686                 :           1194 : }
    1687                 :                : 
    1688                 :              1 : void xlsx_consumer::read_stylesheet (const std::string& xml)
    1689                 :                : {
    1690            [ + ]:              1 :     std::istringstream part_stream(xml);
    1691         [ +  + ]:              1 :     xml::parser parser(part_stream, "/xl/styles.xml");
    1692                 :              1 :     parser_ = &parser;
    1693            [ + ]:              1 :     read_stylesheet();
    1694                 :              1 :     parser_ = nullptr;
    1695                 :              1 : }
    1696                 :                : 
    1697                 :            677 : void xlsx_consumer::read_part(const std::vector<relationship> &rel_chain)
    1698                 :                : {
    1699            [ + ]:            677 :     const auto &manifest = target_.manifest();
    1700            [ + ]:            677 :     const auto part_path = manifest.canonicalize(rel_chain);
    1701            [ + ]:            677 :     auto part_streambuf = archive_->open(part_path);
    1702            [ + ]:            677 :     std::istream part_stream(part_streambuf.get());
    1703         [ +  + ]:            677 :     xml::parser parser(part_stream, part_path.string());
    1704                 :            677 :     parser_ = &parser;
    1705                 :                : 
    1706   [ +  +  +  +  :            677 :     switch (rel_chain.back().type())
          +  -  -  -  -  
          -  -  +  +  +  
          -  -  +  +  -  
          -  -  -  +  +  
          -  -  -  -  -  
          -  -  -  -  -  
                +  +  - ]
    1707                 :                :     {
    1708                 :             85 :     case relationship_type::core_properties:
    1709            [ + ]:             85 :         read_core_properties();
    1710                 :             85 :         break;
    1711                 :                : 
    1712                 :             85 :     case relationship_type::extended_properties:
    1713            [ + ]:             85 :         read_extended_properties();
    1714                 :             85 :         break;
    1715                 :                : 
    1716                 :              6 :     case relationship_type::custom_properties:
    1717            [ + ]:              6 :         read_custom_properties();
    1718                 :              6 :         break;
    1719                 :                : 
    1720                 :             97 :     case relationship_type::office_document:
    1721         [ +  + ]:             98 :         read_office_document(manifest.content_type(part_path));
    1722                 :             96 :         break;
    1723                 :                : 
    1724                 :UBC           0 :     case relationship_type::connections:
    1725                 :              0 :         read_connections();
    1726                 :              0 :         break;
    1727                 :                : 
    1728                 :              0 :     case relationship_type::custom_xml_mappings:
    1729                 :              0 :         read_custom_xml_mappings();
    1730                 :              0 :         break;
    1731                 :                : 
    1732                 :              0 :     case relationship_type::external_workbook_references:
    1733                 :              0 :         read_external_workbook_references();
    1734                 :              0 :         break;
    1735                 :                : 
    1736                 :              0 :     case relationship_type::pivot_table:
    1737                 :              0 :         read_pivot_table();
    1738                 :              0 :         break;
    1739                 :                : 
    1740                 :              0 :     case relationship_type::shared_workbook_revision_headers:
    1741                 :              0 :         read_shared_workbook_revision_headers();
    1742                 :              0 :         break;
    1743                 :                : 
    1744                 :              0 :     case relationship_type::volatile_dependencies:
    1745                 :              0 :         read_volatile_dependencies();
    1746                 :              0 :         break;
    1747                 :                : 
    1748                 :CBC          66 :     case relationship_type::shared_string_table:
    1749            [ + ]:             66 :         read_shared_string_table();
    1750                 :             66 :         break;
    1751                 :                : 
    1752                 :             85 :     case relationship_type::stylesheet:
    1753            [ + ]:             85 :         read_stylesheet();
    1754                 :             85 :         break;
    1755                 :                : 
    1756                 :             72 :     case relationship_type::theme:
    1757            [ + ]:             72 :         read_theme();
    1758                 :             72 :         break;
    1759                 :                : 
    1760                 :UBC           0 :     case relationship_type::chartsheet:
    1761            [ # ]:              0 :         read_chartsheet(rel_chain.back().id());
    1762                 :              0 :         break;
    1763                 :                : 
    1764                 :              0 :     case relationship_type::dialogsheet:
    1765            [ # ]:              0 :         read_dialogsheet(rel_chain.back().id());
    1766                 :              0 :         break;
    1767                 :                : 
    1768                 :CBC         125 :     case relationship_type::worksheet:
    1769         [ +  + ]:            125 :         read_worksheet(rel_chain.back().id());
    1770                 :            125 :         break;
    1771                 :                : 
    1772                 :             44 :     case relationship_type::thumbnail:
    1773            [ + ]:             44 :         read_image(part_path);
    1774                 :             44 :         break;
    1775                 :                : 
    1776                 :UBC           0 :     case relationship_type::calculation_chain:
    1777                 :              0 :         read_calculation_chain();
    1778                 :              0 :         break;
    1779                 :                : 
    1780                 :              0 :     case relationship_type::hyperlink:
    1781                 :              0 :         break;
    1782                 :                : 
    1783                 :              0 :     case relationship_type::comments:
    1784                 :              0 :         break;
    1785                 :                : 
    1786                 :              0 :     case relationship_type::vml_drawing:
    1787                 :              0 :         break;
    1788                 :                : 
    1789                 :CBC           3 :     case relationship_type::unknown:
    1790                 :              3 :         break;
    1791                 :                : 
    1792                 :              5 :     case relationship_type::printer_settings:
    1793            [ + ]:              5 :         read_binary(part_path);
    1794                 :              5 :         break;
    1795                 :                : 
    1796                 :UBC           0 :     case relationship_type::custom_property:
    1797                 :              0 :         break;
    1798                 :                : 
    1799                 :              0 :     case relationship_type::drawings:
    1800                 :              0 :         break;
    1801                 :                : 
    1802                 :              0 :     case relationship_type::pivot_table_cache_definition:
    1803                 :              0 :         break;
    1804                 :                : 
    1805                 :              0 :     case relationship_type::pivot_table_cache_records:
    1806                 :              0 :         break;
    1807                 :                : 
    1808                 :              0 :     case relationship_type::query_table:
    1809                 :              0 :         break;
    1810                 :                : 
    1811                 :              0 :     case relationship_type::shared_workbook:
    1812                 :              0 :         break;
    1813                 :                : 
    1814                 :              0 :     case relationship_type::revision_log:
    1815                 :              0 :         break;
    1816                 :                : 
    1817                 :              0 :     case relationship_type::shared_workbook_user_data:
    1818                 :              0 :         break;
    1819                 :                : 
    1820                 :              0 :     case relationship_type::single_cell_table_definitions:
    1821                 :              0 :         break;
    1822                 :                : 
    1823                 :              0 :     case relationship_type::table_definition:
    1824                 :              0 :         break;
    1825                 :                : 
    1826                 :CBC           2 :     case relationship_type::vbaproject:
    1827            [ + ]:              2 :         read_binary(part_path);
    1828                 :              2 :         break;
    1829                 :                : 
    1830                 :              2 :     case relationship_type::image:
    1831            [ + ]:              2 :         read_image(part_path);
    1832                 :              2 :         break;
    1833                 :                :     }
    1834                 :                : 
    1835                 :            676 :     parser_ = nullptr;
    1836                 :            680 : }
    1837                 :                : 
    1838                 :             97 : void xlsx_consumer::populate_workbook(bool streaming)
    1839                 :                : {
    1840                 :             97 :     streaming_ = streaming;
    1841                 :                : 
    1842            [ + ]:             97 :     target_.clear();
    1843                 :                : 
    1844            [ + ]:             97 :     read_content_types();
    1845         [ +  + ]:             97 :     const auto root_path = path("/");
    1846                 :                : 
    1847      [ +  +  + ]:            417 :     for (const auto &package_rel : read_relationships(root_path))
    1848                 :                :     {
    1849         [ +  + ]:            320 :         manifest().register_relationship(package_rel);
    1850                 :             97 :     }
    1851                 :                : 
    1852   [ +  +  +  +  :            417 :     for (auto package_rel : manifest().relationships(root_path))
                      + ]
    1853                 :                :     {
    1854      [ +  +  + ]:            320 :         if (package_rel.type() == relationship_type::office_document)
    1855                 :                :         {
    1856                 :                :             // Read the workbook after all the other package parts
    1857                 :             97 :             continue;
    1858                 :                :         }
    1859                 :                : 
    1860   [ +  +  +  +  :            669 :         read_part({package_rel});
                   -  - ]
    1861         [ +  + ]:            417 :     }
    1862                 :                : 
    1863      [ +  +  + ]:           1194 :     for (const auto &relationship_source_string : archive_->files())
    1864                 :                :     {
    1865   [ +  +  +  + ]:           1571 :         for (const auto &part_rel : read_relationships(path(relationship_source_string)))
    1866                 :                :         {
    1867         [ +  + ]:            474 :             manifest().register_relationship(part_rel);
    1868                 :           1097 :         }
    1869                 :             97 :     }
    1870                 :                : 
    1871   [ +  +  +  +  :            292 :     read_part({manifest().relationship(root_path,
                   +  + ]
    1872                 :                :         relationship_type::office_document)});
    1873   [ +  +  +  -  :            418 : }
          -  -  -  +  -  
                   -  + ]
    1874                 :                : 
    1875                 :                : // Package Parts
    1876                 :                : 
    1877                 :             97 : void xlsx_consumer::read_content_types()
    1878                 :                : {
    1879            [ + ]:             97 :     auto &manifest = target_.manifest();
    1880      [ +  +  + ]:            194 :     auto content_types_streambuf = archive_->open(path("[Content_Types].xml"));
    1881            [ + ]:             97 :     std::istream content_types_stream(content_types_streambuf.get());
    1882         [ +  + ]:             97 :     xml::parser parser(content_types_stream, "[Content_Types].xml");
    1883                 :             97 :     parser_ = &parser;
    1884                 :                : 
    1885   [ +  +  +  + ]:            388 :     expect_start_element(qn("content-types", "Types"), xml::content::complex);
    1886                 :                : 
    1887   [ +  +  +  +  :           4293 :     while (in_element(qn("content-types", "Types")))
                   +  + ]
    1888                 :                :     {
    1889            [ + ]:            952 :         auto current_element = expect_start_element(xml::content::complex);
    1890                 :                : 
    1891   [ +  +  +  +  :           3808 :         if (current_element == qn("content-types", "Default"))
                      + ]
    1892                 :                :         {
    1893      [ +  +  + ]:            486 :             auto extension = parser.attribute("Extension");
    1894      [ +  +  + ]:            243 :             auto content_type = parser.attribute("ContentType");
    1895            [ + ]:            243 :             manifest.register_default_type(extension, content_type);
    1896                 :            243 :         }
    1897   [ +  +  +  +  :           2836 :         else if (current_element == qn("content-types", "Override"))
                      - ]
    1898                 :                :         {
    1899      [ +  +  + ]:           1418 :             auto part_name = parser.attribute("PartName");
    1900      [ +  +  + ]:            709 :             auto content_type = parser.attribute("ContentType");
    1901         [ +  + ]:            709 :             manifest.register_override_type(path(part_name), content_type);
    1902                 :            709 :         }
    1903                 :                :         else
    1904                 :                :         {
    1905            [ # ]:UBC           0 :             unexpected_element(current_element);
    1906                 :                :         }
    1907                 :                : 
    1908            [ + ]:CBC         952 :         expect_end_element(current_element);
    1909                 :            952 :     }
    1910                 :                : 
    1911   [ +  +  +  + ]:            291 :     expect_end_element(qn("content-types", "Types"));
    1912                 :             97 : }
    1913                 :                : 
    1914                 :             85 : void xlsx_consumer::read_core_properties()
    1915                 :                : {
    1916                 :                :     //qn("extended-properties", "Properties");
    1917                 :                :     //qn("custom-properties", "Properties");
    1918   [ +  +  +  + ]:            340 :     expect_start_element(qn("core-properties", "coreProperties"), xml::content::complex);
    1919                 :                : 
    1920   [ +  +  +  +  :           2101 :     while (in_element(qn("core-properties", "coreProperties")))
                   +  + ]
    1921                 :                :     {
    1922            [ + ]:            419 :         const auto property_element = expect_start_element(xml::content::simple);
    1923            [ + ]:            419 :         const auto prop = detail::from_string<core_property>(property_element.name());
    1924   [ +  +  +  + ]:            419 :         if (prop == core_property::created || prop == core_property::modified)
    1925                 :                :         {
    1926   [ +  +  +  + ]:            632 :             skip_attribute(qn("xsi", "type"));
    1927                 :                :         }
    1928      [ +  +  + ]:            419 :         target_.core_property(prop, read_text());
    1929            [ + ]:            419 :         expect_end_element(property_element);
    1930                 :            419 :     }
    1931                 :                : 
    1932   [ +  +  +  + ]:            255 :     expect_end_element(qn("core-properties", "coreProperties"));
    1933                 :             85 : }
    1934                 :                : 
    1935                 :             85 : void xlsx_consumer::read_extended_properties()
    1936                 :                : {
    1937   [ +  +  +  + ]:            340 :     expect_start_element(qn("extended-properties", "Properties"), xml::content::complex);
    1938                 :                : 
    1939   [ +  +  +  +  :           3325 :     while (in_element(qn("extended-properties", "Properties")))
                   +  + ]
    1940                 :                :     {
    1941            [ + ]:            725 :         const auto property_element = expect_start_element(xml::content::mixed);
    1942            [ + ]:            725 :         const auto prop = detail::from_string<extended_property>(property_element.name());
    1943         [ +  + ]:            725 :         target_.extended_property(prop, read_variant());
    1944            [ + ]:            725 :         expect_end_element(property_element);
    1945                 :            725 :     }
    1946                 :                : 
    1947   [ +  +  +  + ]:            255 :     expect_end_element(qn("extended-properties", "Properties"));
    1948                 :             85 : }
    1949                 :                : 
    1950                 :              6 : void xlsx_consumer::read_custom_properties()
    1951                 :                : {
    1952   [ +  +  +  + ]:             24 :     expect_start_element(qn("custom-properties", "Properties"), xml::content::complex);
    1953                 :                : 
    1954   [ +  +  +  +  :             58 :     while (in_element(qn("custom-properties", "Properties")))
                   +  + ]
    1955                 :                :     {
    1956            [ + ]:              7 :         const auto property_element = expect_start_element(xml::content::complex);
    1957      [ +  +  + ]:             14 :         const auto prop = parser().attribute("name");
    1958      [ +  +  + ]:             14 :         const auto format_id = parser().attribute("fmtid");
    1959      [ +  +  + ]:             14 :         const auto property_id = parser().attribute("pid");
    1960         [ +  + ]:              7 :         target_.custom_property(prop, read_variant());
    1961            [ + ]:              7 :         expect_end_element(property_element);
    1962                 :              7 :     }
    1963                 :                : 
    1964   [ +  +  +  + ]:             18 :     expect_end_element(qn("custom-properties", "Properties"));
    1965                 :              6 : }
    1966                 :                : 
    1967                 :             97 : void xlsx_consumer::read_office_document(const std::string &content_type) // CT_Workbook
    1968                 :                : {
    1969                 :            194 :     if (content_type !=
    1970                 :                :             "application/vnd."
    1971            [ + ]:             97 :             "openxmlformats-officedocument.spreadsheetml.sheet.main+xml"
    1972                 :              3 :         && content_type !=
    1973                 :                :             "application/vnd."
    1974      [ +  +  - ]:              3 :             "openxmlformats-officedocument.spreadsheetml.template.main+xml"
    1975   [ +  +  +  + ]:            100 :         && content_type !=
    1976                 :                :             "application/vnd."
    1977      [ +  +  + ]:              3 :             "ms-excel.sheet.macroEnabled.main+xml")
    1978                 :                :     {
    1979            [ + ]:              1 :         throw xlnt::invalid_file(content_type);
    1980                 :                :     }
    1981                 :                : 
    1982                 :             96 :     target_.d_->calculation_properties_.clear();
    1983                 :                : 
    1984   [ +  +  +  + ]:            480 :     expect_start_element(qn("workbook", "workbook"), xml::content::complex);
    1985   [ +  +  +  + ]:            288 :     skip_attribute(qn("mc", "Ignorable"));
    1986                 :                : 
    1987   [ +  +  +  +  :           2692 :     while (in_element(qn("workbook", "workbook")))
                   +  + ]
    1988                 :                :     {
    1989            [ + ]:            553 :         auto current_workbook_element = expect_start_element(xml::content::complex);
    1990                 :                : 
    1991   [ +  +  +  +  :           2212 :         if (current_workbook_element == qn("workbook", "fileVersion")) // CT_FileVersion 0-1
                      + ]
    1992                 :                :         {
    1993                 :             78 :             detail::workbook_impl::file_version_t file_version;
    1994                 :                : 
    1995   [ +  +  +  - ]:            234 :             if (parser().attribute_present("appName"))
    1996                 :                :             {
    1997      [ +  +  + ]:            234 :                 file_version.app_name = parser().attribute("appName");
    1998                 :                :             }
    1999                 :                : 
    2000   [ +  +  +  + ]:            234 :             if (parser().attribute_present("lastEdited"))
    2001                 :                :             {
    2002         [ +  + ]:            198 :                 file_version.last_edited = parser().attribute<std::size_t>("lastEdited");
    2003                 :                :             }
    2004                 :                : 
    2005   [ +  +  +  + ]:            234 :             if (parser().attribute_present("lowestEdited"))
    2006                 :                :             {
    2007         [ +  + ]:            198 :                 file_version.lowest_edited = parser().attribute<std::size_t>("lowestEdited");
    2008                 :                :             }
    2009                 :                : 
    2010   [ +  +  +  + ]:            234 :             if (parser().attribute_present("lowestEdited"))
    2011                 :                :             {
    2012         [ +  + ]:            198 :                 file_version.rup_build = parser().attribute<std::size_t>("rupBuild");
    2013                 :                :             }
    2014                 :                : 
    2015         [ +  + ]:             78 :             skip_attribute("codeName");
    2016                 :                : 
    2017            [ + ]:             78 :             target_.d_->file_version_ = file_version;
    2018                 :             78 :         }
    2019   [ +  +  +  -  :           1900 :         else if (current_workbook_element == qn("workbook", "fileSharing")) // CT_FileSharing 0-1
                      + ]
    2020                 :                :         {
    2021            [ # ]:UBC           0 :             skip_remaining_content(current_workbook_element);
    2022                 :                :         }
    2023   [ +  +  +  +  :CBC        1900 :         else if (current_workbook_element == qn("mc", "AlternateContent"))
                      + ]
    2024                 :                :         {
    2025   [ +  +  +  +  :            342 :             while (in_element(qn("mc", "AlternateContent")))
                   +  + ]
    2026                 :                :             {
    2027            [ + ]:             38 :                 auto alternate_content_element = expect_start_element(xml::content::complex);
    2028                 :                : 
    2029   [ +  +  +  -  :            228 :                 if (alternate_content_element == qn("mc", "Choice")
          +  -  +  -  +  
          -  -  -  -  -  
             -  -  -  - ]
    2030   [ +  +  +  -  :            152 :                     && parser().attribute_present("Requires")
          +  -  +  -  -  
                -  -  - ]
    2031   [ +  +  -  +  :            266 :                     && parser().attribute("Requires") == "x15")
          +  +  +  -  +  
          -  +  -  +  -  
             -  -  -  - ]
    2032                 :                :                 {
    2033            [ + ]:             38 :                     auto x15_element = expect_start_element(xml::content::simple);
    2034                 :                : 
    2035   [ +  +  +  +  :            152 :                     if (x15_element == qn("x15ac", "absPath"))
                      - ]
    2036                 :                :                     {
    2037      [ +  +  + ]:            114 :                         target_.d_->abs_path_ = parser().attribute("url");
    2038                 :                :                     }
    2039                 :                : 
    2040            [ + ]:             38 :                     skip_remaining_content(x15_element);
    2041            [ + ]:             38 :                     expect_end_element(x15_element);
    2042                 :             38 :                 }
    2043                 :                : 
    2044            [ + ]:             38 :                 skip_remaining_content(alternate_content_element);
    2045            [ + ]:             38 :                 expect_end_element(alternate_content_element);
    2046                 :             38 :             }
    2047                 :                :         }
    2048   [ +  +  +  +  :           1748 :         else if (current_workbook_element == qn("workbook", "workbookPr")) // CT_WorkbookPr 0-1
                      + ]
    2049                 :                :         {
    2050   [ +  +  +  +  :            389 :             target_.base_date(parser().attribute_present("date1904") // optional, bool=false
          +  +  -  -  -  
                   -  - ]
    2051   [ +  +  +  +  :            111 :                         && is_true(parser().attribute("date1904"))
          +  +  +  +  +  
          +  -  -  -  -  
                      - ]
    2052                 :                :                     ? calendar::mac_1904
    2053                 :                :                     : calendar::windows_1900);
    2054         [ +  + ]:            192 :             skip_attribute("showObjects"); // optional, ST_Objects="all"
    2055         [ +  + ]:            192 :             skip_attribute("showBorderUnselectedTables"); // optional, bool=true
    2056         [ +  + ]:            192 :             skip_attribute("filterPrivacy"); // optional, bool=false
    2057         [ +  + ]:            192 :             skip_attribute("promptedSolutions"); // optional, bool=false
    2058         [ +  + ]:            192 :             skip_attribute("showInkAnnotation"); // optional, bool=true
    2059         [ +  + ]:            192 :             skip_attribute("backupFile"); // optional, bool=false
    2060         [ +  + ]:            192 :             skip_attribute("saveExternalLinkValues"); // optional, bool=true
    2061         [ +  + ]:            192 :             skip_attribute("updateLinks"); // optional, ST_UpdateLinks="userSet"
    2062         [ +  + ]:            192 :             skip_attribute("codeName"); // optional, string
    2063         [ +  + ]:            192 :             skip_attribute("hidePivotFieldList"); // optional, bool=false
    2064         [ +  + ]:            192 :             skip_attribute("showPivotChartFilter"); // optional, bool=false
    2065         [ +  + ]:            192 :             skip_attribute("allowRefreshQuery"); // optional, bool=false
    2066         [ +  + ]:            192 :             skip_attribute("publishItems"); // optional, bool=false
    2067         [ +  + ]:            192 :             skip_attribute("checkCompatibility"); // optional, bool=false
    2068         [ +  + ]:            192 :             skip_attribute("autoCompressPictures"); // optional, bool=true
    2069         [ +  + ]:            192 :             skip_attribute("refreshAllConnections"); // optional, bool=false
    2070         [ +  + ]:            192 :             skip_attribute("defaultThemeVersion"); // optional, uint
    2071         [ +  + ]:            192 :             skip_attribute("dateCompatibility"); // optional, bool (undocumented)
    2072                 :                :         }
    2073   [ +  +  +  +  :           1364 :         else if (current_workbook_element == qn("workbook", "workbookProtection")) // CT_WorkbookProtection 0-1
                      + ]
    2074                 :                :         {
    2075            [ + ]:             15 :             skip_remaining_content(current_workbook_element);
    2076                 :                :         }
    2077   [ +  +  +  +  :           1304 :         else if (current_workbook_element == qn("workbook", "bookViews")) // CT_BookViews 0-1
                      + ]
    2078                 :                :         {
    2079   [ +  +  +  +  :            756 :             while (in_element(qn("workbook", "bookViews")))
                   +  + ]
    2080                 :                :             {
    2081   [ +  +  +  + ]:            336 :                 expect_start_element(qn("workbook", "workbookView"), xml::content::simple);
    2082         [ +  + ]:            168 :                 skip_attributes({"firstSheet", "showHorizontalScroll",
    2083                 :                :                     "showSheetTabs", "showVerticalScroll"});
    2084                 :                : 
    2085                 :             84 :                 workbook_view view;
    2086                 :                : 
    2087   [ +  +  +  + ]:            252 :                 if (parser().attribute_present("xWindow"))
    2088                 :                :                 {
    2089         [ +  + ]:            237 :                     view.x_window = parser().attribute<int>("xWindow");
    2090                 :                :                 }
    2091                 :                : 
    2092   [ +  +  +  + ]:            252 :                 if (parser().attribute_present("yWindow"))
    2093                 :                :                 {
    2094         [ +  + ]:            237 :                     view.y_window = parser().attribute<int>("yWindow");
    2095                 :                :                 }
    2096                 :                : 
    2097   [ +  +  +  + ]:            252 :                 if (parser().attribute_present("windowWidth"))
    2098                 :                :                 {
    2099         [ +  + ]:            240 :                     view.window_width = parser().attribute<std::size_t>("windowWidth");
    2100                 :                :                 }
    2101                 :                : 
    2102   [ +  +  +  + ]:            252 :                 if (parser().attribute_present("windowHeight"))
    2103                 :                :                 {
    2104         [ +  + ]:            240 :                     view.window_height = parser().attribute<std::size_t>("windowHeight");
    2105                 :                :                 }
    2106                 :                : 
    2107   [ +  +  +  + ]:            252 :                 if (parser().attribute_present("tabRatio"))
    2108                 :                :                 {
    2109         [ +  + ]:            183 :                     view.tab_ratio = parser().attribute<std::size_t>("tabRatio");
    2110                 :                :                 }
    2111                 :                : 
    2112   [ +  +  +  + ]:            252 :                 if (parser().attribute_present("activeTab"))
    2113                 :                :                 {
    2114         [ +  + ]:             34 :                     view.active_tab = parser().attribute<std::size_t>("activeTab");
    2115            [ + ]:             17 :                     target_.d_->active_sheet_index_.set(view.active_tab.get());
    2116                 :                :                 }
    2117                 :                : 
    2118            [ + ]:             84 :                 target_.view(view);
    2119                 :                : 
    2120            [ + ]:             84 :                 skip_attributes();
    2121   [ +  +  +  + ]:            252 :                 expect_end_element(qn("workbook", "workbookView"));
    2122                 :             84 :             }
    2123                 :                :         }
    2124   [ +  +  +  +  :            968 :         else if (current_workbook_element == qn("workbook", "sheets")) // CT_Sheets 1
                      + ]
    2125                 :                :         {
    2126                 :             96 :             std::size_t index = 0;
    2127                 :                : 
    2128   [ +  +  +  +  :            992 :             while (in_element(qn("workbook", "sheets")))
                   +  + ]
    2129                 :                :             {
    2130   [ +  +  +  + ]:            512 :                 expect_start_element(qn("spreadsheetml", "sheet"), xml::content::simple);
    2131                 :                : 
    2132      [ +  +  + ]:            256 :                 auto title = parser().attribute("name");
    2133                 :                : 
    2134            [ + ]:            128 :                 sheet_title_index_map_[title] = index++;
    2135      [ +  +  + ]:            256 :                 sheet_title_id_map_[title] = parser().attribute<std::size_t>("sheetId");
    2136   [ +  +  +  +  :            512 :                 target_.d_->sheet_title_rel_id_map_[title] = parser().attribute(qn("r", "id"));
                   +  + ]
    2137                 :                : 
    2138   [ +  +  +  + ]:            512 :                 bool hidden = parser().attribute<std::string>("state", "") == "hidden";
    2139            [ + ]:            128 :                 target_.d_->sheet_hidden_.push_back(hidden);
    2140                 :                : 
    2141   [ +  +  +  + ]:            384 :                 expect_end_element(qn("spreadsheetml", "sheet"));
    2142                 :            128 :             }
    2143                 :                :         }
    2144   [ +  +  +  +  :            584 :         else if (current_workbook_element == qn("workbook", "functionGroups")) // CT_FunctionGroups 0-1
                      + ]
    2145                 :                :         {
    2146            [ + ]:              1 :             skip_remaining_content(current_workbook_element);
    2147                 :                :         }
    2148   [ +  +  +  +  :            580 :         else if (current_workbook_element == qn("workbook", "externalReferences")) // CT_ExternalReferences 0-1
                      + ]
    2149                 :                :         {
    2150            [ + ]:              1 :             skip_remaining_content(current_workbook_element);
    2151                 :                :         }
    2152   [ +  +  +  +  :            576 :         else if (current_workbook_element == qn("workbook", "definedNames")) // CT_DefinedNames 0-1
                      + ]
    2153                 :                :         {
    2154   [ +  +  +  +  :             75 :             while (in_element(qn("workbook", "definedNames")))
                   +  + ]
    2155                 :                :             {
    2156   [ +  +  +  + ]:             40 :                 expect_start_element(qn("spreadsheetml", "definedName"), xml::content::mixed);
    2157                 :                : 
    2158                 :             10 :                 defined_name name;
    2159      [ +  +  + ]:             20 :                 name.name = parser().attribute("name");
    2160   [ +  +  +  + ]:             30 :                 if (parser().attribute_present("localSheetId"))
    2161                 :                :                 {
    2162         [ +  + ]:             27 :                     name.sheet_id = parser().attribute<std::size_t>("localSheetId");
    2163                 :                :                 }
    2164   [ +  +  +  + ]:             30 :                 if (parser().attribute_present("hidden"))
    2165                 :                :                 {
    2166      [ +  +  + ]:              3 :                     name.hidden = is_true(parser().attribute("hidden"));
    2167                 :                :                 }
    2168            [ + ]:             10 :                 parser().attribute_map(); // skip remaining attributes
    2169            [ + ]:             10 :                 name.value = read_text();
    2170            [ + ]:             10 :                 defined_names_.push_back(name);
    2171                 :                : 
    2172   [ +  +  +  + ]:             30 :                 expect_end_element(qn("spreadsheetml", "definedName"));
    2173                 :             10 :             }
    2174                 :                :         }
    2175   [ +  +  +  +  :            548 :         else if (current_workbook_element == qn("workbook", "calcPr")) // CT_CalcPr 0-1
                      + ]
    2176                 :                :         {
    2177                 :             82 :             xlnt::calculation_properties calc_props;
    2178   [ +  +  +  + ]:            246 :             if (parser().attribute_present("calcId"))
    2179                 :                :             {
    2180         [ +  + ]:            207 :                 calc_props.calc_id = parser().attribute<std::size_t>("calcId");
    2181                 :                :             }
    2182   [ +  +  +  + ]:            246 :             if (parser().attribute_present("concurrentCalc"))
    2183                 :                :             {
    2184      [ +  +  + ]:            141 :                 calc_props.concurrent_calc = is_true(parser().attribute("concurrentCalc"));
    2185                 :                :             }
    2186            [ + ]:             82 :             target_.calculation_properties(calc_props);
    2187            [ + ]:             82 :             parser().attribute_map(); // skip remaining
    2188                 :                :         }
    2189   [ +  +  +  -  :            220 :         else if (current_workbook_element == qn("workbook", "oleSize")) // CT_OleSize 0-1
                      + ]
    2190                 :                :         {
    2191            [ # ]:UBC           0 :             skip_remaining_content(current_workbook_element);
    2192                 :                :         }
    2193   [ +  +  +  -  :CBC         220 :         else if (current_workbook_element == qn("workbook", "customWorkbookViews")) // CT_CustomWorkbookViews 0-1
                      + ]
    2194                 :                :         {
    2195            [ # ]:UBC           0 :             skip_remaining_content(current_workbook_element);
    2196                 :                :         }
    2197   [ +  +  +  -  :CBC         220 :         else if (current_workbook_element == qn("workbook", "pivotCaches")) // CT_PivotCaches 0-1
                      + ]
    2198                 :                :         {
    2199            [ # ]:UBC           0 :             skip_remaining_content(current_workbook_element);
    2200                 :                :         }
    2201   [ +  +  +  -  :CBC         220 :         else if (current_workbook_element == qn("workbook", "smartTagPr")) // CT_SmartTagPr 0-1
                      + ]
    2202                 :                :         {
    2203            [ # ]:UBC           0 :             skip_remaining_content(current_workbook_element);
    2204                 :                :         }
    2205   [ +  +  +  -  :CBC         220 :         else if (current_workbook_element == qn("workbook", "smartTagTypes")) // CT_SmartTagTypes 0-1
                      + ]
    2206                 :                :         {
    2207            [ # ]:UBC           0 :             skip_remaining_content(current_workbook_element);
    2208                 :                :         }
    2209   [ +  +  +  -  :CBC         220 :         else if (current_workbook_element == qn("workbook", "webPublishing")) // CT_WebPublishing 0-1
                      + ]
    2210                 :                :         {
    2211            [ # ]:UBC           0 :             skip_remaining_content(current_workbook_element);
    2212                 :                :         }
    2213   [ +  +  +  -  :CBC         220 :         else if (current_workbook_element == qn("workbook", "fileRecoveryPr")) // CT_FileRecoveryPr 0+
                      + ]
    2214                 :                :         {
    2215            [ # ]:UBC           0 :             skip_remaining_content(current_workbook_element);
    2216                 :                :         }
    2217   [ +  +  +  -  :CBC         220 :         else if (current_workbook_element == qn("workbook", "webPublishObjects")) // CT_WebPublishObjects 0-1
                      + ]
    2218                 :                :         {
    2219            [ # ]:UBC           0 :             skip_remaining_content(current_workbook_element);
    2220                 :                :         }
    2221   [ +  +  +  +  :CBC         220 :         else if (current_workbook_element == qn("workbook", "extLst")) // CT_ExtensionList 0-1
                      + ]
    2222                 :                :         {
    2223   [ +  +  +  +  :            404 :             while (in_element(qn("workbook", "extLst")))
                   +  + ]
    2224                 :                :             {
    2225            [ + ]:             46 :                 auto extension_element = expect_start_element(xml::content::complex);
    2226                 :                : 
    2227   [ +  +  +  -  :            276 :                 if (extension_element == qn("workbook", "ext")
          +  -  +  -  +  
          +  -  -  -  -  
             -  -  -  - ]
    2228   [ +  +  +  -  :            184 :                     && parser().attribute_present("uri")
          +  -  +  -  -  
                -  -  - ]
    2229   [ +  +  -  +  :            322 :                     && parser().attribute("uri") == "{7523E5D3-25F3-A5E0-1632-64F254C22452}")
          +  +  +  +  +  
          -  +  -  +  -  
             -  -  -  - ]
    2230                 :                :                 {
    2231            [ + ]:             21 :                     auto arch_id_extension_element = expect_start_element(xml::content::simple);
    2232                 :                : 
    2233   [ +  +  +  +  :             84 :                     if (arch_id_extension_element == qn("mx", "ArchID"))
                      - ]
    2234                 :                :                     {
    2235         [ +  + ]:             63 :                         target_.d_->arch_id_flags_ = parser().attribute<std::size_t>("Flags");
    2236                 :                :                     }
    2237                 :                : 
    2238            [ + ]:             21 :                     skip_remaining_content(arch_id_extension_element);
    2239            [ + ]:             21 :                     expect_end_element(arch_id_extension_element);
    2240                 :             21 :                 }
    2241                 :                : 
    2242            [ + ]:             46 :                 skip_remaining_content(extension_element);
    2243            [ + ]:             46 :                 expect_end_element(extension_element);
    2244                 :             46 :             }
    2245                 :                :         }
    2246                 :                :         else
    2247                 :                :         {
    2248            [ + ]:             11 :             unexpected_element(current_workbook_element);
    2249                 :                :         }
    2250                 :                : 
    2251            [ + ]:            553 :         expect_end_element(current_workbook_element);
    2252                 :            553 :     }
    2253                 :                : 
    2254   [ +  +  +  + ]:            288 :     expect_end_element(qn("workbook", "workbook"));
    2255                 :                : 
    2256   [ +  +  +  + ]:            192 :     auto workbook_rel = manifest().relationship(path("/"), relationship_type::office_document);
    2257      [ +  +  + ]:             96 :     auto workbook_path = workbook_rel.target().path();
    2258                 :                : 
    2259                 :             96 :     const auto rel_types = {
    2260                 :                :         relationship_type::shared_string_table,
    2261                 :                :         relationship_type::stylesheet,
    2262                 :                :         relationship_type::theme,
    2263                 :                :         relationship_type::vbaproject,
    2264                 :             96 :     };
    2265                 :                : 
    2266         [ +  + ]:            480 :     for (auto rel_type : rel_types)
    2267                 :                :     {
    2268   [ +  +  +  + ]:            384 :         if (manifest().has_relationship(workbook_path, rel_type))
    2269                 :                :         {
    2270   [ +  +  +  +  :            900 :             read_part({workbook_rel,
                   -  - ]
    2271                 :            450 :                 manifest().relationship(workbook_path, rel_type)});
    2272                 :                :         }
    2273                 :                :     }
    2274                 :                : 
    2275   [ +  +  +  +  :            224 :     for (auto worksheet_rel : manifest().relationships(workbook_path, relationship_type::worksheet))
                      + ]
    2276                 :                :     {
    2277            [ + ]:            128 :         auto title = std::find_if(target_.d_->sheet_title_rel_id_map_.begin(),
    2278                 :            128 :             target_.d_->sheet_title_rel_id_map_.end(),
    2279                 :            203 :             [&](const std::pair<std::string, std::string> &p) {
    2280                 :            203 :                 return p.second == worksheet_rel.id();
    2281            [ + ]:            128 :             })->first;
    2282                 :                : 
    2283            [ + ]:            128 :         auto id = sheet_title_id_map_[title];
    2284            [ + ]:            128 :         auto index = sheet_title_index_map_[title];
    2285                 :                : 
    2286                 :            128 :         auto insertion_iter = target_.d_->worksheets_.begin();
    2287                 :            147 :         while (insertion_iter != target_.d_->worksheets_.end()
    2288   [ +  +  +  +  :            147 :             && sheet_title_index_map_[insertion_iter->title_] < index)
                +  +  + ]
    2289                 :                :         {
    2290                 :             19 :             ++insertion_iter;
    2291                 :                :         }
    2292                 :                : 
    2293            [ + ]:            128 :         current_worksheet_ = &*target_.d_->worksheets_.emplace(insertion_iter, &target_, id, title);
    2294                 :                : 
    2295         [ +  + ]:            128 :         if (!streaming_)
    2296                 :                :         {
    2297   [ +  +  +  +  :            500 :             read_part({workbook_rel, worksheet_rel});
                   -  - ]
    2298                 :                :         }
    2299                 :            224 :     }
    2300   [ +  +  +  +  :            446 : }
          +  -  -  -  -  
             -  -  -  - ]
    2301                 :                : 
    2302                 :                : // Write Workbook Relationship Target Parts
    2303                 :                : 
    2304                 :UBC           0 : void xlsx_consumer::read_calculation_chain()
    2305                 :                : {
    2306                 :              0 : }
    2307                 :                : 
    2308                 :              0 : void xlsx_consumer::read_chartsheet(const std::string & /*title*/)
    2309                 :                : {
    2310                 :              0 : }
    2311                 :                : 
    2312                 :              0 : void xlsx_consumer::read_connections()
    2313                 :                : {
    2314                 :              0 : }
    2315                 :                : 
    2316                 :              0 : void xlsx_consumer::read_custom_property()
    2317                 :                : {
    2318                 :              0 : }
    2319                 :                : 
    2320                 :              0 : void xlsx_consumer::read_custom_xml_mappings()
    2321                 :                : {
    2322                 :              0 : }
    2323                 :                : 
    2324                 :              0 : void xlsx_consumer::read_dialogsheet(const std::string & /*title*/)
    2325                 :                : {
    2326                 :              0 : }
    2327                 :                : 
    2328                 :              0 : void xlsx_consumer::read_external_workbook_references()
    2329                 :                : {
    2330                 :              0 : }
    2331                 :                : 
    2332                 :              0 : void xlsx_consumer::read_pivot_table()
    2333                 :                : {
    2334                 :              0 : }
    2335                 :                : 
    2336                 :CBC          66 : void xlsx_consumer::read_shared_string_table()
    2337                 :                : {
    2338   [ +  +  +  + ]:            264 :     expect_start_element(qn("spreadsheetml", "sst"), xml::content::complex);
    2339         [ +  + ]:            132 :     skip_attributes({"count"});
    2340                 :                : 
    2341   [ +  +  +  +  :           3186 :     while (in_element(qn("spreadsheetml", "sst")))
                   +  + ]
    2342                 :                :     {
    2343   [ +  +  +  + ]:           3570 :         expect_start_element(qn("spreadsheetml", "si"), xml::content::complex);
    2344   [ +  +  +  + ]:           2142 :         auto rt = read_rich_text(qn("spreadsheetml", "si"));
    2345            [ + ]:            714 :         target_.add_shared_string(rt, true);
    2346   [ +  +  +  + ]:           2142 :         expect_end_element(qn("spreadsheetml", "si"));
    2347                 :            714 :     }
    2348                 :                : 
    2349   [ +  +  +  + ]:            198 :     expect_end_element(qn("spreadsheetml", "sst"));
    2350                 :                : 
    2351                 :                : #ifdef THROW_ON_INVALID_XML
    2352                 :                :     if (parser().attribute_present("uniqueCount"))
    2353                 :                :     {
    2354                 :                :         std::size_t unique_count = parser().attribute<std::size_t>("uniqueCount");
    2355                 :                :         if (unique_count != target_.shared_strings().size())
    2356                 :                :         {
    2357                 :                :             throw invalid_file("sizes don't match");
    2358                 :                :         }
    2359                 :                :     }
    2360                 :                : #endif
    2361                 :             66 : }
    2362                 :                : 
    2363                 :UBC           0 : void xlsx_consumer::read_shared_workbook_revision_headers()
    2364                 :                : {
    2365                 :              0 : }
    2366                 :                : 
    2367                 :              0 : void xlsx_consumer::read_shared_workbook()
    2368                 :                : {
    2369                 :              0 : }
    2370                 :                : 
    2371                 :              0 : void xlsx_consumer::read_shared_workbook_user_data()
    2372                 :                : {
    2373                 :              0 : }
    2374                 :                : 
    2375                 :CBC          86 : void xlsx_consumer::read_stylesheet()
    2376                 :                : {
    2377         [ +  + ]:             86 :     target_.impl().stylesheet_ = detail::stylesheet();
    2378         [ +  + ]:             86 :     auto &stylesheet = target_.impl().stylesheet_.get();
    2379                 :             86 :     stylesheet.garbage_collection_enabled = false; // garbage collection isn't allowed while reading the file, as other parts of the xlsx file reference to the index of a format in the stylesheet. If garbage collection is enabled, all formats will be deleted immediately (before the format usage is read).
    2380                 :                : 
    2381   [ +  +  +  + ]:            344 :     expect_start_element(qn("spreadsheetml", "styleSheet"), xml::content::complex);
    2382   [ +  +  +  +  :            344 :     skip_attributes({qn("mc", "Ignorable")});
             +  +  +  -  
                      - ]
    2383                 :                : 
    2384                 :             86 :     std::vector<std::pair<style_impl, std::size_t>> styles;
    2385                 :             86 :     std::vector<std::pair<format_impl, std::size_t>> format_records;
    2386                 :             86 :     std::vector<std::pair<format_impl, std::size_t>> style_records;
    2387                 :                : 
    2388   [ +  +  +  +  :           3334 :     while (in_element(qn("spreadsheetml", "styleSheet")))
                   +  + ]
    2389                 :                :     {
    2390            [ + ]:            726 :         auto current_style_element = expect_start_element(xml::content::complex);
    2391                 :                : 
    2392   [ +  +  +  +  :           2904 :         if (current_style_element == qn("spreadsheetml", "borders"))
                      + ]
    2393                 :                :         {
    2394                 :             85 :             auto &borders = stylesheet.borders;
    2395                 :             85 :             optional<std::size_t> count;
    2396   [ +  +  +  - ]:            255 :             if (parser().attribute_present("count"))
    2397                 :                :             {
    2398         [ +  + ]:            170 :                 count = parser().attribute<std::size_t>("count");
    2399      [ +  +  + ]:             85 :                 borders.reserve(xlnt::detail::clip_reserve_elements(count.get()));
    2400                 :                :             }
    2401                 :                : 
    2402   [ +  +  +  +  :           1189 :             while (in_element(qn("spreadsheetml", "borders")))
                   +  + ]
    2403                 :                :             {
    2404         [ +  + ]:            191 :                 borders.push_back(xlnt::border());
    2405                 :            191 :                 auto &border = borders.back();
    2406                 :                : 
    2407   [ +  +  +  + ]:            764 :                 expect_start_element(qn("spreadsheetml", "border"), xml::content::complex);
    2408                 :                : 
    2409                 :            191 :                 auto diagonal = diagonal_direction::neither;
    2410                 :                : 
    2411   [ +  +  +  +  :            597 :                 if (parser().attribute_present("diagonalDown") && parser().attribute("diagonalDown") == "1")
          +  +  +  -  +  
          +  +  +  +  +  
          -  +  -  -  +  
          -  -  -  -  -  
                -  -  - ]
    2412                 :                :                 {
    2413                 :UBC           0 :                     diagonal = diagonal_direction::down;
    2414                 :                :                 }
    2415                 :                : 
    2416   [ +  +  +  +  :CBC         597 :                 if (parser().attribute_present("diagonalUp") && parser().attribute("diagonalUp") == "1")
          +  +  +  -  +  
          +  +  +  +  +  
          -  +  -  -  +  
          -  -  -  -  -  
                -  -  - ]
    2417                 :                :                 {
    2418         [ #  # ]:UBC           0 :                     diagonal = diagonal == diagonal_direction::down ? diagonal_direction::both : diagonal_direction::up;
    2419                 :                :                 }
    2420                 :                : 
    2421         [ -  + ]:CBC         191 :                 if (diagonal != diagonal_direction::neither)
    2422                 :                :                 {
    2423            [ # ]:UBC           0 :                     border.diagonal(diagonal);
    2424                 :                :                 }
    2425                 :                : 
    2426   [ +  +  +  +  :CBC        4739 :                 while (in_element(qn("spreadsheetml", "border")))
                   +  + ]
    2427                 :                :                 {
    2428            [ + ]:            946 :                     auto current_side_element = expect_start_element(xml::content::complex);
    2429                 :                : 
    2430                 :            946 :                     xlnt::border::border_property side;
    2431                 :                : 
    2432   [ +  +  +  + ]:           2838 :                     if (parser().attribute_present("style"))
    2433                 :                :                     {
    2434      [ +  +  + ]:            840 :                         side.style(parser().attribute<xlnt::border_style>("style"));
    2435                 :                :                     }
    2436                 :                : 
    2437      [ +  +  + ]:            946 :                     if (in_element(current_side_element))
    2438                 :                :                     {
    2439   [ +  +  +  + ]:           1116 :                         expect_start_element(qn("spreadsheetml", "color"), xml::content::complex);
    2440         [ +  + ]:            279 :                         side.color(read_color());
    2441   [ +  +  +  + ]:           1116 :                         expect_end_element(qn("spreadsheetml", "color"));
    2442                 :                :                     }
    2443                 :                : 
    2444            [ + ]:            946 :                     expect_end_element(current_side_element);
    2445                 :                : 
    2446         [ +  + ]:            946 :                     auto side_type = xml::value_traits<xlnt::border_side>::parse(current_side_element.name(), parser());
    2447            [ + ]:            946 :                     border.side(side_type, side);
    2448                 :            946 :                 }
    2449                 :                : 
    2450   [ +  +  +  + ]:            764 :                 expect_end_element(qn("spreadsheetml", "border"));
    2451                 :                :             }
    2452                 :                : 
    2453                 :                : #ifdef THROW_ON_INVALID_XML
    2454                 :                :             if (count.is_set() && count != borders.size())
    2455                 :                :             {
    2456                 :                :                 throw xlnt::exception("border counts don't match");
    2457                 :                :             }
    2458                 :                : #endif
    2459                 :             85 :         }
    2460   [ +  +  +  +  :           2564 :         else if (current_style_element == qn("spreadsheetml", "fills"))
                      + ]
    2461                 :                :         {
    2462                 :             86 :             auto &fills = stylesheet.fills;
    2463                 :             86 :             optional<std::size_t> count;
    2464   [ +  +  +  - ]:            258 :             if (parser().attribute_present("count"))
    2465                 :                :             {
    2466         [ +  + ]:            172 :                 count = parser().attribute<std::size_t>("count");
    2467      [ +  +  + ]:             86 :                 fills.reserve(xlnt::detail::clip_reserve_elements(count.get()));
    2468                 :                :             }
    2469                 :                : 
    2470   [ +  +  +  +  :           2386 :             while (in_element(qn("spreadsheetml", "fills")))
                   +  + ]
    2471                 :                :             {
    2472         [ +  + ]:            489 :                 fills.push_back(xlnt::fill());
    2473                 :            489 :                 auto &new_fill = fills.back();
    2474                 :                : 
    2475   [ +  +  +  + ]:           2445 :                 expect_start_element(qn("spreadsheetml", "fill"), xml::content::complex);
    2476                 :                : 
    2477   [ +  +  +  +  :           1956 :                 if (in_element(qn("spreadsheetml", "fill")))
                   +  + ]
    2478                 :                :                 {
    2479                 :                : 
    2480            [ + ]:            488 :                     auto fill_element = expect_start_element(xml::content::complex);
    2481                 :                : 
    2482   [ +  +  +  +  :           1952 :                     if (fill_element == qn("spreadsheetml", "patternFill"))
                      + ]
    2483                 :                :                     {
    2484            [ + ]:            487 :                         xlnt::pattern_fill pattern;
    2485                 :                : 
    2486   [ +  +  +  + ]:           1461 :                         if (parser().attribute_present("patternType"))
    2487                 :                :                         {
    2488      [ +  +  + ]:            968 :                             pattern.type(parser().attribute<xlnt::pattern_fill_type>("patternType"));
    2489                 :                : 
    2490   [ +  +  +  +  :           4636 :                             while (in_element(qn("spreadsheetml", "patternFill")))
                   +  + ]
    2491                 :                :                             {
    2492            [ + ]:            554 :                                 auto pattern_type_element = expect_start_element(xml::content::complex);
    2493                 :                : 
    2494   [ +  +  +  +  :           2216 :                                 if (pattern_type_element == qn("spreadsheetml", "fgColor"))
                      + ]
    2495                 :                :                                 {
    2496         [ +  + ]:            317 :                                     pattern.foreground(read_color());
    2497                 :                :                                 }
    2498   [ +  +  +  +  :            948 :                                 else if (pattern_type_element == qn("spreadsheetml", "bgColor"))
                      - ]
    2499                 :                :                                 {
    2500         [ +  + ]:            237 :                                     pattern.background(read_color());
    2501                 :                :                                 }
    2502                 :                :                                 else
    2503                 :                :                                 {
    2504            [ # ]:UBC           0 :                                     unexpected_element(pattern_type_element);
    2505                 :                :                                 }
    2506                 :                : 
    2507            [ + ]:CBC         554 :                                 expect_end_element(pattern_type_element);
    2508                 :            554 :                             }
    2509                 :                :                         }
    2510                 :                : 
    2511            [ + ]:            487 :                         new_fill = pattern;
    2512                 :            487 :                     }
    2513   [ +  +  +  +  :              4 :                     else if (fill_element == qn("spreadsheetml", "gradientFill"))
                      - ]
    2514                 :                :                     {
    2515            [ + ]:              1 :                         xlnt::gradient_fill gradient;
    2516                 :                : 
    2517   [ +  +  -  + ]:              3 :                         if (parser().attribute_present("type"))
    2518                 :                :                         {
    2519      [ #  #  # ]:UBC           0 :                             gradient.type(parser().attribute<xlnt::gradient_fill_type>("type"));
    2520                 :                :                         }
    2521                 :                :                         else
    2522                 :                :                         {
    2523            [ + ]:CBC           1 :                             gradient.type(xlnt::gradient_fill_type::linear);
    2524                 :                :                         }
    2525                 :                : 
    2526   [ +  +  +  +  :              5 :                         while (in_element(qn("spreadsheetml", "gradientFill")))
                   -  + ]
    2527                 :                :                         {
    2528   [ #  #  #  # ]:UBC           0 :                             expect_start_element(qn("spreadsheetml", "stop"), xml::content::complex);
    2529      [ #  #  # ]:              0 :                             auto position = xlnt::detail::deserialise(parser().attribute("position"));
    2530   [ #  #  #  # ]:              0 :                             expect_start_element(qn("spreadsheetml", "color"), xml::content::complex);
    2531            [ # ]:              0 :                             auto color = read_color();
    2532   [ #  #  #  # ]:              0 :                             expect_end_element(qn("spreadsheetml", "color"));
    2533   [ #  #  #  # ]:              0 :                             expect_end_element(qn("spreadsheetml", "stop"));
    2534                 :                : 
    2535            [ # ]:              0 :                             gradient.add_stop(position, color);
    2536                 :              0 :                         }
    2537                 :                : 
    2538            [ + ]:CBC           1 :                         new_fill = gradient;
    2539                 :              1 :                     }
    2540                 :                :                     else
    2541                 :                :                     {
    2542            [ # ]:UBC           0 :                         unexpected_element(fill_element);
    2543                 :                :                     }
    2544                 :                : 
    2545            [ + ]:CBC         488 :                     expect_end_element(fill_element);
    2546                 :            488 :                 }
    2547                 :                : 
    2548   [ +  +  +  + ]:           1956 :                 expect_end_element(qn("spreadsheetml", "fill"));
    2549                 :                :             }
    2550                 :                : 
    2551                 :                : #ifdef THROW_ON_INVALID_XML
    2552                 :                :             if (count.is_set() && count != fills.size())
    2553                 :                :             {
    2554                 :                :                 throw xlnt::exception("counts don't match");
    2555                 :                :             }
    2556                 :                : #endif
    2557                 :             86 :         }
    2558   [ +  +  +  +  :           2220 :         else if (current_style_element == qn("spreadsheetml", "fonts"))
                      + ]
    2559                 :                :         {
    2560                 :             85 :             auto &fonts = stylesheet.fonts;
    2561                 :             85 :             optional<std::size_t> count;
    2562   [ +  +  +  - ]:            255 :             if (parser().attribute_present("count"))
    2563                 :                :             {
    2564         [ +  + ]:            170 :                 count = parser().attribute<std::size_t>("count");
    2565      [ +  +  + ]:             85 :                 fonts.reserve(xlnt::detail::clip_reserve_elements(count.get()));
    2566                 :                :             }
    2567                 :                : 
    2568   [ +  +  +  +  :            425 :             if (parser().attribute_present(qn("x14ac", "knownFonts")))
                   +  + ]
    2569                 :                :             {
    2570            [ + ]:             46 :                 target_.enable_known_fonts();
    2571                 :                :             }
    2572                 :                : 
    2573   [ +  +  +  +  :           2577 :             while (in_element(qn("spreadsheetml", "fonts")))
                   +  + ]
    2574                 :                :             {
    2575         [ +  + ]:            538 :                 fonts.push_back(xlnt::font());
    2576                 :            538 :                 auto &new_font = stylesheet.fonts.back();
    2577                 :                : 
    2578   [ +  +  +  + ]:           2152 :                 expect_start_element(qn("spreadsheetml", "font"), xml::content::complex);
    2579                 :                : 
    2580   [ +  +  +  +  :          13818 :                 while (in_element(qn("spreadsheetml", "font")))
                   +  + ]
    2581                 :                :                 {
    2582            [ + ]:           2782 :                     auto font_property_element = expect_start_element(xml::content::simple);
    2583                 :                : 
    2584   [ +  +  +  +  :          11128 :                     if (font_property_element == qn("spreadsheetml", "sz"))
                      + ]
    2585                 :                :                     {
    2586   [ +  +  +  + ]:           1611 :                         new_font.size(xlnt::detail::deserialise(parser().attribute("val")));
    2587                 :                :                     }
    2588   [ +  +  +  +  :           8980 :                     else if (font_property_element == qn("spreadsheetml", "name"))
                      + ]
    2589                 :                :                     {
    2590      [ +  +  + ]:           1614 :                         new_font.name(parser().attribute("val"));
    2591                 :                :                     }
    2592   [ +  +  +  +  :           6828 :                     else if (font_property_element == qn("spreadsheetml", "color"))
                      + ]
    2593                 :                :                     {
    2594         [ +  + ]:            475 :                         new_font.color(read_color());
    2595                 :                :                     }
    2596   [ +  +  +  +  :           4928 :                     else if (font_property_element == qn("spreadsheetml", "family"))
                      + ]
    2597                 :                :                     {
    2598      [ +  +  + ]:           1317 :                         new_font.family(parser().attribute<std::size_t>("val"));
    2599                 :                :                     }
    2600   [ +  +  +  +  :           3172 :                     else if (font_property_element == qn("spreadsheetml", "scheme"))
                      + ]
    2601                 :                :                     {
    2602      [ +  +  + ]:           1245 :                         new_font.scheme(parser().attribute("val"));
    2603                 :                :                     }
    2604   [ +  +  +  +  :           1512 :                     else if (font_property_element == qn("spreadsheetml", "b"))
                      + ]
    2605                 :                :                     {
    2606   [ +  +  +  + ]:            366 :                         if (parser().attribute_present("val"))
    2607                 :                :                         {
    2608   [ +  +  +  + ]:             27 :                             new_font.bold(is_true(parser().attribute("val")));
    2609                 :                :                         }
    2610                 :                :                         else
    2611                 :                :                         {
    2612            [ + ]:            113 :                             new_font.bold(true);
    2613                 :                :                         }
    2614                 :                :                     }
    2615   [ +  +  +  +  :           1024 :                     else if (font_property_element == qn("spreadsheetml", "vertAlign"))
                      + ]
    2616                 :                :                     {
    2617      [ +  +  + ]:             54 :                         auto vert_align = parser().attribute("val");
    2618                 :                : 
    2619      [ +  +  + ]:             27 :                         if (vert_align == "superscript")
    2620                 :                :                         {
    2621            [ + ]:              9 :                             new_font.superscript(true);
    2622                 :                :                         }
    2623      [ +  +  - ]:             18 :                         else if (vert_align == "subscript")
    2624                 :                :                         {
    2625            [ + ]:             18 :                             new_font.subscript(true);
    2626                 :                :                         }
    2627                 :             27 :                     }
    2628   [ +  +  +  +  :            916 :                     else if (font_property_element == qn("spreadsheetml", "strike"))
                      + ]
    2629                 :                :                     {
    2630   [ +  +  -  + ]:             60 :                         if (parser().attribute_present("val"))
    2631                 :                :                         {
    2632   [ #  #  #  # ]:UBC           0 :                             new_font.strikethrough(is_true(parser().attribute("val")));
    2633                 :                :                         }
    2634                 :                :                         else
    2635                 :                :                         {
    2636            [ + ]:CBC          20 :                             new_font.strikethrough(true);
    2637                 :                :                         }
    2638                 :                :                     }
    2639   [ +  +  +  -  :            836 :                     else if (font_property_element == qn("spreadsheetml", "outline"))
                      + ]
    2640                 :                :                     {
    2641   [ #  #  #  # ]:UBC           0 :                         if (parser().attribute_present("val"))
    2642                 :                :                         {
    2643   [ #  #  #  # ]:              0 :                             new_font.outline(is_true(parser().attribute("val")));
    2644                 :                :                         }
    2645                 :                :                         else
    2646                 :                :                         {
    2647            [ # ]:              0 :                             new_font.outline(true);
    2648                 :                :                         }
    2649                 :                :                     }
    2650   [ +  +  +  -  :CBC         836 :                     else if (font_property_element == qn("spreadsheetml", "shadow"))
                      + ]
    2651                 :                :                     {
    2652   [ #  #  #  # ]:UBC           0 :                         if (parser().attribute_present("val"))
    2653                 :                :                         {
    2654   [ #  #  #  # ]:              0 :                             new_font.shadow(is_true(parser().attribute("val")));
    2655                 :                :                         }
    2656                 :                :                         else
    2657                 :                :                         {
    2658            [ # ]:              0 :                             new_font.shadow(true);
    2659                 :                :                         }
    2660                 :                :                     }
    2661   [ +  +  +  +  :CBC         836 :                     else if (font_property_element == qn("spreadsheetml", "i"))
                      + ]
    2662                 :                :                     {
    2663   [ +  +  -  + ]:            138 :                         if (parser().attribute_present("val"))
    2664                 :                :                         {
    2665   [ #  #  #  # ]:UBC           0 :                             new_font.italic(is_true(parser().attribute("val")));
    2666                 :                :                         }
    2667                 :                :                         else
    2668                 :                :                         {
    2669            [ + ]:CBC          46 :                             new_font.italic(true);
    2670                 :                :                         }
    2671                 :                :                     }
    2672   [ +  +  +  +  :            652 :                     else if (font_property_element == qn("spreadsheetml", "u"))
                      + ]
    2673                 :                :                     {
    2674   [ +  +  +  + ]:            204 :                         if (parser().attribute_present("val"))
    2675                 :                :                         {
    2676      [ +  +  + ]:            132 :                             new_font.underline(parser().attribute<xlnt::font::underline_style>("val"));
    2677                 :                :                         }
    2678                 :                :                         else
    2679                 :                :                         {
    2680            [ + ]:             24 :                             new_font.underline(xlnt::font::underline_style::single);
    2681                 :                :                         }
    2682                 :                :                     }
    2683   [ +  +  +  +  :            380 :                     else if (font_property_element == qn("spreadsheetml", "charset"))
                      - ]
    2684                 :                :                     {
    2685   [ +  +  +  - ]:            285 :                         if (parser().attribute_present("val"))
    2686                 :                :                         {
    2687         [ +  + ]:            285 :                             parser().attribute("val");
    2688                 :                :                         }
    2689                 :                :                     }
    2690                 :                :                     else
    2691                 :                :                     {
    2692            [ # ]:UBC           0 :                         unexpected_element(font_property_element);
    2693                 :                :                     }
    2694                 :                : 
    2695            [ + ]:CBC        2782 :                     expect_end_element(font_property_element);
    2696                 :           2782 :                 }
    2697                 :                : 
    2698   [ +  +  +  + ]:           2152 :                 expect_end_element(qn("spreadsheetml", "font"));
    2699                 :                :             }
    2700                 :                : 
    2701                 :                : #ifdef THROW_ON_INVALID_XML
    2702                 :                :             if (count.is_set() && count != stylesheet.fonts.size())
    2703                 :                :             {
    2704                 :                :                 throw xlnt::exception("counts don't match");
    2705                 :                :             }
    2706                 :                : #endif
    2707                 :             85 :         }
    2708   [ +  +  +  +  :           1880 :         else if (current_style_element == qn("spreadsheetml", "numFmts"))
                      + ]
    2709                 :                :         {
    2710                 :             19 :             auto &number_formats = stylesheet.number_formats;
    2711                 :             19 :             optional<std::size_t> count;
    2712   [ +  +  +  - ]:             57 :             if (parser().attribute_present("count"))
    2713                 :                :             {
    2714         [ +  + ]:             38 :                 count = parser().attribute<std::size_t>("count");
    2715      [ +  +  + ]:             19 :                 number_formats.reserve(xlnt::detail::clip_reserve_elements(count.get()));
    2716                 :                :             }
    2717                 :                : 
    2718   [ +  +  +  +  :            223 :             while (in_element(qn("spreadsheetml", "numFmts")))
                   +  + ]
    2719                 :                :             {
    2720   [ +  +  +  + ]:            128 :                 expect_start_element(qn("spreadsheetml", "numFmt"), xml::content::simple);
    2721                 :                : 
    2722      [ +  +  + ]:             64 :                 auto format_string = parser().attribute("formatCode");
    2723                 :                : 
    2724      [ +  -  + ]:             32 :                 if (format_string == "GENERAL")
    2725                 :                :                 {
    2726            [ # ]:UBC           0 :                     format_string = "General";
    2727                 :                :                 }
    2728                 :                : 
    2729            [ + ]:CBC          32 :                 xlnt::number_format nf;
    2730                 :                : 
    2731            [ + ]:             32 :                 nf.format_string(format_string);
    2732      [ +  +  + ]:             96 :                 nf.id(parser().attribute<std::size_t>("numFmtId"));
    2733                 :                : 
    2734   [ +  +  +  + ]:             96 :                 expect_end_element(qn("spreadsheetml", "numFmt"));
    2735                 :                : 
    2736            [ + ]:             32 :                 number_formats.push_back(nf);
    2737                 :             32 :             }
    2738                 :                : 
    2739                 :                : #ifdef THROW_ON_INVALID_XML
    2740                 :                :             if (count.is_set() && count != number_formats.size())
    2741                 :                :             {
    2742                 :                :                 throw xlnt::exception("counts don't match");
    2743                 :                :             }
    2744                 :                : #endif
    2745                 :             19 :         }
    2746   [ +  +  +  +  :           1804 :         else if (current_style_element == qn("spreadsheetml", "cellStyles"))
                      + ]
    2747                 :                :         {
    2748                 :             84 :             optional<std::size_t> count;
    2749   [ +  +  +  - ]:            252 :             if (parser().attribute_present("count"))
    2750                 :                :             {
    2751         [ +  + ]:            168 :                 count = parser().attribute<std::size_t>("count");
    2752      [ +  +  + ]:             84 :                 styles.reserve(xlnt::detail::clip_reserve_elements(count.get()));
    2753                 :                :             }
    2754                 :                : 
    2755   [ +  +  +  +  :           3144 :             while (in_element(qn("spreadsheetml", "cellStyles")))
                   +  + ]
    2756                 :                :             {
    2757            [ + ]:            681 :                 auto &data = *styles.emplace(styles.end());
    2758                 :                : 
    2759   [ +  +  +  + ]:           2724 :                 expect_start_element(qn("spreadsheetml", "cellStyle"), xml::content::simple);
    2760                 :                : 
    2761      [ +  +  + ]:           1362 :                 data.first.name = parser().attribute("name");
    2762         [ +  + ]:           1362 :                 data.second = parser().attribute<std::size_t>("xfId");
    2763                 :                : 
    2764   [ +  +  +  + ]:           2043 :                 if (parser().attribute_present("builtinId"))
    2765                 :                :                 {
    2766         [ +  + ]:           1701 :                     data.first.builtin_id = parser().attribute<std::size_t>("builtinId");
    2767                 :                :                 }
    2768                 :                : 
    2769   [ +  +  +  + ]:           2043 :                 if (parser().attribute_present("hidden"))
    2770                 :                :                 {
    2771      [ +  +  + ]:              9 :                     data.first.hidden_style = is_true(parser().attribute("hidden"));
    2772                 :                :                 }
    2773                 :                : 
    2774   [ +  +  +  + ]:           2043 :                 if (parser().attribute_present("customBuiltin"))
    2775                 :                :                 {
    2776      [ +  +  + ]:           1281 :                     data.first.custom_builtin = is_true(parser().attribute("customBuiltin"));
    2777                 :                :                 }
    2778                 :                : 
    2779   [ +  +  +  + ]:           2724 :                 expect_end_element(qn("spreadsheetml", "cellStyle"));
    2780                 :                :             }
    2781                 :                : 
    2782                 :                : #ifdef THROW_ON_INVALID_XML
    2783                 :                :             if (count.is_set() && count != styles.size())
    2784                 :                :             {
    2785                 :                :                 throw xlnt::exception("counts don't match");
    2786                 :                :             }
    2787                 :                : #endif
    2788                 :             84 :         }
    2789   [ +  +  +  -  :           2202 :         else if (current_style_element == qn("spreadsheetml", "cellStyleXfs")
          +  -  +  -  +  
          +  -  -  -  -  
             -  -  -  - ]
    2790   [ +  +  +  +  :           2596 :             || current_style_element == qn("spreadsheetml", "cellXfs"))
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  -  -  
          -  -  -  -  -  
                   -  - ]
    2791                 :                :         {
    2792            [ + ]:            170 :             auto in_style_records = current_style_element.name() == "cellStyleXfs";
    2793                 :            170 :             optional<std::size_t> count;
    2794   [ +  +  +  - ]:            510 :             if (parser().attribute_present("count"))
    2795                 :                :             {
    2796         [ +  + ]:            340 :                 count = parser().attribute<std::size_t>("count");
    2797         [ +  + ]:            170 :                 if (in_style_records)
    2798                 :                :                 {
    2799      [ +  +  + ]:             85 :                     style_records.reserve(xlnt::detail::clip_reserve_elements(count.get()));
    2800                 :                :                 }
    2801                 :                :                 else
    2802                 :                :                 {
    2803      [ +  +  + ]:             85 :                     format_records.reserve(xlnt::detail::clip_reserve_elements(count.get()));
    2804                 :                :                 }
    2805                 :                :             }
    2806                 :                : 
    2807      [ +  +  + ]:           1459 :             while (in_element(current_style_element))
    2808                 :                :             {
    2809   [ +  +  +  + ]:           5156 :                 expect_start_element(qn("spreadsheetml", "xf"), xml::content::complex);
    2810                 :                : 
    2811                 :           1289 :                 auto &record = *(!in_style_records
    2812      [ +  +  + ]:           1289 :                         ? format_records.emplace(format_records.end())
    2813            [ + ]:           1289 :                         : style_records.emplace(style_records.end()));
    2814                 :                : 
    2815   [ +  +  +  + ]:           3867 :                 if (parser().attribute_present("applyBorder"))
    2816                 :                :                 {
    2817      [ +  +  + ]:           2097 :                     record.first.border_applied = is_true(parser().attribute("applyBorder"));
    2818                 :                :                 }
    2819         [ +  + ]:           2578 :                 record.first.border_id = parser().attribute_present("borderId")
    2820   [ +  +  +  +  :           5124 :                     ? parser().attribute<std::size_t>("borderId")
          +  +  +  +  -  
                -  -  - ]
    2821                 :           1289 :                     : optional<std::size_t>();
    2822                 :                : 
    2823   [ +  +  +  + ]:           3867 :                 if (parser().attribute_present("applyFill"))
    2824                 :                :                 {
    2825      [ +  +  + ]:            486 :                     record.first.fill_applied = is_true(parser().attribute("applyFill"));
    2826                 :                :                 }
    2827         [ +  + ]:           2578 :                 record.first.fill_id = parser().attribute_present("fillId")
    2828   [ +  +  +  +  :           5110 :                     ? parser().attribute<std::size_t>("fillId")
          +  +  +  +  -  
                -  -  - ]
    2829                 :           1289 :                     : optional<std::size_t>();
    2830                 :                : 
    2831   [ +  +  +  + ]:           3867 :                 if (parser().attribute_present("applyFont"))
    2832                 :                :                 {
    2833      [ +  +  + ]:           1572 :                     record.first.font_applied = is_true(parser().attribute("applyFont"));
    2834                 :                :                 }
    2835         [ +  + ]:           2578 :                 record.first.font_id = parser().attribute_present("fontId")
    2836   [ +  +  +  +  :           5154 :                     ? parser().attribute<std::size_t>("fontId")
          +  +  +  +  -  
                -  -  - ]
    2837                 :           1289 :                     : optional<std::size_t>();
    2838                 :                : 
    2839   [ +  +  +  + ]:           3867 :                 if (parser().attribute_present("applyNumberFormat"))
    2840                 :                :                 {
    2841      [ +  +  + ]:           1665 :                     record.first.number_format_applied = is_true(parser().attribute("applyNumberFormat"));
    2842                 :                :                 }
    2843         [ +  + ]:           2578 :                 record.first.number_format_id = parser().attribute_present("numFmtId")
    2844   [ +  +  +  +  :           5140 :                     ? parser().attribute<std::size_t>("numFmtId")
          +  +  +  +  -  
                -  -  - ]
    2845                 :           1289 :                     : optional<std::size_t>();
    2846                 :                : 
    2847         [ +  + ]:           2578 :                 auto apply_alignment_present = parser().attribute_present("applyAlignment");
    2848         [ +  + ]:           1289 :                 if (apply_alignment_present)
    2849                 :                :                 {
    2850      [ +  +  + ]:           2499 :                     record.first.alignment_applied = is_true(parser().attribute("applyAlignment"));
    2851                 :                :                 }
    2852                 :                : 
    2853         [ +  + ]:           2578 :                 auto apply_protection_present = parser().attribute_present("applyProtection");
    2854         [ +  + ]:           1289 :                 if (apply_protection_present)
    2855                 :                :                 {
    2856      [ +  +  + ]:           1992 :                     record.first.protection_applied = is_true(parser().attribute("applyProtection"));
    2857                 :                :                 }
    2858                 :                : 
    2859   [ +  +  -  -  :           2578 :                 record.first.pivot_button_ = parser().attribute_present("pivotButton")
                -  -  - ]
    2860   [ +  +  +  +  :           2584 :                     && is_true(parser().attribute("pivotButton"));
          +  +  -  +  +  
          +  +  +  +  -  
             -  -  -  - ]
    2861   [ +  +  -  -  :           2578 :                 record.first.quote_prefix_ = parser().attribute_present("quotePrefix")
                -  -  - ]
    2862   [ +  +  +  +  :           2584 :                     && is_true(parser().attribute("quotePrefix"));
          +  +  -  +  +  
          +  +  +  +  -  
             -  -  -  - ]
    2863                 :                : 
    2864   [ +  +  +  + ]:           3867 :                 if (parser().attribute_present("xfId"))
    2865                 :                :                 {
    2866         [ +  + ]:           1248 :                     record.second = parser().attribute<std::size_t>("xfId");
    2867                 :                :                 }
    2868                 :                : 
    2869   [ +  +  +  +  :           8689 :                 while (in_element(qn("spreadsheetml", "xf")))
                   +  + ]
    2870                 :                :                 {
    2871            [ + ]:            561 :                     auto xf_child_element = expect_start_element(xml::content::simple);
    2872                 :                : 
    2873   [ +  +  +  +  :           2244 :                     if (xf_child_element == qn("spreadsheetml", "alignment"))
                      + ]
    2874                 :                :                     {
    2875                 :            532 :                         record.first.alignment_id = stylesheet.alignments.size();
    2876            [ + ]:            532 :                         auto &alignment = *stylesheet.alignments.emplace(stylesheet.alignments.end());
    2877                 :                : 
    2878   [ +  +  +  + ]:           1596 :                         if (parser().attribute_present("wrapText"))
    2879                 :                :                         {
    2880   [ +  +  +  + ]:            291 :                             alignment.wrap(is_true(parser().attribute("wrapText")));
    2881                 :                :                         }
    2882                 :                : 
    2883   [ +  +  +  + ]:           1596 :                         if (parser().attribute_present("shrinkToFit"))
    2884                 :                :                         {
    2885   [ +  +  +  + ]:            129 :                             alignment.shrink(is_true(parser().attribute("shrinkToFit")));
    2886                 :                :                         }
    2887                 :                : 
    2888   [ +  +  +  + ]:           1596 :                         if (parser().attribute_present("indent"))
    2889                 :                :                         {
    2890      [ +  +  + ]:            126 :                             alignment.indent(parser().attribute<int>("indent"));
    2891                 :                :                         }
    2892                 :                : 
    2893   [ +  +  +  + ]:           1596 :                         if (parser().attribute_present("textRotation"))
    2894                 :                :                         {
    2895      [ +  +  + ]:             99 :                             alignment.rotation(parser().attribute<int>("textRotation"));
    2896                 :                :                         }
    2897                 :                : 
    2898   [ +  +  +  + ]:           1596 :                         if (parser().attribute_present("vertical"))
    2899                 :                :                         {
    2900      [ +  +  + ]:           1371 :                             alignment.vertical(parser().attribute<xlnt::vertical_alignment>("vertical"));
    2901                 :                :                         }
    2902                 :                : 
    2903   [ +  +  +  + ]:           1596 :                         if (parser().attribute_present("horizontal"))
    2904                 :                :                         {
    2905      [ +  +  + ]:            348 :                             alignment.horizontal(parser().attribute<xlnt::horizontal_alignment>("horizontal"));
    2906                 :                :                         }
    2907                 :                : 
    2908   [ +  +  +  + ]:           1596 :                         if (parser().attribute_present("readingOrder"))
    2909                 :                :                         {
    2910         [ +  + ]:              3 :                             parser().attribute<int>("readingOrder");
    2911                 :                :                         }
    2912                 :                :                     }
    2913   [ +  +  +  +  :            116 :                     else if (xf_child_element == qn("spreadsheetml", "protection"))
                      - ]
    2914                 :                :                     {
    2915                 :             29 :                         record.first.protection_id = stylesheet.protections.size();
    2916            [ + ]:             29 :                         auto &protection = *stylesheet.protections.emplace(stylesheet.protections.end());
    2917                 :                : 
    2918   [ +  +  +  -  :             87 :                         protection.locked(parser().attribute_present("locked")
             -  -  -  - ]
    2919   [ +  +  -  +  :            145 :                             && is_true(parser().attribute("locked")));
          +  +  +  +  +  
          -  +  -  +  -  
             -  -  -  - ]
    2920   [ +  +  +  -  :            116 :                         protection.hidden(parser().attribute_present("hidden")
             -  -  -  - ]
    2921   [ +  +  +  +  :            135 :                             && is_true(parser().attribute("hidden")));
          +  +  -  +  +  
          +  +  +  +  -  
             -  -  -  - ]
    2922                 :                :                     }
    2923                 :                :                     else
    2924                 :                :                     {
    2925            [ # ]:UBC           0 :                         unexpected_element(xf_child_element);
    2926                 :                :                     }
    2927                 :                : 
    2928            [ + ]:CBC         561 :                     expect_end_element(xf_child_element);
    2929                 :            561 :                 }
    2930                 :                : 
    2931   [ +  +  +  + ]:           5156 :                 expect_end_element(qn("spreadsheetml", "xf"));
    2932                 :                :             }
    2933                 :                : 
    2934                 :                : #ifdef THROW_ON_INVALID_XML
    2935                 :                :             if (count.is_set() && ((in_style_records && count != style_records.size())
    2936                 :                :                 || (!in_style_records && count != format_records.size())))
    2937                 :                :             {
    2938                 :                :                 throw xlnt::exception("counts don't match");
    2939                 :                :             }
    2940                 :                : #endif
    2941                 :            170 :         }
    2942   [ +  +  +  +  :            788 :         else if (current_style_element == qn("spreadsheetml", "dxfs"))
                      + ]
    2943                 :                :         {
    2944                 :             68 :             std::size_t processed = 0;
    2945                 :                : 
    2946      [ +  -  + ]:             68 :             while (in_element(current_style_element))
    2947                 :                :             {
    2948            [ # ]:UBC           0 :                 auto current_element = expect_start_element(xml::content::mixed);
    2949            [ # ]:              0 :                 skip_remaining_content(current_element);
    2950            [ # ]:              0 :                 expect_end_element(current_element);
    2951                 :              0 :                 ++processed;
    2952                 :              0 :             }
    2953                 :                : 
    2954                 :                : #ifdef THROW_ON_INVALID_XML
    2955                 :                :             if (parser().attribute_present("count"))
    2956                 :                :             {
    2957                 :                :                 std::size_t count = parser().attribute<std::size_t>("count");
    2958                 :                :                 if (count != processed)
    2959                 :                :                 {
    2960                 :                :                     throw xlnt::exception("counts don't match");
    2961                 :                :                 }
    2962                 :                :             }
    2963                 :                : #endif
    2964                 :                :         }
    2965   [ +  +  +  +  :CBC         516 :         else if (current_style_element == qn("spreadsheetml", "tableStyles"))
                      + ]
    2966                 :                :         {
    2967         [ +  + ]:            142 :             skip_attribute("defaultTableStyle");
    2968         [ +  + ]:             71 :             skip_attribute("defaultPivotStyle");
    2969                 :                : 
    2970                 :             71 :             std::size_t processed = 0;
    2971                 :                : 
    2972   [ +  +  +  +  :            355 :             while (in_element(qn("spreadsheetml", "tableStyles")))
                   -  + ]
    2973                 :                :             {
    2974            [ # ]:UBC           0 :                 auto current_element = expect_start_element(xml::content::complex);
    2975            [ # ]:              0 :                 skip_remaining_content(current_element);
    2976            [ # ]:              0 :                 expect_end_element(current_element);
    2977                 :              0 :                 ++processed;
    2978                 :              0 :             }
    2979                 :                : 
    2980                 :                : #ifdef THROW_ON_INVALID_XML
    2981                 :                :             if (parser().attribute_present("count"))
    2982                 :                :             {
    2983                 :                :                 std::size_t count = parser().attribute<std::size_t>("count");
    2984                 :                :                 if (count != processed)
    2985                 :                :                 {
    2986                 :                :                     throw xlnt::exception("counts don't match");
    2987                 :                :                 }
    2988                 :                :             }
    2989                 :                : #endif
    2990                 :                :         }
    2991   [ +  +  +  +  :CBC         232 :         else if (current_style_element == qn("spreadsheetml", "extLst"))
                      + ]
    2992                 :                :         {
    2993   [ +  +  +  +  :            555 :             while (in_element(qn("spreadsheetml", "extLst")))
                   +  + ]
    2994                 :                :             {
    2995   [ +  +  +  + ]:            280 :                 expect_start_element(qn("spreadsheetml", "ext"), xml::content::complex);
    2996                 :                : 
    2997      [ +  +  + ]:            140 :                 const auto uri = parser().attribute("uri");
    2998                 :                : 
    2999      [ +  +  + ]:             70 :                 if (uri == "{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}") // slicerStyles
    3000                 :                :                 {
    3001   [ +  +  +  + ]:            220 :                     expect_start_element(qn("x14", "slicerStyles"), xml::content::simple);
    3002      [ +  +  + ]:            165 :                     stylesheet.default_slicer_style = parser().attribute("defaultSlicerStyle");
    3003   [ +  +  +  + ]:            220 :                     expect_end_element(qn("x14", "slicerStyles"));
    3004                 :                :                 }
    3005                 :                :                 else
    3006                 :                :                 {
    3007   [ +  +  +  + ]:             60 :                     skip_remaining_content(qn("spreadsheetml", "ext"));
    3008                 :                :                 }
    3009                 :                : 
    3010   [ +  +  +  + ]:            210 :                 expect_end_element(qn("spreadsheetml", "ext"));
    3011                 :             70 :             }
    3012                 :                :         }
    3013   [ +  +  +  +  :             12 :         else if (current_style_element == qn("spreadsheetml", "colors")) // CT_Colors 0-1
                      - ]
    3014                 :                :         {
    3015   [ +  +  +  +  :             27 :             while (in_element(qn("spreadsheetml", "colors")))
                   +  + ]
    3016                 :                :             {
    3017            [ + ]:              3 :                 auto colors_child_element = expect_start_element(xml::content::complex);
    3018                 :                : 
    3019   [ +  +  +  +  :             12 :                 if (colors_child_element == qn("spreadsheetml", "indexedColors")) // CT_IndexedColors 0-1
                      - ]
    3020                 :                :                 {
    3021      [ +  +  + ]:             93 :                     while (in_element(colors_child_element))
    3022                 :                :                     {
    3023   [ +  +  +  + ]:            360 :                         expect_start_element(qn("spreadsheetml", "rgbColor"), xml::content::simple);
    3024         [ +  + ]:             90 :                         stylesheet.colors.push_back(read_color());
    3025   [ +  +  +  + ]:            360 :                         expect_end_element(qn("spreadsheetml", "rgbColor"));
    3026                 :                :                     }
    3027                 :                :                 }
    3028   [ #  #  #  #  :UBC           0 :                 else if (colors_child_element == qn("spreadsheetml", "mruColors")) // CT_MRUColors
                      # ]
    3029                 :                :                 {
    3030            [ # ]:              0 :                     skip_remaining_content(colors_child_element);
    3031                 :                :                 }
    3032                 :                :                 else
    3033                 :                :                 {
    3034            [ # ]:              0 :                     unexpected_element(colors_child_element);
    3035                 :                :                 }
    3036                 :                : 
    3037            [ + ]:CBC           3 :                 expect_end_element(colors_child_element);
    3038                 :              3 :             }
    3039                 :                :         }
    3040                 :                :         else
    3041                 :                :         {
    3042            [ # ]:UBC           0 :             unexpected_element(current_style_element);
    3043                 :                :         }
    3044                 :                : 
    3045            [ + ]:CBC         726 :         expect_end_element(current_style_element);
    3046                 :            726 :     }
    3047                 :                : 
    3048   [ +  +  +  + ]:            258 :     expect_end_element(qn("spreadsheetml", "styleSheet"));
    3049                 :                : 
    3050                 :             86 :     std::size_t xf_id = 0;
    3051                 :                : 
    3052         [ +  + ]:            936 :     for (const auto &record : style_records)
    3053                 :                :     {
    3054            [ + ]:            850 :         auto style_iter = std::find_if(styles.begin(), styles.end(),
    3055                 :          21341 :             [&xf_id](const std::pair<style_impl, std::size_t> &s) { return s.second == xf_id; });
    3056                 :            850 :         ++xf_id;
    3057                 :                : 
    3058         [ +  + ]:            850 :         if (style_iter == styles.end()) continue;
    3059                 :                : 
    3060            [ + ]:            681 :         auto new_style = stylesheet.create_style(style_iter->first.name);
    3061                 :                : 
    3062                 :            681 :         new_style.d_->pivot_button_ = style_iter->first.pivot_button_;
    3063                 :            681 :         new_style.d_->quote_prefix_ = style_iter->first.quote_prefix_;
    3064                 :            681 :         new_style.d_->formatting_record_id = style_iter->first.formatting_record_id;
    3065                 :            681 :         new_style.d_->hidden_style = style_iter->first.hidden_style;
    3066                 :            681 :         new_style.d_->custom_builtin = style_iter->first.custom_builtin;
    3067                 :            681 :         new_style.d_->hidden_style = style_iter->first.hidden_style;
    3068            [ + ]:            681 :         new_style.d_->builtin_id = style_iter->first.builtin_id;
    3069            [ + ]:            681 :         new_style.d_->outline_style = style_iter->first.outline_style;
    3070                 :                : 
    3071            [ + ]:            681 :         new_style.d_->alignment_applied = record.first.alignment_applied;
    3072            [ + ]:            681 :         new_style.d_->alignment_id = record.first.alignment_id;
    3073            [ + ]:            681 :         new_style.d_->border_applied = record.first.border_applied;
    3074            [ + ]:            681 :         new_style.d_->border_id = record.first.border_id;
    3075            [ + ]:            681 :         new_style.d_->fill_applied = record.first.fill_applied;
    3076            [ + ]:            681 :         new_style.d_->fill_id = record.first.fill_id;
    3077            [ + ]:            681 :         new_style.d_->font_applied = record.first.font_applied;
    3078            [ + ]:            681 :         new_style.d_->font_id = record.first.font_id;
    3079            [ + ]:            681 :         new_style.d_->number_format_applied = record.first.number_format_applied;
    3080            [ + ]:            681 :         new_style.d_->number_format_id = record.first.number_format_id;
    3081                 :                :     }
    3082                 :                : 
    3083                 :             86 :     std::size_t record_index = 0;
    3084                 :                : 
    3085         [ +  + ]:            525 :     for (const auto &record : format_records)
    3086                 :                :     {
    3087            [ + ]:            439 :         stylesheet.format_impls.emplace_back();
    3088                 :            439 :         auto &new_format = *stylesheet.format_impls.back();
    3089                 :                : 
    3090                 :            439 :         new_format.id = record_index++;
    3091                 :            439 :         new_format.parent = &stylesheet;
    3092                 :                : 
    3093            [ + ]:            439 :         new_format.alignment_id = record.first.alignment_id;
    3094            [ + ]:            439 :         new_format.alignment_applied = record.first.alignment_applied;
    3095            [ + ]:            439 :         new_format.border_id = record.first.border_id;
    3096            [ + ]:            439 :         new_format.border_applied = record.first.border_applied;
    3097            [ + ]:            439 :         new_format.fill_id = record.first.fill_id;
    3098            [ + ]:            439 :         new_format.fill_applied = record.first.fill_applied;
    3099            [ + ]:            439 :         new_format.font_id = record.first.font_id;
    3100            [ + ]:            439 :         new_format.font_applied = record.first.font_applied;
    3101            [ + ]:            439 :         new_format.number_format_id = record.first.number_format_id;
    3102            [ + ]:            439 :         new_format.number_format_applied = record.first.number_format_applied;
    3103            [ + ]:            439 :         new_format.protection_id = record.first.protection_id;
    3104            [ + ]:            439 :         new_format.protection_applied = record.first.protection_applied;
    3105                 :            439 :         new_format.pivot_button_ = record.first.pivot_button_;
    3106                 :            439 :         new_format.quote_prefix_ = record.first.quote_prefix_;
    3107                 :                : 
    3108            [ + ]:            439 :         set_style_by_xfid(styles, record.second, new_format.style);
    3109                 :                :     }
    3110   [ +  -  -  -  :            258 : }
                      - ]
    3111                 :                : 
    3112                 :             72 : void xlsx_consumer::read_theme()
    3113                 :                : {
    3114      [ +  +  + ]:            216 :     auto workbook_rel = manifest().relationship(path("/"),
    3115            [ + ]:             72 :         relationship_type::office_document);
    3116         [ +  + ]:             72 :     auto theme_rel = manifest().relationship(workbook_rel.target().path(),
    3117         [ +  + ]:             72 :         relationship_type::theme);
    3118   [ +  +  +  +  :            360 :     auto theme_path = manifest().canonicalize({workbook_rel, theme_rel});
                +  -  - ]
    3119                 :                : 
    3120            [ + ]:             72 :     target_.theme(theme());
    3121                 :                : 
    3122   [ +  +  +  + ]:             72 :     if (manifest().has_relationship(theme_path, relationship_type::image))
    3123                 :                :     {
    3124   [ +  +  +  +  :             10 :         read_part({workbook_rel, theme_rel,
                   -  - ]
    3125                 :              4 :             manifest().relationship(theme_path,
    3126                 :                :                 relationship_type::image)});
    3127                 :                :     }
    3128   [ +  +  +  +  :            146 : }
          +  +  -  -  -  
             -  -  -  -  
                      - ]
    3129                 :                : 
    3130                 :UBC           0 : void xlsx_consumer::read_volatile_dependencies()
    3131                 :                : {
    3132                 :              0 : }
    3133                 :                : 
    3134                 :                : // Sheet Relationship Target Parts
    3135                 :                : 
    3136                 :CBC          14 : void xlsx_consumer::read_vml_drawings(worksheet /*ws*/)
    3137                 :                : {
    3138                 :             14 : }
    3139                 :                : 
    3140                 :             14 : void xlsx_consumer::read_comments(worksheet ws)
    3141                 :                : {
    3142                 :             14 :     std::vector<std::string> authors;
    3143                 :                : 
    3144   [ +  +  +  + ]:             70 :     expect_start_element(qn("spreadsheetml", "comments"), xml::content::complex);
    3145                 :                :     // name space can be ignored
    3146   [ +  +  +  + ]:             42 :     skip_attribute(qn("mc", "Ignorable"));
    3147   [ +  +  +  + ]:             56 :     expect_start_element(qn("spreadsheetml", "authors"), xml::content::complex);
    3148                 :                : 
    3149   [ +  +  +  +  :            126 :     while (in_element(qn("spreadsheetml", "authors")))
                   +  + ]
    3150                 :                :     {
    3151   [ +  +  +  + ]:             56 :         expect_start_element(qn("spreadsheetml", "author"), xml::content::simple);
    3152         [ +  + ]:             14 :         authors.push_back(read_text());
    3153   [ +  +  +  + ]:             56 :         expect_end_element(qn("spreadsheetml", "author"));
    3154                 :                :     }
    3155                 :                : 
    3156   [ +  +  +  + ]:             42 :     expect_end_element(qn("spreadsheetml", "authors"));
    3157   [ +  +  +  + ]:             56 :     expect_start_element(qn("spreadsheetml", "commentList"), xml::content::complex);
    3158                 :                : 
    3159   [ +  +  +  +  :            182 :     while (in_element(xml::qname(qn("spreadsheetml", "commentList"))))
                +  +  + ]
    3160                 :                :     {
    3161   [ +  +  +  + ]:            140 :         expect_start_element(qn("spreadsheetml", "comment"), xml::content::complex);
    3162                 :                : 
    3163         [ +  + ]:             28 :         skip_attribute("shapeId");
    3164      [ +  +  + ]:             56 :         auto cell_ref = parser().attribute("ref");
    3165         [ +  + ]:             56 :         auto author_id = parser().attribute<std::size_t>("authorId");
    3166                 :                : 
    3167   [ +  +  +  + ]:            112 :         expect_start_element(qn("spreadsheetml", "text"), xml::content::complex);
    3168                 :                : 
    3169   [ +  +  +  +  :            140 :         ws.cell(cell_ref).comment(comment(read_rich_text(qn("spreadsheetml", "text")), authors.at(author_id)));
             +  +  +  +  
                      + ]
    3170                 :                : 
    3171   [ +  +  +  + ]:            112 :         expect_end_element(qn("spreadsheetml", "text"));
    3172                 :                : 
    3173   [ +  +  +  +  :            112 :         if (in_element(xml::qname(qn("spreadsheetml", "comment"))))
                +  -  + ]
    3174                 :                :         {
    3175   [ #  #  #  # ]:UBC           0 :             expect_start_element(qn("mc", "AlternateContent"), xml::content::complex);
    3176   [ #  #  #  # ]:              0 :             skip_remaining_content(qn("mc", "AlternateContent"));
    3177   [ #  #  #  # ]:              0 :             expect_end_element(qn("mc", "AlternateContent"));
    3178                 :                :         }
    3179                 :                : 
    3180   [ +  +  +  + ]:CBC          84 :         expect_end_element(qn("spreadsheetml", "comment"));
    3181                 :             28 :     }
    3182                 :                : 
    3183   [ +  +  +  + ]:             56 :     expect_end_element(qn("spreadsheetml", "commentList"));
    3184   [ +  +  +  + ]:             42 :     expect_end_element(qn("spreadsheetml", "comments"));
    3185                 :             14 : }
    3186                 :                : 
    3187                 :              3 : void xlsx_consumer::read_drawings(worksheet ws, const path &part)
    3188                 :                : {
    3189         [ +  + ]:              3 :     auto images = manifest().relationships(part, relationship_type::image);
    3190                 :                : 
    3191            [ + ]:              3 :     auto sd = drawing::spreadsheet_drawing(parser());
    3192                 :                : 
    3193      [ +  +  + ]:              5 :     for (const auto &image_rel_id : sd.get_embed_ids())
    3194                 :                :     {
    3195            [ + ]:              2 :         auto image_rel = std::find_if(images.begin(), images.end(),
    3196                 :              2 :             [&](const relationship &r) { return r.id() == image_rel_id; });
    3197                 :                : 
    3198         [ +  - ]:              2 :         if (image_rel != images.end())
    3199                 :                :         {
    3200   [ +  +  +  + ]:              2 :             const auto url = image_rel->target().path().resolve(part.parent());
    3201                 :                : 
    3202            [ + ]:              2 :             read_image(url);
    3203                 :              2 :         }
    3204                 :              3 :     }
    3205                 :                : 
    3206            [ + ]:              3 :     ws.d_->drawing_ = sd;
    3207                 :              3 : }
    3208                 :                : 
    3209                 :                : // Unknown Parts
    3210                 :                : 
    3211                 :UBC           0 : void xlsx_consumer::read_unknown_parts()
    3212                 :                : {
    3213                 :              0 : }
    3214                 :                : 
    3215                 :              0 : void xlsx_consumer::read_unknown_relationships()
    3216                 :                : {
    3217                 :              0 : }
    3218                 :                : 
    3219                 :CBC          48 : void xlsx_consumer::read_image(const xlnt::path &image_path)
    3220                 :                : {
    3221            [ + ]:             48 :     auto image_streambuf = archive_->open(image_path);
    3222      [ +  +  + ]:             48 :     vector_ostreambuf buffer(target_.d_->images_[image_path.string()]);
    3223            [ + ]:             48 :     std::ostream out_stream(&buffer);
    3224            [ + ]:             48 :     out_stream << image_streambuf.get();
    3225                 :             48 : }
    3226                 :                : 
    3227                 :              7 : void xlsx_consumer::read_binary(const xlnt::path &binary_path)
    3228                 :                : {
    3229            [ + ]:              7 :     auto binary_streambuf = archive_->open(binary_path);
    3230      [ +  +  + ]:              7 :     vector_ostreambuf buffer(target_.d_->binaries_[binary_path.string()]);
    3231            [ + ]:              7 :     std::ostream out_stream(&buffer);
    3232            [ + ]:              7 :     out_stream << binary_streambuf.get();
    3233                 :              7 : }
    3234                 :                : 
    3235                 :           5208 : std::string xlsx_consumer::read_text()
    3236                 :                : {
    3237                 :           5208 :     auto text = std::string();
    3238                 :                : 
    3239      [ +  +  + ]:           7737 :     while (parser().peek() == xml::parser::event_type::characters)
    3240                 :                :     {
    3241            [ + ]:           2529 :         parser().next_expect(xml::parser::event_type::characters);
    3242            [ + ]:           2529 :         text.append(parser().value());
    3243                 :                :     }
    3244                 :                : 
    3245                 :           5208 :     return text;
    3246                 :UBC           0 : }
    3247                 :                : 
    3248                 :CBC         973 : variant xlsx_consumer::read_variant()
    3249                 :                : {
    3250         [ +  + ]:            973 :     auto value = variant(read_text());
    3251                 :                : 
    3252      [ +  +  + ]:            973 :     if (in_element(stack_.back()))
    3253                 :                :     {
    3254            [ + ]:            382 :         auto element = expect_start_element(xml::content::mixed);
    3255            [ + ]:            382 :         auto text = read_text();
    3256                 :                : 
    3257   [ +  +  +  +  :           3410 :         if (element == qn("vt", "lpwstr") || element == qn("vt", "lpstr"))
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          -  +  -  +  -  
          +  -  +  +  -  
          -  -  -  -  -  
          -  -  -  -  -  
             -  -  -  -  
                      - ]
    3258                 :                :         {
    3259            [ + ]:            177 :             value = variant(text);
    3260                 :                :         }
    3261   [ +  +  +  +  :           1528 :         if (element == qn("vt", "i4"))
                      + ]
    3262                 :                :         {
    3263                 :             71 :             int number = -1;
    3264      [ +  +  - ]:             71 :             if (detail::parse(text, number) != std::errc())
    3265                 :                :             {
    3266                 :                : #ifdef THROW_ON_INVALID_XML
    3267                 :                :                 throw xlnt::invalid_parameter();
    3268                 :                : #endif
    3269                 :                :             }
    3270                 :                :             else
    3271                 :                :             {
    3272            [ + ]:             71 :                 value = variant(number);
    3273                 :                :             }
    3274                 :                :         }
    3275   [ +  +  +  -  :           1528 :         if (element == qn("vt", "bool"))
                      + ]
    3276                 :                :         {
    3277         [ #  # ]:UBC           0 :             value = variant(is_true(text));
    3278                 :                :         }
    3279   [ +  +  +  +  :CBC        1528 :         else if (element == qn("vt", "vector"))
                      + ]
    3280                 :                :         {
    3281         [ +  + ]:            268 :             auto size = parser().attribute<std::size_t>("size");
    3282      [ +  +  + ]:            268 :             auto base_type = parser().attribute("baseType");
    3283                 :                : 
    3284                 :            134 :             std::vector<variant> vector;
    3285                 :                : 
    3286         [ +  + ]:            375 :             for (auto i = std::size_t(0); i < size; ++i)
    3287                 :                :             {
    3288      [ +  +  + ]:            241 :                 if (base_type == "variant")
    3289                 :                :                 {
    3290   [ +  +  +  + ]:            710 :                     expect_start_element(qn("vt", "variant"), xml::content::complex);
    3291                 :                :                 }
    3292                 :                : 
    3293         [ +  + ]:            241 :                 vector.push_back(read_variant());
    3294                 :                : 
    3295      [ +  +  + ]:            241 :                 if (base_type == "variant")
    3296                 :                :                 {
    3297   [ +  +  +  + ]:            426 :                     expect_end_element(qn("vt", "variant"));
    3298            [ + ]:            142 :                     read_text();
    3299                 :                :                 }
    3300                 :                :             }
    3301                 :                : 
    3302            [ + ]:            134 :             value = variant(vector);
    3303                 :            134 :         }
    3304                 :                : 
    3305            [ + ]:            382 :         expect_end_element(element);
    3306            [ + ]:            382 :         read_text();
    3307                 :            382 :     }
    3308                 :                : 
    3309                 :            973 :     return value;
    3310                 :UBC           0 : }
    3311                 :                : 
    3312                 :CBC         429 : void xlsx_consumer::skip_attributes(const std::vector<std::string> &names)
    3313                 :                : {
    3314         [ +  + ]:           3021 :     for (const auto &name : names)
    3315                 :                :     {
    3316      [ +  +  + ]:           2592 :         if (parser().attribute_present(name))
    3317                 :                :         {
    3318            [ + ]:            379 :             parser().attribute(name);
    3319                 :                :         }
    3320                 :                :     }
    3321                 :            429 : }
    3322                 :                : 
    3323                 :            214 : void xlsx_consumer::skip_attributes(const std::vector<xml::qname> &names)
    3324                 :                : {
    3325         [ +  + ]:            428 :     for (const auto &name : names)
    3326                 :                :     {
    3327      [ +  +  + ]:            214 :         if (parser().attribute_present(name))
    3328                 :                :         {
    3329            [ + ]:            120 :             parser().attribute(name);
    3330                 :                :         }
    3331                 :                :     }
    3332                 :            214 : }
    3333                 :                : 
    3334                 :           1521 : void xlsx_consumer::skip_attributes()
    3335                 :                : {
    3336                 :           1521 :     parser().attribute_map();
    3337                 :           1521 : }
    3338                 :                : 
    3339                 :            268 : void xlsx_consumer::skip_attribute(const xml::qname &name)
    3340                 :                : {
    3341         [ +  + ]:            268 :     if (parser().attribute_present(name))
    3342                 :                :     {
    3343                 :            198 :         parser().attribute(name);
    3344                 :                :     }
    3345                 :            268 : }
    3346                 :                : 
    3347                 :           2062 : void xlsx_consumer::skip_attribute(const std::string &name)
    3348                 :                : {
    3349         [ +  + ]:           2062 :     if (parser().attribute_present(name))
    3350                 :                :     {
    3351                 :            229 :         parser().attribute(name);
    3352                 :                :     }
    3353                 :           2062 : }
    3354                 :                : 
    3355                 :            511 : void xlsx_consumer::skip_remaining_content(const xml::qname &name)
    3356                 :                : {
    3357                 :                :     // start by assuming we've already parsed the opening tag
    3358                 :                : 
    3359                 :            511 :     skip_attributes();
    3360            [ + ]:            511 :     read_text();
    3361                 :                : 
    3362                 :                :     // continue until the closing tag is reached
    3363         [ +  + ]:            561 :     while (in_element(name))
    3364                 :                :     {
    3365            [ + ]:             50 :         auto child_element = expect_start_element(xml::content::mixed);
    3366            [ + ]:             50 :         skip_remaining_content(child_element);
    3367            [ + ]:             50 :         expect_end_element(child_element);
    3368            [ + ]:             50 :         read_text(); // trailing character content (usually whitespace)
    3369                 :             50 :     }
    3370                 :            511 : }
    3371                 :                : 
    3372                 :          24954 : bool xlsx_consumer::in_element(const xml::qname &name)
    3373                 :                : {
    3374                 :          24954 :     return parser().peek() != xml::parser::event_type::end_element
    3375   [ +  +  +  + ]:          24954 :         && stack_.back() == name;
    3376                 :                : }
    3377                 :                : 
    3378                 :          11553 : xml::qname xlsx_consumer::expect_start_element(xml::content content)
    3379                 :                : {
    3380            [ + ]:          11553 :     parser().next_expect(xml::parser::event_type::start_element);
    3381            [ + ]:          11553 :     parser().content(content);
    3382            [ + ]:          11553 :     stack_.push_back(parser().qname());
    3383                 :                : 
    3384   [ +  +  +  + ]:          34659 :     const auto xml_space = qn("xml", "space");
    3385   [ +  +  +  +  :          11553 :     preserve_space_ = parser().attribute_present(xml_space) ? parser().attribute(xml_space) == "preserve" : false;
                      + ]
    3386                 :                : 
    3387            [ + ]:          23106 :     return stack_.back();
    3388                 :          11553 : }
    3389                 :                : 
    3390                 :           7052 : void xlsx_consumer::expect_start_element(const xml::qname &name, xml::content content)
    3391                 :                : {
    3392            [ + ]:           7052 :     parser().next_expect(xml::parser::event_type::start_element, name);
    3393            [ + ]:           7052 :     parser().content(content);
    3394            [ + ]:           7052 :     stack_.push_back(name);
    3395                 :                : 
    3396   [ +  +  +  + ]:          21156 :     const auto xml_space = qn("xml", "space");
    3397   [ +  +  +  +  :           7052 :     preserve_space_ = parser().attribute_present(xml_space) ? parser().attribute(xml_space) == "preserve" : false;
                      + ]
    3398                 :           7052 : }
    3399                 :                : 
    3400                 :          18476 : void xlsx_consumer::expect_end_element(const xml::qname &name)
    3401                 :                : {
    3402                 :          18476 :     parser().attribute_map();
    3403                 :          18476 :     parser().next_expect(xml::parser::event_type::end_element, name);
    3404                 :          18476 :     stack_.pop_back();
    3405                 :          18476 : }
    3406                 :                : 
    3407                 :             11 : void xlsx_consumer::unexpected_element(const xml::qname &name)
    3408                 :                : {
    3409                 :                : #ifdef THROW_ON_INVALID_XML
    3410                 :                :     throw xlnt::exception(name.string());
    3411                 :                : #else
    3412                 :             11 :     skip_remaining_content(name);
    3413                 :                : #endif
    3414                 :             11 : }
    3415                 :                : 
    3416                 :            742 : rich_text xlsx_consumer::read_rich_text(const xml::qname &parent)
    3417                 :                : {
    3418                 :            742 :     const auto &xmlns = parent.namespace_();
    3419                 :            742 :     rich_text t;
    3420                 :                : 
    3421      [ +  +  + ]:           1541 :     while (in_element(parent))
    3422                 :                :     {
    3423            [ + ]:            799 :         auto text_element = expect_start_element(xml::content::mixed);
    3424   [ +  +  +  + ]:           2397 :         const auto xml_space = qn("xml", "space");
    3425            [ + ]:            799 :         const auto preserve_space = parser().attribute_present(xml_space)
    3426   [ +  +  +  + ]:            799 :             ? parser().attribute(xml_space) == "preserve"
    3427                 :            799 :             : false;
    3428            [ + ]:            799 :         skip_attributes();
    3429            [ + ]:            799 :         auto text = read_text();
    3430                 :                : 
    3431   [ +  +  +  + ]:           1598 :         if (text_element == xml::qname(xmlns, "t"))
    3432                 :                :         {
    3433            [ + ]:            712 :             t.plain_text(text, preserve_space);
    3434                 :                :         }
    3435   [ +  +  +  + ]:            174 :         else if (text_element == xml::qname(xmlns, "r"))
    3436                 :                :         {
    3437                 :             51 :             rich_text_run run;
    3438                 :             51 :             run.preserve_space = preserve_space;
    3439                 :                : 
    3440   [ +  +  +  +  :            357 :             while (in_element(xml::qname(xmlns, "r")))
                      + ]
    3441                 :                :             {
    3442            [ + ]:            102 :                 auto run_element = expect_start_element(xml::content::mixed);
    3443            [ + ]:            102 :                 auto run_text = read_text();
    3444                 :                : 
    3445   [ +  +  +  + ]:            204 :                 if (run_element == xml::qname(xmlns, "rPr"))
    3446                 :                :                 {
    3447            [ + ]:             51 :                     run.second = xlnt::font();
    3448                 :                : 
    3449   [ +  +  +  +  :            601 :                     while (in_element(xml::qname(xmlns, "rPr")))
                      + ]
    3450                 :                :                     {
    3451            [ + ]:            224 :                         auto current_run_property_element = expect_start_element(xml::content::simple);
    3452                 :                : 
    3453   [ +  +  +  + ]:            448 :                         if (current_run_property_element == xml::qname(xmlns, "sz"))
    3454                 :                :                         {
    3455   [ +  +  +  +  :            153 :                             run.second.get().size(xlnt::detail::deserialise(parser().attribute("val")));
                      + ]
    3456                 :                :                         }
    3457   [ +  +  +  + ]:            346 :                         else if (current_run_property_element == xml::qname(xmlns, "rFont"))
    3458                 :                :                         {
    3459   [ +  +  +  + ]:            153 :                             run.second.get().name(parser().attribute("val"));
    3460                 :                :                         }
    3461   [ +  +  +  + ]:            244 :                         else if (current_run_property_element == xml::qname(xmlns, "color"))
    3462                 :                :                         {
    3463      [ +  +  + ]:             51 :                             run.second.get().color(read_color());
    3464                 :                :                         }
    3465   [ +  +  +  + ]:            142 :                         else if (current_run_property_element == xml::qname(xmlns, "family"))
    3466                 :                :                         {
    3467   [ +  +  +  + ]:             42 :                             run.second.get().family(parser().attribute<std::size_t>("val"));
    3468                 :                :                         }
    3469   [ +  +  -  + ]:            114 :                         else if (current_run_property_element == xml::qname(xmlns, "charset"))
    3470                 :                :                         {
    3471   [ #  #  #  # ]:UBC           0 :                             run.second.get().charset(parser().attribute<std::size_t>("val"));
    3472                 :                :                         }
    3473   [ +  +  +  + ]:CBC         114 :                         else if (current_run_property_element == xml::qname(xmlns, "scheme"))
    3474                 :                :                         {
    3475   [ +  +  +  + ]:             39 :                             run.second.get().scheme(parser().attribute("val"));
    3476                 :                :                         }
    3477   [ +  +  +  + ]:             88 :                         else if (current_run_property_element == xml::qname(xmlns, "b"))
    3478                 :                :                         {
    3479   [ +  +  +  -  :            108 :                             run.second.get().bold(parser().attribute_present("val")
                   +  + ]
    3480   [ -  -  -  -  :             36 :                                     ? is_true(parser().attribute("val"))
          +  -  +  -  -  
                   -  - ]
    3481                 :                :                                     : true);
    3482                 :                :                         }
    3483   [ +  +  -  + ]:             16 :                         else if (current_run_property_element == xml::qname(xmlns, "i"))
    3484                 :                :                         {
    3485   [ #  #  #  #  :UBC           0 :                             run.second.get().italic(parser().attribute_present("val")
                   #  # ]
    3486   [ #  #  #  #  :              0 :                                     ? is_true(parser().attribute("val"))
          #  #  #  #  #  
                   #  # ]
    3487                 :                :                                     : true);
    3488                 :                :                         }
    3489   [ +  +  +  + ]:CBC          16 :                         else if (current_run_property_element == xml::qname(xmlns, "u"))
    3490                 :                :                         {
    3491   [ +  +  -  + ]:             18 :                             if (parser().attribute_present("val"))
    3492                 :                :                             {
    3493   [ #  #  #  # ]:UBC           0 :                                 run.second.get().underline(parser().attribute<font::underline_style>("val"));
    3494                 :                :                             }
    3495                 :                :                             else
    3496                 :                :                             {
    3497         [ +  + ]:CBC           6 :                                 run.second.get().underline(font::underline_style::single);
    3498                 :                :                             }
    3499                 :                :                         }
    3500   [ +  +  +  - ]:              4 :                         else if (current_run_property_element == xml::qname(xmlns, "strike"))
    3501                 :                :                         {
    3502   [ +  +  +  -  :              6 :                             run.second.get().strikethrough(parser().attribute_present("val")
                   +  + ]
    3503   [ -  -  -  -  :              2 :                                     ? is_true(parser().attribute("val"))
          +  -  +  -  -  
                   -  - ]
    3504                 :                :                                     : true);
    3505                 :                :                         }
    3506                 :                :                         else
    3507                 :                :                         {
    3508            [ # ]:UBC           0 :                             unexpected_element(current_run_property_element);
    3509                 :                :                         }
    3510                 :                : 
    3511            [ + ]:CBC         224 :                         expect_end_element(current_run_property_element);
    3512            [ + ]:            224 :                         read_text();
    3513                 :            224 :                     }
    3514                 :                :                 }
    3515   [ +  +  +  - ]:            102 :                 else if (run_element == xml::qname(xmlns, "t"))
    3516                 :                :                 {
    3517            [ + ]:             51 :                     run.first = run_text;
    3518                 :                :                 }
    3519                 :                :                 else
    3520                 :                :                 {
    3521            [ # ]:UBC           0 :                     unexpected_element(run_element);
    3522                 :                :                 }
    3523                 :                : 
    3524            [ + ]:CBC         102 :                 read_text();
    3525            [ + ]:            102 :                 expect_end_element(run_element);
    3526            [ + ]:            102 :                 read_text();
    3527                 :            102 :             }
    3528                 :                : 
    3529            [ + ]:             51 :             t.add_run(run);
    3530                 :             51 :         }
    3531   [ +  +  +  + ]:             72 :         else if (text_element == xml::qname(xmlns, "rPh"))
    3532                 :                :         {
    3533                 :              2 :             phonetic_run pr;
    3534         [ +  + ]:              4 :             pr.start = parser().attribute<std::uint32_t>("sb");
    3535         [ +  + ]:              4 :             pr.end = parser().attribute<std::uint32_t>("eb");
    3536                 :                : 
    3537      [ +  +  + ]:              4 :             expect_start_element(xml::qname(xmlns, "t"), xml::content::simple);
    3538            [ + ]:              2 :             pr.text = read_text();
    3539                 :                : 
    3540      [ +  +  - ]:              2 :             if (parser().attribute_present(xml_space))
    3541                 :                :             {
    3542         [ +  + ]:              2 :                 pr.preserve_space = parser().attribute(xml_space) == "preserve";
    3543                 :                :             }
    3544                 :                : 
    3545      [ +  +  + ]:              2 :             expect_end_element(xml::qname(xmlns, "t"));
    3546                 :                : 
    3547            [ + ]:              2 :             t.add_phonetic_run(pr);
    3548                 :              2 :         }
    3549   [ +  +  +  - ]:             68 :         else if (text_element == xml::qname(xmlns, "phoneticPr"))
    3550                 :                :         {
    3551      [ +  +  + ]:             68 :             phonetic_pr ph(parser().attribute<phonetic_pr::font_id_t>("fontId"));
    3552   [ +  +  +  + ]:            102 :             if (parser().attribute_present("type"))
    3553                 :                :             {
    3554   [ +  +  +  + ]:             96 :                 ph.type(phonetic_pr::type_from_string(parser().attribute("type")));
    3555                 :                :             }
    3556   [ +  +  -  + ]:            102 :             if (parser().attribute_present("alignment"))
    3557                 :                :             {
    3558   [ #  #  #  # ]:UBC           0 :                 ph.alignment(phonetic_pr::alignment_from_string(parser().attribute("alignment")));
    3559                 :                :             }
    3560            [ + ]:CBC          34 :             t.phonetic_properties(ph);
    3561                 :             34 :         }
    3562                 :                :         else
    3563                 :                :         {
    3564            [ # ]:UBC           0 :             unexpected_element(text_element);
    3565                 :                :         }
    3566                 :                : 
    3567            [ + ]:CBC         799 :         read_text();
    3568            [ + ]:            799 :         expect_end_element(text_element);
    3569                 :            799 :     }
    3570                 :                : 
    3571                 :            742 :     return t;
    3572                 :UBC           0 : }
    3573                 :                : 
    3574                 :CBC        1449 : xlnt::color xlsx_consumer::read_color()
    3575                 :                : {
    3576                 :           1449 :     xlnt::color result;
    3577                 :                : 
    3578   [ +  +  +  +  :           4355 :     if (parser().attribute_present("auto") && is_true(parser().attribute("auto")))
          +  +  +  +  -  
          +  +  +  +  +  
          -  +  -  +  +  
          -  -  -  -  -  
                -  -  - ]
    3579                 :                :     {
    3580            [ + ]:              4 :         result.auto_(true);
    3581                 :              4 :         return result;
    3582                 :                :     }
    3583                 :                : 
    3584   [ +  +  +  + ]:           4335 :     if (parser().attribute_present("rgb"))
    3585                 :                :     {
    3586   [ +  +  +  + ]:           1404 :         result = xlnt::rgb_color(parser().attribute("rgb"));
    3587                 :                :     }
    3588   [ +  +  +  + ]:           2931 :     else if (parser().attribute_present("theme"))
    3589                 :                :     {
    3590   [ +  +  +  + ]:           1860 :         result = xlnt::theme_color(parser().attribute<std::size_t>("theme"));
    3591                 :                :     }
    3592   [ +  +  +  - ]:           1071 :     else if (parser().attribute_present("indexed"))
    3593                 :                :     {
    3594   [ +  +  +  + ]:           1071 :         result = xlnt::indexed_color(parser().attribute<std::size_t>("indexed"));
    3595                 :                :     }
    3596                 :                : 
    3597   [ +  +  +  + ]:           4335 :     if (parser().attribute_present("tint"))
    3598                 :                :     {
    3599   [ +  +  +  + ]:            597 :         result.tint(xlnt::detail::deserialise(parser().attribute("tint")));
    3600                 :                :     }
    3601                 :                : 
    3602                 :           1445 :     return result;
    3603                 :UBC           0 : }
    3604                 :                : 
    3605                 :CBC        2082 : manifest &xlsx_consumer::manifest()
    3606                 :                : {
    3607                 :           2082 :     return target_.manifest();
    3608                 :                : }
    3609                 :                : 
    3610                 :                : } // namespace detail
    3611                 :                : } // namespace xlnt
        

Generated by: LCOV version 2.3.1-beta