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 = ¤t_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
|