Branch data TLA Line data Source code
1 : : // Copyright (c) 2014-2022 Thomas Fussell
2 : : // Copyright (c) 2010-2015 openpyxl
3 : : // Copyright (c) 2024-2025 xlnt-community
4 : : //
5 : : // Permission is hereby granted, free of charge, to any person obtaining a copy
6 : : // of this software and associated documentation files (the "Software"), to deal
7 : : // in the Software without restriction, including without limitation the rights
8 : : // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : : // copies of the Software, and to permit persons to whom the Software is
10 : : // furnished to do so, subject to the following conditions:
11 : : //
12 : : // The above copyright notice and this permission notice shall be included in
13 : : // all copies or substantial portions of the Software.
14 : : //
15 : : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : : // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : : // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : : // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : : // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : : // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : : // THE SOFTWARE
22 : : //
23 : : // @license: http://www.opensource.org/licenses/mit-license.php
24 : : // @author: see AUTHORS file
25 : :
26 : : #include <algorithm>
27 : : #include <cassert>
28 : : #include <cmath>
29 : :
30 : : #include <xlnt/cell/cell.hpp>
31 : : #include <xlnt/cell/cell_reference.hpp>
32 : : #include <xlnt/cell/comment.hpp>
33 : : #include <xlnt/cell/hyperlink.hpp>
34 : : #include <xlnt/cell/rich_text.hpp>
35 : : #include <xlnt/packaging/manifest.hpp>
36 : : #include <xlnt/packaging/relationship.hpp>
37 : : #include <xlnt/styles/alignment.hpp>
38 : : #include <xlnt/styles/border.hpp>
39 : : #include <xlnt/styles/color.hpp>
40 : : #include <xlnt/styles/fill.hpp>
41 : : #include <xlnt/styles/font.hpp>
42 : : #include <xlnt/styles/format.hpp>
43 : : #include <xlnt/styles/number_format.hpp>
44 : : #include <xlnt/styles/protection.hpp>
45 : : #include <xlnt/styles/style.hpp>
46 : : #include <xlnt/utils/date.hpp>
47 : : #include <xlnt/utils/datetime.hpp>
48 : : #include <xlnt/utils/exceptions.hpp>
49 : : #include <xlnt/utils/time.hpp>
50 : : #include <xlnt/utils/timedelta.hpp>
51 : : #include <xlnt/workbook/workbook.hpp>
52 : : #include <xlnt/worksheet/column_properties.hpp>
53 : : #include <xlnt/worksheet/phonetic_pr.hpp>
54 : : #include <xlnt/worksheet/row_properties.hpp>
55 : : #include <xlnt/worksheet/worksheet.hpp>
56 : : #include <detail/implementations/cell_impl.hpp>
57 : : #include <detail/implementations/format_impl.hpp>
58 : : #include <detail/implementations/hyperlink_impl.hpp>
59 : : #include <detail/implementations/stylesheet.hpp>
60 : : #include <detail/implementations/worksheet_impl.hpp>
61 : : #include <detail/serialization/serialisation_helpers.hpp>
62 : :
63 : : namespace {
64 : :
65 :CBC 11 : std::pair<bool, double> cast_numeric(const std::string &s)
66 : : {
67 : 11 : size_t len_convert = 0;
68 [ + ]: 11 : double result = xlnt::detail::deserialise(s, &len_convert);
69 : 11 : return (len_convert != s.size())
70 [ + + ]: 11 : ? std::make_pair(false, 0.0)
71 : 11 : : std::make_pair(true, result);
72 : : }
73 : :
74 : 14 : std::pair<bool, double> cast_percentage(const std::string &s)
75 : : {
76 [ + + ]: 14 : if (s.back() == '%')
77 : : {
78 [ + + ]: 1 : auto number = cast_numeric(s.substr(0, s.size() - 1));
79 : :
80 [ + - ]: 1 : if (number.first)
81 : : {
82 : 1 : return {true, number.second / 100};
83 : : }
84 : : }
85 : :
86 : 13 : return {false, 0.0};
87 : : }
88 : :
89 : 13 : std::pair<bool, xlnt::time> cast_time(const std::string &s)
90 : : {
91 [ + ]: 13 : xlnt::time result;
92 : :
93 : 13 : std::vector<std::string> time_components;
94 : 13 : std::size_t prev = 0;
95 : 13 : auto colon_index = s.find(':');
96 : :
97 [ + + ]: 18 : while (colon_index != std::string::npos)
98 : : {
99 [ + + ]: 5 : time_components.push_back(s.substr(prev, colon_index - prev));
100 : 5 : prev = colon_index + 1;
101 : 5 : colon_index = s.find(':', colon_index + 1);
102 : : }
103 : :
104 [ + + ]: 13 : time_components.push_back(s.substr(prev, colon_index - prev));
105 : :
106 [ + + - + : 13 : if (time_components.size() < 2 || time_components.size() > 3)
+ + ]
107 : : {
108 : 9 : return {false, result};
109 : : }
110 : :
111 : 4 : std::vector<double> numeric_components;
112 : :
113 [ + + ]: 12 : for (const auto & component : time_components)
114 : : {
115 [ + + + - : 9 : if (component.empty() || (component.substr(0, component.find('.')).size() > 2))
+ + + + +
- - ]
116 : : {
117 : 1 : return {false, result};
118 : : }
119 : :
120 [ + + ]: 34 : for (auto d : component)
121 : : {
122 [ + + - + : 26 : if (!(d >= '0' && d <= '9') && d != '.')
- + ]
123 : : {
124 :UBC 0 : return {false, result};
125 : : }
126 : : }
127 [ + ]:CBC 8 : auto numeric = xlnt::detail::deserialise(component);
128 : :
129 [ + ]: 8 : numeric_components.push_back(numeric);
130 : : }
131 : :
132 : 3 : result.hour = static_cast<int>(numeric_components[0]);
133 : 3 : result.minute = static_cast<int>(numeric_components[1]);
134 : :
135 [ + + ]: 3 : if (std::fabs(static_cast<double>(result.minute) - numeric_components[1]) > std::numeric_limits<double>::epsilon())
136 : : {
137 : 1 : result.minute = result.hour;
138 : 1 : result.hour = 0;
139 : 1 : result.second = static_cast<int>(numeric_components[1]);
140 : 1 : result.microsecond = static_cast<int>((numeric_components[1] - result.second) * 1E6);
141 : : }
142 [ + + ]: 2 : else if (numeric_components.size() > 2)
143 : : {
144 : 1 : result.second = static_cast<int>(numeric_components[2]);
145 : 1 : result.microsecond = static_cast<int>((numeric_components[2] - result.second) * 1E6);
146 : : }
147 : :
148 : 3 : return {true, result};
149 : 13 : }
150 : :
151 : : } // namespace
152 : :
153 : : namespace xlnt {
154 : :
155 : 1 : const std::unordered_map<std::string, int> &cell::error_codes()
156 : : {
157 : : static const auto codes = std::unordered_map<std::string, int>{
158 :UBC 0 : {"#NULL!", 0},
159 : 0 : {"#DIV/0!", 1},
160 : 0 : {"#VALUE!", 2},
161 : 0 : {"#REF!", 3},
162 : 0 : {"#NAME?", 4},
163 : 0 : {"#NUM!", 5},
164 [ + - + - :CBC 10 : {"#N/A!", 6}};
+ + + - -
- - ]
165 : :
166 : 1 : return codes;
167 [ + + + + : 1 : }
+ + + - -
- - ]
168 : :
169 : 621 : std::string cell::check_string(const std::string &to_check)
170 : : {
171 : : // so we can modify it
172 : 621 : std::string s = to_check;
173 : :
174 [ + + ]: 621 : if (s.size() == 0)
175 : : {
176 : 2 : return s;
177 : : }
178 [ + + ]: 619 : else if (s.size() > 32767)
179 : : {
180 [ + ]: 1 : s = s.substr(0, 32767); // max string length in Excel
181 : : }
182 : :
183 [ + + ]: 68427 : for (char c : s)
184 : : {
185 [ + - + + : 67837 : if (c >= 0 && (c <= 8 || c == 11 || c == 12 || (c >= 14 && c <= 31)))
+ + + + +
+ + + ]
186 : : {
187 [ + ]: 29 : throw illegal_character(c);
188 : : }
189 : : }
190 : :
191 : 590 : return s;
192 : 29 : }
193 : :
194 : 3284 : cell::cell(detail::cell_impl *d)
195 : 3284 : : d_(d)
196 : : {
197 : 3284 : }
198 : :
199 : 957 : bool cell::garbage_collectible() const
200 : : {
201 : 957 : return d_->is_garbage_collectible();
202 : : }
203 : :
204 : 1 : void cell::value(std::nullptr_t)
205 : : {
206 : 1 : clear_value();
207 : 1 : }
208 : :
209 : 10 : void cell::value(bool boolean_value)
210 : : {
211 : 10 : d_->type_ = type::boolean;
212 [ + + ]: 10 : d_->value_numeric_ = boolean_value ? 1.0 : 0.0;
213 : 10 : }
214 : :
215 : 14 : void cell::value(int int_value)
216 : : {
217 : 14 : d_->value_numeric_ = static_cast<double>(int_value);
218 : 14 : d_->type_ = type::number;
219 : 14 : }
220 : :
221 : 2 : void cell::value(unsigned int int_value)
222 : : {
223 : 2 : d_->value_numeric_ = static_cast<double>(int_value);
224 : 2 : d_->type_ = type::number;
225 : 2 : }
226 : :
227 : 2 : void cell::value(long long int int_value)
228 : : {
229 : 2 : d_->value_numeric_ = static_cast<double>(int_value);
230 : 2 : d_->type_ = type::number;
231 : 2 : }
232 : :
233 : 2 : void cell::value(unsigned long long int int_value)
234 : : {
235 : 2 : d_->value_numeric_ = static_cast<double>(int_value);
236 : 2 : d_->type_ = type::number;
237 : 2 : }
238 : :
239 : 2 : void cell::value(float float_value)
240 : : {
241 : 2 : d_->value_numeric_ = static_cast<double>(float_value);
242 : 2 : d_->type_ = type::number;
243 : 2 : }
244 : :
245 : 6 : void cell::value(double float_value)
246 : : {
247 : 6 : d_->value_numeric_ = static_cast<double>(float_value);
248 : 6 : d_->type_ = type::number;
249 : 6 : }
250 : :
251 : 325 : void cell::value(const std::string &s)
252 : : {
253 [ + + + ]: 325 : value(rich_text(check_string(s)));
254 : 296 : }
255 : :
256 : 296 : void cell::value(const rich_text &text)
257 : : {
258 [ + + ]: 296 : check_string(text.plain_text());
259 : :
260 : 296 : value_no_check(text);
261 : 296 : }
262 : :
263 : 124 : void cell::value(const char *c)
264 : : {
265 [ + + ]: 124 : value(std::string(c));
266 : 124 : }
267 : :
268 : 7 : void cell::value(const cell c)
269 : : {
270 [ + + + + : 7 : if (c.worksheet().workbook() != worksheet().workbook())
+ + + ]
271 : : {
272 : 5 : copy_from_other_workbook(c);
273 : 5 : return;
274 : : }
275 : :
276 : : // Same workbook: shallow copy (existing behavior)
277 : 2 : d_->type_ = c.d_->type_;
278 : 2 : d_->value_numeric_ = c.d_->value_numeric_;
279 : 2 : d_->value_text_ = c.d_->value_text_;
280 : 2 : d_->hyperlink_ = c.d_->hyperlink_;
281 : 2 : d_->formula_ = c.d_->formula_;
282 : 2 : d_->format_ = c.d_->format_;
283 : : }
284 : :
285 : 299 : void cell::value_no_check(const rich_text &text)
286 : : {
287 : 299 : d_->type_ = type::shared_string;
288 [ + + ]: 299 : d_->value_numeric_ = static_cast<double>(workbook().add_shared_string(text));
289 : 299 : }
290 : :
291 : 5 : void cell::copy_from_other_workbook(const cell &source)
292 : : {
293 : 5 : d_->type_ = source.d_->type_;
294 : :
295 : : // Handle shared_string: remap to destination workbook
296 [ + + ]: 5 : if (source.data_type() == type::shared_string)
297 : : {
298 [ + + ]: 3 : value_no_check(source.value<rich_text>());
299 : : }
300 : : else
301 : : {
302 : 2 : d_->value_numeric_ = source.d_->value_numeric_;
303 : : }
304 : :
305 : 5 : d_->value_text_ = source.d_->value_text_;
306 : 5 : d_->formula_ = source.d_->formula_;
307 : :
308 : : // Copy external hyperlinks; internal hyperlinks (cell/range references)
309 : : // are not yet implemented as they would need worksheet title remapping.
310 : : // TODO: implement internal hyperlink remapping.
311 [ + + ]: 5 : if (source.has_hyperlink())
312 : : {
313 [ + ]: 1 : auto copy_hyperlink = source.hyperlink();
314 : :
315 [ + + - ]: 1 : if (copy_hyperlink.external())
316 : : {
317 [ + + + ]: 1 : hyperlink(copy_hyperlink.url(), copy_hyperlink.display());
318 : : }
319 : : }
320 : :
321 [ + + ]: 5 : if (source.has_format())
322 : : {
323 [ + + ]: 1 : format(source.format());
324 : : }
325 : : else
326 : : {
327 : 4 : d_->format_.clear();
328 : : }
329 : 5 : }
330 : :
331 : 3 : void cell::value(const date &d)
332 : : {
333 : 3 : d_->type_ = type::number;
334 : 3 : d_->value_numeric_ = d.to_number(base_date());
335 [ + + ]: 3 : number_format(number_format::date_yyyymmdd2());
336 : 3 : }
337 : :
338 : 5 : void cell::value(const datetime &d)
339 : : {
340 : 5 : d_->type_ = type::number;
341 : 5 : d_->value_numeric_ = d.to_number(base_date());
342 [ + + ]: 5 : number_format(number_format::date_datetime());
343 : 5 : }
344 : :
345 : 2 : void cell::value(const time &t)
346 : : {
347 : 2 : d_->type_ = type::number;
348 : 2 : d_->value_numeric_ = t.to_number();
349 [ + + ]: 2 : number_format(number_format::date_time6());
350 : 2 : }
351 : :
352 : 2 : void cell::value(const timedelta &t)
353 : : {
354 : 2 : d_->type_ = type::number;
355 : 2 : d_->value_numeric_ = t.to_number();
356 [ + + + ]: 2 : number_format(xlnt::number_format("[hh]:mm:ss"));
357 : 2 : }
358 : :
359 : 7 : row_t cell::row() const
360 : : {
361 : 7 : return d_->row_;
362 : : }
363 : :
364 : 39 : column_t cell::column() const
365 : : {
366 : 39 : return d_->column_;
367 : : }
368 : :
369 : 1 : column_t::index_t cell::column_index() const
370 : : {
371 : 1 : return d_->column_.index;
372 : : }
373 : :
374 : 774 : void cell::merged(bool merged)
375 : : {
376 : 774 : d_->is_merged_ = merged;
377 : 774 : }
378 : :
379 :UBC 0 : bool cell::is_merged() const
380 : : {
381 : 0 : return d_->is_merged_;
382 : : }
383 : :
384 :CBC 964 : bool cell::phonetics_visible() const
385 : : {
386 : 964 : return d_->phonetics_visible_;
387 : : }
388 : :
389 : 2 : void cell::show_phonetics(bool phonetics)
390 : : {
391 : 2 : d_->phonetics_visible_ = phonetics;
392 : 2 : }
393 : :
394 : 7 : bool cell::is_date() const
395 : : {
396 : 7 : return data_type() == type::number
397 [ + - ]: 4 : && has_format()
398 [ + + + + : 11 : && number_format().is_date_format();
+ + + + -
- ]
399 : : }
400 : :
401 : 1124 : cell_reference cell::reference() const
402 : : {
403 [ + ]: 1124 : return {d_->column_, d_->row_};
404 : : }
405 : :
406 : 17 : bool cell::compare(const cell &other, bool compare_by_reference) const
407 : : {
408 [ + - ]: 17 : if (compare_by_reference)
409 : : {
410 : 17 : return d_ == other.d_;
411 : : }
412 : : else
413 : : {
414 :UBC 0 : return *d_ == *other.d_;
415 : : }
416 : : }
417 : :
418 :CBC 17 : bool cell::operator==(const cell &comparand) const
419 : : {
420 : 17 : return compare(comparand, true);
421 : : }
422 : :
423 : 2 : bool cell::operator!=(const cell &comparand) const
424 : : {
425 : 2 : return !(*this == comparand);
426 : : }
427 : :
428 : 1 : cell &cell::operator=(const cell &rhs) = default;
429 : :
430 : 65 : hyperlink cell::hyperlink() const
431 : : {
432 [ + + ]: 65 : return xlnt::hyperlink(&d_->hyperlink_.get());
433 : : }
434 : :
435 : 41 : void cell::hyperlink(const std::string &url, const std::string &display)
436 : : {
437 [ + + ]: 41 : if (url.empty())
438 : : {
439 [ + ]: 1 : throw invalid_parameter();
440 : : }
441 : :
442 [ + ]: 40 : auto ws = worksheet();
443 [ + + ]: 40 : auto &manifest = ws.workbook().manifest();
444 : :
445 [ + ]: 40 : d_->hyperlink_ = detail::hyperlink_impl();
446 : :
447 : : // check for existing relationships
448 [ + + ]: 40 : auto relationships = manifest.relationships(ws.path(), relationship_type::hyperlink);
449 [ + ]: 40 : auto relation = std::find_if(relationships.cbegin(), relationships.cend(),
450 : 57 : [&url](xlnt::relationship rel) { return rel.target().path().string() == url; });
451 [ + + ]: 40 : if (relation != relationships.end())
452 : : {
453 [ + + ]: 30 : d_->hyperlink_.get().relationship = *relation;
454 : : }
455 : : else
456 : : { // register a new relationship
457 : : auto rel_id = manifest.register_relationship(
458 [ + + + ]: 20 : uri(ws.path().string()),
459 : : relationship_type::hyperlink,
460 [ + ]: 20 : uri(url),
461 [ + ]: 10 : target_mode::external);
462 : : // TODO: make manifest::register_relationship return the created relationship instead of rel id
463 [ + + + ]: 10 : d_->hyperlink_.get().relationship = manifest.relationship(ws.path(), rel_id);
464 : 10 : }
465 : : // if a value is already present, the display string is ignored
466 [ + + ]: 40 : if (has_value())
467 : : {
468 [ + + ]: 34 : d_->hyperlink_.get().display.set(to_string());
469 : : }
470 : : else
471 : : {
472 [ + + + + ]: 6 : d_->hyperlink_.get().display.set(display.empty() ? url : display);
473 [ + + + ]: 6 : value(hyperlink().display());
474 : : }
475 : 40 : }
476 : :
477 : 4 : void cell::hyperlink(xlnt::cell target, const std::string &display)
478 : : {
479 : : // TODO: should this computed value be a method on a cell?
480 [ + + + + : 4 : const auto cell_address = target.worksheet().title() + "!" + target.reference().to_string();
+ + ]
481 : :
482 [ + ]: 4 : d_->hyperlink_ = detail::hyperlink_impl();
483 [ + ]: 8 : d_->hyperlink_.get().relationship = xlnt::relationship("", relationship_type::hyperlink,
484 [ + + + + : 28 : uri(""), uri(cell_address), target_mode::internal);
+ ]
485 : : // if a value is already present, the display string is ignored
486 [ + + ]: 4 : if (has_value())
487 : : {
488 [ + + ]: 1 : d_->hyperlink_.get().display.set(to_string());
489 : : }
490 : : else
491 : : {
492 [ + + + + ]: 3 : d_->hyperlink_.get().display.set(display.empty() ? cell_address : display);
493 [ + + + ]: 3 : value(hyperlink().display());
494 : : }
495 : 4 : }
496 : :
497 : 3 : void cell::hyperlink(xlnt::range target, const std::string &display)
498 : : {
499 : : // TODO: should this computed value be a method on a cell?
500 [ + + + + : 3 : const auto range_address = target.target_worksheet().title() + "!" + target.reference().to_string();
+ + ]
501 : :
502 [ + ]: 3 : d_->hyperlink_ = detail::hyperlink_impl();
503 [ + ]: 6 : d_->hyperlink_.get().relationship = xlnt::relationship("", relationship_type::hyperlink,
504 [ + + + + : 21 : uri(""), uri(range_address), target_mode::internal);
+ ]
505 : :
506 : : // if a value is already present, the display string is ignored
507 [ + + ]: 3 : if (has_value())
508 : : {
509 [ + + ]: 1 : d_->hyperlink_.get().display.set(to_string());
510 : : }
511 : : else
512 : : {
513 [ + + + + ]: 2 : d_->hyperlink_.get().display.set(display.empty() ? range_address : display);
514 [ + + + ]: 2 : value(hyperlink().display());
515 : : }
516 : 3 : }
517 : :
518 : 21 : void cell::formula(const std::string &formula)
519 : : {
520 [ + + ]: 21 : if (formula.empty())
521 : : {
522 : 1 : return clear_formula();
523 : : }
524 : :
525 [ + + ]: 20 : if (formula[0] == '=')
526 : : {
527 [ + ]: 8 : d_->formula_ = formula.substr(1);
528 : : }
529 : : else
530 : : {
531 : 12 : d_->formula_ = formula;
532 : : }
533 : :
534 [ + + ]: 20 : worksheet().register_calc_chain_in_manifest();
535 : : }
536 : :
537 : 2208 : bool cell::has_formula() const
538 : : {
539 : 2208 : return d_->formula_.is_set();
540 : : }
541 : :
542 : 19 : std::string cell::formula() const
543 : : {
544 : 19 : return d_->formula_.get();
545 : : }
546 : :
547 : 794 : void cell::clear_formula()
548 : : {
549 [ + + ]: 794 : if (has_formula())
550 : : {
551 : 18 : d_->formula_.clear();
552 [ + + ]: 18 : worksheet().garbage_collect_formulae();
553 : : }
554 : 794 : }
555 : :
556 : 21 : std::string cell::error() const
557 : : {
558 [ + + ]: 21 : if (d_->type_ != type::error)
559 : : {
560 [ + + ]: 21 : throw xlnt::exception("called error() when cell type is not error");
561 : : }
562 : 14 : return value<std::string>();
563 : : }
564 : :
565 : 17 : void cell::error(const std::string &error)
566 : : {
567 [ + + + + : 17 : if (error.length() == 0 || error[0] != '#')
+ + ]
568 : : {
569 [ + ]: 2 : throw invalid_data_type();
570 : : }
571 : :
572 : 15 : d_->value_text_.plain_text(error, false);
573 : 15 : d_->type_ = type::error;
574 : 15 : }
575 : :
576 : 1 : cell cell::offset(int column, int row)
577 : : {
578 [ + + + + ]: 1 : return worksheet().cell(reference().make_offset(column, row));
579 : : }
580 : :
581 : 591 : worksheet cell::worksheet()
582 : : {
583 : 591 : return xlnt::worksheet(d_->parent_);
584 : : }
585 : :
586 : 266 : const worksheet cell::worksheet() const
587 : : {
588 : 266 : return xlnt::worksheet(d_->parent_);
589 : : }
590 : :
591 : 467 : workbook cell::workbook()
592 : : {
593 [ + + ]: 467 : return worksheet().workbook();
594 : : }
595 : :
596 : 209 : const workbook cell::workbook() const
597 : : {
598 [ + + ]: 209 : return worksheet().workbook();
599 : : }
600 : :
601 : 35 : std::pair<int, int> cell::anchor() const
602 : : {
603 : 35 : double left = 0;
604 : :
605 [ + + + + : 35 : for (column_t column_index = 1; column_index <= d_->column_ - 1; column_index++)
- + ]
606 : : {
607 [ # # # ]:UBC 0 : left += worksheet().column_width(column_index);
608 : : }
609 : :
610 :CBC 35 : double top = 0;
611 : :
612 [ + + ]: 51 : for (row_t row_index = 1; row_index <= d_->row_ - 1; row_index++)
613 : : {
614 [ + + ]: 16 : top += worksheet().row_height(row_index);
615 : : }
616 : :
617 : 35 : return {static_cast<int>(left), static_cast<int>(top)};
618 : : }
619 : :
620 : 2983 : cell::type cell::data_type() const
621 : : {
622 : 2983 : return d_->type_;
623 : : }
624 : :
625 : 151 : void cell::data_type(type t)
626 : : {
627 : 151 : d_->type_ = t;
628 : 151 : }
629 : :
630 : 50 : number_format cell::computed_number_format() const
631 : : {
632 : 50 : return xlnt::number_format();
633 : : }
634 : :
635 :UBC 0 : font cell::computed_font() const
636 : : {
637 : 0 : return xlnt::font();
638 : : }
639 : :
640 : 0 : fill cell::computed_fill() const
641 : : {
642 : 0 : return xlnt::fill();
643 : : }
644 : :
645 : 0 : border cell::computed_border() const
646 : : {
647 : 0 : return xlnt::border();
648 : : }
649 : :
650 : 0 : alignment cell::computed_alignment() const
651 : : {
652 : 0 : return xlnt::alignment();
653 : : }
654 : :
655 : 0 : protection cell::computed_protection() const
656 : : {
657 [ # ]: 0 : return xlnt::protection();
658 : : }
659 : :
660 :CBC 767 : void cell::clear_value()
661 : : {
662 : 767 : d_->value_numeric_ = 0;
663 : 767 : d_->value_text_.clear();
664 : 767 : d_->type_ = cell::type::empty;
665 : 767 : clear_formula();
666 : 767 : }
667 : :
668 : : template <>
669 : 7 : bool cell::value() const
670 : : {
671 : 7 : return d_->value_numeric_ != 0.0;
672 : : }
673 : :
674 : : template <>
675 : 24 : int cell::value() const
676 : : {
677 : 24 : return static_cast<int>(d_->value_numeric_);
678 : : }
679 : :
680 : : template <>
681 : 1 : long long int cell::value() const
682 : : {
683 : 1 : return static_cast<long long int>(d_->value_numeric_);
684 : : }
685 : :
686 : : template <>
687 : 1 : unsigned int cell::value() const
688 : : {
689 : 1 : return static_cast<unsigned int>(d_->value_numeric_);
690 : : }
691 : :
692 : : template <>
693 : 1 : unsigned long long cell::value() const
694 : : {
695 : 1 : return static_cast<unsigned long long>(d_->value_numeric_);
696 : : }
697 : :
698 : : template <>
699 : 1 : float cell::value() const
700 : : {
701 : 1 : return static_cast<float>(d_->value_numeric_);
702 : : }
703 : :
704 : : template <>
705 : 67 : double cell::value() const
706 : : {
707 : 67 : return static_cast<double>(d_->value_numeric_);
708 : : }
709 : :
710 : : template <>
711 : 3 : time cell::value() const
712 : : {
713 : 3 : return time::from_number(d_->value_numeric_);
714 : : }
715 : :
716 : : template <>
717 :UBC 0 : datetime cell::value() const
718 : : {
719 : 0 : return datetime::from_number(d_->value_numeric_, base_date());
720 : : }
721 : :
722 : : template <>
723 : 0 : date cell::value() const
724 : : {
725 : 0 : return date::from_number(static_cast<int>(d_->value_numeric_), base_date());
726 : : }
727 : :
728 : : template <>
729 : 0 : timedelta cell::value() const
730 : : {
731 : 0 : return timedelta::from_number(d_->value_numeric_);
732 : : }
733 : :
734 :CBC 1 : void cell::alignment(const class alignment &alignment_)
735 : : {
736 [ - + - + : 1 : auto new_format = has_format() ? modifiable_format() : workbook().create_format();
+ + - -
- ]
737 [ + + + ]: 1 : format(new_format.alignment(alignment_, optional<bool>(true)));
738 : 1 : }
739 : :
740 : 1 : void cell::border(const class border &border_)
741 : : {
742 [ - + - + : 1 : auto new_format = has_format() ? modifiable_format() : workbook().create_format();
+ + - -
- ]
743 [ + + + ]: 1 : format(new_format.border(border_, optional<bool>(true)));
744 : 1 : }
745 : :
746 : 13 : void cell::fill(const class fill &fill_)
747 : : {
748 [ + + + + : 13 : auto new_format = has_format() ? modifiable_format() : workbook().create_format();
+ + + -
- ]
749 [ + + + ]: 13 : format(new_format.fill(fill_, optional<bool>(true)));
750 : 13 : }
751 : :
752 : 26 : void cell::font(const class font &font_)
753 : : {
754 [ + + + + : 26 : auto new_format = has_format() ? modifiable_format() : workbook().create_format();
+ + + -
- ]
755 [ + + + ]: 26 : format(new_format.font(font_, optional<bool>(true)));
756 : 26 : }
757 : :
758 : 21 : void cell::number_format(const class number_format &number_format_)
759 : : {
760 [ + + + + : 21 : auto new_format = has_format() ? modifiable_format() : workbook().create_format();
+ + + -
- ]
761 [ + + + ]: 21 : format(new_format.number_format(number_format_, optional<bool>(true)));
762 : 21 : }
763 : :
764 : 1 : void cell::protection(const class protection &protection_)
765 : : {
766 [ - + - + : 1 : auto new_format = has_format() ? modifiable_format() : workbook().create_format();
+ + - -
- ]
767 [ + + + ]: 1 : format(new_format.protection(protection_, optional<bool>(true)));
768 : 1 : }
769 : :
770 : : template <>
771 : 219 : std::string cell::value() const
772 : : {
773 [ + + ]: 219 : return value<rich_text>().plain_text();
774 : : }
775 : :
776 : : template <>
777 : 225 : rich_text cell::value() const
778 : : {
779 [ + + ]: 225 : if (data_type() == cell::type::shared_string)
780 : : {
781 [ + + + ]: 196 : return workbook().shared_strings(static_cast<std::size_t>(d_->value_numeric_));
782 : : }
783 : :
784 : 29 : return d_->value_text_;
785 : : }
786 : :
787 : 115 : bool cell::has_value() const
788 : : {
789 : 115 : return d_->type_ != cell::type::empty;
790 : : }
791 : :
792 : 50 : std::string cell::to_string() const
793 : : {
794 [ + ]: 50 : auto nf = computed_number_format();
795 : :
796 [ + + + + : 50 : switch (data_type())
- ]
797 : : {
798 : 2 : case cell::type::empty:
799 [ + ]: 4 : return "";
800 : 5 : case cell::type::date:
801 : : case cell::type::number:
802 [ + + ]: 5 : return nf.format(value<double>(), base_date());
803 : 39 : case cell::type::inline_string:
804 : : case cell::type::shared_string:
805 : : case cell::type::formula_string:
806 : : case cell::type::error:
807 [ + + ]: 39 : return nf.format(value<std::string>());
808 : 4 : case cell::type::boolean:
809 [ + + + ]: 8 : return value<double>() == 0.0 ? "FALSE" : "TRUE";
810 : : }
811 : :
812 [ # ]:UBC 0 : return "";
813 :CBC 50 : }
814 : :
815 : 1062 : bool cell::has_format() const
816 : : {
817 : 1062 : return d_->format_.is_set();
818 : : }
819 : :
820 : 108 : void cell::format(const class format new_format)
821 : : {
822 : : // Check if format belongs to a different workbook (dangling pointer risk)
823 [ + + + + ]: 108 : if (!workbook().owns_format(new_format))
824 : : {
825 : 2 : copy_format_from_other_workbook(new_format);
826 : 2 : return;
827 : : }
828 : :
829 : : // Same workbook: direct assignment (original behavior)
830 : 106 : d_->format_ = new_format.d_;
831 : : }
832 : :
833 : 2 : void cell::copy_format_from_other_workbook(const class format &source_format)
834 : : {
835 [ + + ]: 2 : auto cloned_format = workbook().clone_format_from(source_format);
836 : :
837 : : // Use the cloned format
838 [ + ]: 2 : d_->format_ = cloned_format.d_;
839 : 2 : }
840 : :
841 : 13 : calendar cell::base_date() const
842 : : {
843 [ + + ]: 13 : return workbook().base_date();
844 : : }
845 : :
846 : 4 : bool operator==(std::nullptr_t, const cell &cell)
847 : : {
848 : 4 : return cell.data_type() == cell::type::empty;
849 : : }
850 : :
851 : 2 : bool operator==(const cell &cell, std::nullptr_t)
852 : : {
853 : 2 : return nullptr == cell;
854 : : }
855 : :
856 :UBC 0 : bool operator!=(std::nullptr_t, const cell &cell)
857 : : {
858 : 0 : return !(nullptr == cell);
859 : : }
860 : :
861 : 0 : bool operator!=(const cell &cell, std::nullptr_t)
862 : : {
863 : 0 : return nullptr != cell;
864 : : }
865 : :
866 :CBC 6 : std::ostream &operator<<(std::ostream &stream, const xlnt::cell &cell)
867 : : {
868 [ + + ]: 6 : return stream << cell.to_string();
869 : : }
870 : :
871 : 23 : void cell::value(const std::string &value_string, bool infer_type)
872 : : {
873 [ + ]: 23 : value(value_string);
874 : :
875 [ + - - + : 23 : if (!infer_type || value_string.empty())
- + ]
876 : : {
877 : 9 : return;
878 : : }
879 : :
880 [ + + + + : 23 : if (value_string.front() == '=' && value_string.size() > 1)
+ + ]
881 : : {
882 [ + ]: 2 : formula(value_string);
883 : 2 : return;
884 : : }
885 : :
886 [ + + + - : 21 : if (value_string.front() == '#' && value_string.size() > 1)
+ + ]
887 : : {
888 [ + ]: 7 : error(value_string);
889 : 7 : return;
890 : : }
891 : :
892 [ + ]: 14 : auto percentage = cast_percentage(value_string);
893 : :
894 [ + + ]: 14 : if (percentage.first)
895 : : {
896 : 1 : d_->value_numeric_ = percentage.second;
897 : 1 : d_->type_ = cell::type::number;
898 [ + + ]: 1 : number_format(xlnt::number_format::percentage());
899 : : }
900 : : else
901 : : {
902 [ + ]: 13 : auto time = cast_time(value_string);
903 : :
904 [ + + ]: 13 : if (time.first)
905 : : {
906 : 3 : d_->type_ = cell::type::number;
907 [ + + ]: 3 : number_format(number_format::date_time6());
908 [ + ]: 3 : d_->value_numeric_ = time.second.to_number();
909 : : }
910 : : else
911 : : {
912 [ + ]: 10 : auto numeric = cast_numeric(value_string);
913 : :
914 [ + + ]: 10 : if (numeric.first)
915 : : {
916 : 8 : d_->value_numeric_ = numeric.second;
917 : 8 : d_->type_ = cell::type::number;
918 : : }
919 : : }
920 : : }
921 : : }
922 : :
923 : 2 : void cell::clear_format()
924 : : {
925 [ + - ]: 2 : if (d_->format_.is_set())
926 : 2 : d_->format_.clear();
927 : 2 : }
928 : :
929 : 2 : void cell::clear_style()
930 : : {
931 [ + - ]: 2 : if (has_format())
932 : : {
933 [ + + ]: 2 : modifiable_format().clear_style();
934 : : }
935 : 2 : }
936 : :
937 : 4 : void cell::style(const class style &new_style)
938 : : {
939 [ + + + + : 4 : auto new_format = has_format() ? format() : workbook().create_format();
+ + + -
- ]
940 : :
941 [ + + ]: 4 : new_format.border(new_style.border());
942 [ + + ]: 4 : new_format.fill(new_style.fill());
943 [ + + ]: 4 : new_format.font(new_style.font());
944 [ + + ]: 4 : new_format.number_format(new_style.number_format());
945 : :
946 [ + + + ]: 4 : format(new_format.style(new_style));
947 : 4 : }
948 : :
949 : 2 : void cell::style(const std::string &style_name)
950 : : {
951 [ + + + ]: 3 : style(workbook().style(style_name));
952 : 1 : }
953 : :
954 : 7 : style cell::style()
955 : : {
956 [ + - + + : 7 : if (!has_format() || !format().has_style())
+ + + - +
+ - - ]
957 : : {
958 [ + ]: 1 : throw invalid_attribute();
959 : : }
960 : :
961 [ + ]: 6 : auto f = format();
962 : :
963 [ + ]: 12 : return f.style();
964 : 6 : }
965 : :
966 :UBC 0 : const style cell::style() const
967 : : {
968 [ # # # # : 0 : if (!has_format() || !format().has_style())
# # # # #
# # # ]
969 : : {
970 [ # ]: 0 : throw invalid_attribute();
971 : : }
972 : :
973 [ # # ]: 0 : return format().style();
974 : : }
975 : :
976 :CBC 6 : bool cell::has_style() const
977 : : {
978 [ + + + + : 6 : return has_format() && format().has_style();
+ + + + -
- ]
979 : : }
980 : :
981 : 11 : format cell::modifiable_format()
982 : : {
983 [ - + ]: 11 : if (!d_->format_.is_set())
984 : : {
985 [ # ]:UBC 0 : throw invalid_attribute();
986 : : }
987 : :
988 [ + + ]:CBC 11 : return xlnt::format(d_->format_);
989 : : }
990 : :
991 : 400 : const format cell::format() const
992 : : {
993 [ - + ]: 400 : if (!d_->format_.is_set())
994 : : {
995 [ # ]:UBC 0 : throw invalid_attribute();
996 : : }
997 : :
998 [ + + ]:CBC 400 : return xlnt::format(d_->format_);
999 : : }
1000 : :
1001 : 1 : alignment cell::alignment() const
1002 : : {
1003 [ + + ]: 1 : return format().alignment();
1004 : : }
1005 : :
1006 : 1 : border cell::border() const
1007 : : {
1008 [ + + ]: 1 : return format().border();
1009 : : }
1010 : :
1011 : 3 : fill cell::fill() const
1012 : : {
1013 [ + + ]: 3 : return format().fill();
1014 : : }
1015 : :
1016 : 12 : font cell::font() const
1017 : : {
1018 [ + + ]: 12 : return format().font();
1019 : : }
1020 : :
1021 : 17 : number_format cell::number_format() const
1022 : : {
1023 [ + + ]: 17 : return format().number_format();
1024 : : }
1025 : :
1026 : 1 : protection cell::protection() const
1027 : : {
1028 [ + + ]: 1 : return format().protection();
1029 : : }
1030 : :
1031 : 974 : bool cell::has_hyperlink() const
1032 : : {
1033 : 974 : return d_->hyperlink_.is_set();
1034 : : }
1035 : :
1036 : : // comment
1037 : :
1038 : 1049 : bool cell::has_comment() const
1039 : : {
1040 : 1049 : return d_->comment_.is_set();
1041 : : }
1042 : :
1043 : 1 : void cell::clear_comment()
1044 : : {
1045 [ + - ]: 1 : if (has_comment())
1046 : : {
1047 [ + + + ]: 1 : d_->parent_->comments_.erase(reference().to_string());
1048 : 1 : d_->comment_.clear();
1049 : : }
1050 : 1 : }
1051 : :
1052 : 56 : class comment cell::comment() const
1053 : : {
1054 [ + + ]: 56 : if (!has_comment())
1055 : : {
1056 [ + + ]: 6 : throw xlnt::exception("cell has no comment");
1057 : : }
1058 : :
1059 : 54 : return *d_->comment_.get();
1060 : : }
1061 : :
1062 :UBC 0 : void cell::comment(const std::string &text, const std::string &author)
1063 : : {
1064 [ # # ]: 0 : comment(xlnt::comment(text, author));
1065 : 0 : }
1066 : :
1067 :CBC 4 : void cell::comment(const std::string &text, const class font &comment_font, const std::string &author)
1068 : : {
1069 [ + + + ]: 4 : comment(xlnt::comment(xlnt::rich_text(text, comment_font), author));
1070 : 4 : }
1071 : :
1072 : 34 : void cell::comment(const class comment &new_comment)
1073 : : {
1074 [ - + ]: 34 : if (has_comment())
1075 : : {
1076 [ # # ]:UBC 0 : *d_->comment_.get() = new_comment;
1077 : : }
1078 : : else
1079 : : {
1080 [ + + + + ]:CBC 34 : d_->parent_->comments_[reference().to_string()] = new_comment;
1081 [ + + + ]: 34 : d_->comment_.set(&d_->parent_->comments_[reference().to_string()]);
1082 : : }
1083 : :
1084 : : // offset comment 5 pixels down and 5 pixels right of the top right corner of the cell
1085 [ + ]: 34 : auto cell_position = anchor();
1086 [ + ]: 34 : cell_position.first += static_cast<int>(width()) + 5;
1087 : 34 : cell_position.second += 5;
1088 : :
1089 [ + + ]: 34 : d_->comment_.get()->position(cell_position.first, cell_position.second);
1090 : :
1091 [ + + ]: 34 : worksheet().register_comments_in_manifest();
1092 : 34 : }
1093 : :
1094 : 34 : double cell::width() const
1095 : : {
1096 [ + + ]: 34 : return worksheet().column_width(column());
1097 : : }
1098 : :
1099 :UBC 0 : double cell::height() const
1100 : : {
1101 [ # # ]: 0 : return worksheet().row_height(row());
1102 : : }
1103 : :
1104 : : } // namespace xlnt
|