1 // Copyright (c) 2014-2020 Thomas Fussell
2 // Copyright (c) 2010-2015 openpyxl
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, WRISING 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 <algorithm>
26 #include <cmath>
27 #include <sstream>
28
29 #include <xlnt/cell/cell.hpp>
30 #include <xlnt/cell/cell_reference.hpp>
31 #include <xlnt/cell/comment.hpp>
32 #include <xlnt/cell/hyperlink.hpp>
33 #include <xlnt/cell/rich_text.hpp>
34 #include <xlnt/packaging/manifest.hpp>
35 #include <xlnt/packaging/relationship.hpp>
36 #include <xlnt/styles/alignment.hpp>
37 #include <xlnt/styles/border.hpp>
38 #include <xlnt/styles/color.hpp>
39 #include <xlnt/styles/fill.hpp>
40 #include <xlnt/styles/font.hpp>
41 #include <xlnt/styles/format.hpp>
42 #include <xlnt/styles/number_format.hpp>
43 #include <xlnt/styles/protection.hpp>
44 #include <xlnt/styles/style.hpp>
45 #include <xlnt/utils/date.hpp>
46 #include <xlnt/utils/datetime.hpp>
47 #include <xlnt/utils/exceptions.hpp>
48 #include <xlnt/utils/time.hpp>
49 #include <xlnt/utils/timedelta.hpp>
50 #include <xlnt/workbook/workbook.hpp>
51 #include <xlnt/worksheet/column_properties.hpp>
52 #include <xlnt/worksheet/phonetic_pr.hpp>
53 #include <xlnt/worksheet/row_properties.hpp>
54 #include <xlnt/worksheet/worksheet.hpp>
55 #include <detail/implementations/cell_impl.hpp>
56 #include <detail/implementations/format_impl.hpp>
57 #include <detail/implementations/hyperlink_impl.hpp>
58 #include <detail/implementations/stylesheet.hpp>
59 #include <detail/implementations/worksheet_impl.hpp>
60 #include <xlnt/utils/numeric.hpp>
61
62 namespace {
63
cast_numeric(const std::string & s)64 std::pair<bool, double> cast_numeric(const std::string &s)
65 {
66 xlnt::detail::number_serialiser ser;
67 ptrdiff_t len_convert;
68 double result = ser.deserialise(s, &len_convert);
69 return (len_convert != static_cast<ptrdiff_t>(s.size()))
70 ? std::make_pair(false, 0.0)
71 : std::make_pair(true, result);
72 }
73
cast_percentage(const std::string & s)74 std::pair<bool, double> cast_percentage(const std::string &s)
75 {
76 if (s.back() == '%')
77 {
78 auto number = cast_numeric(s.substr(0, s.size() - 1));
79
80 if (number.first)
81 {
82 return {true, number.second / 100};
83 }
84 }
85
86 return {false, 0.0};
87 }
88
cast_time(const std::string & s)89 std::pair<bool, xlnt::time> cast_time(const std::string &s)
90 {
91 xlnt::time result;
92
93 std::vector<std::string> time_components;
94 std::size_t prev = 0;
95 auto colon_index = s.find(':');
96
97 while (colon_index != std::string::npos)
98 {
99 time_components.push_back(s.substr(prev, colon_index - prev));
100 prev = colon_index + 1;
101 colon_index = s.find(':', colon_index + 1);
102 }
103
104 time_components.push_back(s.substr(prev, colon_index - prev));
105
106 if (time_components.size() < 2 || time_components.size() > 3)
107 {
108 return {false, result};
109 }
110
111 std::vector<double> numeric_components;
112 xlnt::detail::number_serialiser ser;
113 for (auto component : time_components)
114 {
115 if (component.empty() || (component.substr(0, component.find('.')).size() > 2))
116 {
117 return {false, result};
118 }
119
120 for (auto d : component)
121 {
122 if (!(d >= '0' && d <= '9') && d != '.')
123 {
124 return {false, result};
125 }
126 }
127 auto numeric = ser.deserialise(component);
128
129 numeric_components.push_back(numeric);
130 }
131
132 result.hour = static_cast<int>(numeric_components[0]);
133 result.minute = static_cast<int>(numeric_components[1]);
134
135 if (std::fabs(static_cast<double>(result.minute) - numeric_components[1]) > std::numeric_limits<double>::epsilon())
136 {
137 result.minute = result.hour;
138 result.hour = 0;
139 result.second = static_cast<int>(numeric_components[1]);
140 result.microsecond = static_cast<int>((numeric_components[1] - result.second) * 1E6);
141 }
142 else if (numeric_components.size() > 2)
143 {
144 result.second = static_cast<int>(numeric_components[2]);
145 result.microsecond = static_cast<int>((numeric_components[2] - result.second) * 1E6);
146 }
147
148 return {true, result};
149 }
150
151 } // namespace
152
153 namespace xlnt {
154
error_codes()155 const std::unordered_map<std::string, int> &cell::error_codes()
156 {
157 static const auto codes = std::unordered_map<std::string, int>{
158 {"#NULL!", 0},
159 {"#DIV/0!", 1},
160 {"#VALUE!", 2},
161 {"#REF!", 3},
162 {"#NAME?", 4},
163 {"#NUM!", 5},
164 {"#N/A!", 6}};
165
166 return codes;
167 }
168
check_string(const std::string & to_check)169 std::string cell::check_string(const std::string &to_check)
170 {
171 // so we can modify it
172 std::string s = to_check;
173
174 if (s.size() == 0)
175 {
176 return s;
177 }
178 else if (s.size() > 32767)
179 {
180 s = s.substr(0, 32767); // max string length in Excel
181 }
182
183 for (char c : s)
184 {
185 if (c >= 0 && (c <= 8 || c == 11 || c == 12 || (c >= 14 && c <= 31)))
186 {
187 throw illegal_character(c);
188 }
189 }
190
191 return s;
192 }
193
cell(detail::cell_impl * d)194 cell::cell(detail::cell_impl *d)
195 : d_(d)
196 {
197 }
198
garbage_collectible() const199 bool cell::garbage_collectible() const
200 {
201 return d_->is_garbage_collectible();
202 }
203
value(std::nullptr_t)204 void cell::value(std::nullptr_t)
205 {
206 clear_value();
207 }
208
value(bool boolean_value)209 void cell::value(bool boolean_value)
210 {
211 d_->type_ = type::boolean;
212 d_->value_numeric_ = boolean_value ? 1.0 : 0.0;
213 }
214
value(int int_value)215 void cell::value(int int_value)
216 {
217 d_->value_numeric_ = static_cast<double>(int_value);
218 d_->type_ = type::number;
219 }
220
value(unsigned int int_value)221 void cell::value(unsigned int int_value)
222 {
223 d_->value_numeric_ = static_cast<double>(int_value);
224 d_->type_ = type::number;
225 }
226
value(long long int int_value)227 void cell::value(long long int int_value)
228 {
229 d_->value_numeric_ = static_cast<double>(int_value);
230 d_->type_ = type::number;
231 }
232
value(unsigned long long int int_value)233 void cell::value(unsigned long long int int_value)
234 {
235 d_->value_numeric_ = static_cast<double>(int_value);
236 d_->type_ = type::number;
237 }
238
value(float float_value)239 void cell::value(float float_value)
240 {
241 d_->value_numeric_ = static_cast<double>(float_value);
242 d_->type_ = type::number;
243 }
244
value(double float_value)245 void cell::value(double float_value)
246 {
247 d_->value_numeric_ = static_cast<double>(float_value);
248 d_->type_ = type::number;
249 }
250
value(const std::string & s)251 void cell::value(const std::string &s)
252 {
253 value(rich_text(check_string(s)));
254 }
255
value(const rich_text & text)256 void cell::value(const rich_text &text)
257 {
258 check_string(text.plain_text());
259
260 d_->type_ = type::shared_string;
261 d_->value_numeric_ = static_cast<double>(workbook().add_shared_string(text));
262 }
263
value(const char * c)264 void cell::value(const char *c)
265 {
266 value(std::string(c));
267 }
268
value(const cell c)269 void cell::value(const cell c)
270 {
271 d_->type_ = c.d_->type_;
272 d_->value_numeric_ = c.d_->value_numeric_;
273 d_->value_text_ = c.d_->value_text_;
274 d_->hyperlink_ = c.d_->hyperlink_;
275 d_->formula_ = c.d_->formula_;
276 d_->format_ = c.d_->format_;
277 }
278
value(const date & d)279 void cell::value(const date &d)
280 {
281 d_->type_ = type::number;
282 d_->value_numeric_ = d.to_number(base_date());
283 number_format(number_format::date_yyyymmdd2());
284 }
285
value(const datetime & d)286 void cell::value(const datetime &d)
287 {
288 d_->type_ = type::number;
289 d_->value_numeric_ = d.to_number(base_date());
290 number_format(number_format::date_datetime());
291 }
292
value(const time & t)293 void cell::value(const time &t)
294 {
295 d_->type_ = type::number;
296 d_->value_numeric_ = t.to_number();
297 number_format(number_format::date_time6());
298 }
299
value(const timedelta & t)300 void cell::value(const timedelta &t)
301 {
302 d_->type_ = type::number;
303 d_->value_numeric_ = t.to_number();
304 number_format(xlnt::number_format("[hh]:mm:ss"));
305 }
306
row() const307 row_t cell::row() const
308 {
309 return d_->row_;
310 }
311
column() const312 column_t cell::column() const
313 {
314 return d_->column_;
315 }
316
column_index() const317 column_t::index_t cell::column_index() const
318 {
319 return d_->column_.index;
320 }
321
merged(bool merged)322 void cell::merged(bool merged)
323 {
324 d_->is_merged_ = merged;
325 }
326
is_merged() const327 bool cell::is_merged() const
328 {
329 return d_->is_merged_;
330 }
331
phonetics_visible() const332 bool cell::phonetics_visible() const
333 {
334 return d_->phonetics_visible_;
335 }
336
show_phonetics(bool phonetics)337 void cell::show_phonetics(bool phonetics)
338 {
339 d_->phonetics_visible_ = phonetics;
340 }
341
is_date() const342 bool cell::is_date() const
343 {
344 return data_type() == type::number
345 && has_format()
346 && number_format().is_date_format();
347 }
348
reference() const349 cell_reference cell::reference() const
350 {
351 return {d_->column_, d_->row_};
352 }
353
operator ==(const cell & comparand) const354 bool cell::operator==(const cell &comparand) const
355 {
356 return d_ == comparand.d_;
357 }
358
operator !=(const cell & comparand) const359 bool cell::operator!=(const cell &comparand) const
360 {
361 return d_ != comparand.d_;
362 }
363
364 cell &cell::operator=(const cell &rhs) = default;
365
hyperlink() const366 hyperlink cell::hyperlink() const
367 {
368 return xlnt::hyperlink(&d_->hyperlink_.get());
369 }
370
hyperlink(const std::string & url,const std::string & display)371 void cell::hyperlink(const std::string &url, const std::string &display)
372 {
373 if (url.empty())
374 {
375 throw invalid_parameter();
376 }
377
378 auto ws = worksheet();
379 auto &manifest = ws.workbook().manifest();
380
381 d_->hyperlink_ = detail::hyperlink_impl();
382
383 // check for existing relationships
384 auto relationships = manifest.relationships(ws.path(), relationship_type::hyperlink);
385 auto relation = std::find_if(relationships.cbegin(), relationships.cend(),
386 [&url](xlnt::relationship rel) { return rel.target().path().string() == url; });
387 if (relation != relationships.end())
388 {
389 d_->hyperlink_.get().relationship = *relation;
390 }
391 else
392 { // register a new relationship
393 auto rel_id = manifest.register_relationship(
394 uri(ws.path().string()),
395 relationship_type::hyperlink,
396 uri(url),
397 target_mode::external);
398 // TODO: make manifest::register_relationship return the created relationship instead of rel id
399 d_->hyperlink_.get().relationship = manifest.relationship(ws.path(), rel_id);
400 }
401 // if a value is already present, the display string is ignored
402 if (has_value())
403 {
404 d_->hyperlink_.get().display.set(to_string());
405 }
406 else
407 {
408 d_->hyperlink_.get().display.set(display.empty() ? url : display);
409 value(hyperlink().display());
410 }
411 }
412
hyperlink(xlnt::cell target,const std::string & display)413 void cell::hyperlink(xlnt::cell target, const std::string &display)
414 {
415 // TODO: should this computed value be a method on a cell?
416 const auto cell_address = target.worksheet().title() + "!" + target.reference().to_string();
417
418 d_->hyperlink_ = detail::hyperlink_impl();
419 d_->hyperlink_.get().relationship = xlnt::relationship("", relationship_type::hyperlink,
420 uri(""), uri(cell_address), target_mode::internal);
421 // if a value is already present, the display string is ignored
422 if (has_value())
423 {
424 d_->hyperlink_.get().display.set(to_string());
425 }
426 else
427 {
428 d_->hyperlink_.get().display.set(display.empty() ? cell_address : display);
429 value(hyperlink().display());
430 }
431 }
432
hyperlink(xlnt::range target,const std::string & display)433 void cell::hyperlink(xlnt::range target, const std::string &display)
434 {
435 // TODO: should this computed value be a method on a cell?
436 const auto range_address = target.target_worksheet().title() + "!" + target.reference().to_string();
437
438 d_->hyperlink_ = detail::hyperlink_impl();
439 d_->hyperlink_.get().relationship = xlnt::relationship("", relationship_type::hyperlink,
440 uri(""), uri(range_address), target_mode::internal);
441
442 // if a value is already present, the display string is ignored
443 if (has_value())
444 {
445 d_->hyperlink_.get().display.set(to_string());
446 }
447 else
448 {
449 d_->hyperlink_.get().display.set(display.empty() ? range_address : display);
450 value(hyperlink().display());
451 }
452 }
453
formula(const std::string & formula)454 void cell::formula(const std::string &formula)
455 {
456 if (formula.empty())
457 {
458 return clear_formula();
459 }
460
461 if (formula[0] == '=')
462 {
463 d_->formula_ = formula.substr(1);
464 }
465 else
466 {
467 d_->formula_ = formula;
468 }
469
470 worksheet().register_calc_chain_in_manifest();
471 }
472
has_formula() const473 bool cell::has_formula() const
474 {
475 return d_->formula_.is_set();
476 }
477
formula() const478 std::string cell::formula() const
479 {
480 return d_->formula_.get();
481 }
482
clear_formula()483 void cell::clear_formula()
484 {
485 if (has_formula())
486 {
487 d_->formula_.clear();
488 worksheet().garbage_collect_formulae();
489 }
490 }
491
error() const492 std::string cell::error() const
493 {
494 if (d_->type_ != type::error)
495 {
496 throw xlnt::exception("called error() when cell type is not error");
497 }
498 return value<std::string>();
499 }
500
error(const std::string & error)501 void cell::error(const std::string &error)
502 {
503 if (error.length() == 0 || error[0] != '#')
504 {
505 throw invalid_data_type();
506 }
507
508 d_->value_text_.plain_text(error, false);
509 d_->type_ = type::error;
510 }
511
offset(int column,int row)512 cell cell::offset(int column, int row)
513 {
514 return worksheet().cell(reference().make_offset(column, row));
515 }
516
worksheet()517 worksheet cell::worksheet()
518 {
519 return xlnt::worksheet(d_->parent_);
520 }
521
worksheet() const522 const worksheet cell::worksheet() const
523 {
524 return xlnt::worksheet(d_->parent_);
525 }
526
workbook()527 workbook &cell::workbook()
528 {
529 return worksheet().workbook();
530 }
531
workbook() const532 const workbook &cell::workbook() const
533 {
534 return worksheet().workbook();
535 }
536
anchor() const537 std::pair<int, int> cell::anchor() const
538 {
539 double left = 0;
540
541 for (column_t column_index = 1; column_index <= d_->column_ - 1; column_index++)
542 {
543 left += worksheet().column_width(column_index);
544 }
545
546 double top = 0;
547
548 for (row_t row_index = 1; row_index <= d_->row_ - 1; row_index++)
549 {
550 top += worksheet().row_height(row_index);
551 }
552
553 return {static_cast<int>(left), static_cast<int>(top)};
554 }
555
data_type() const556 cell::type cell::data_type() const
557 {
558 return d_->type_;
559 }
560
data_type(type t)561 void cell::data_type(type t)
562 {
563 d_->type_ = t;
564 }
565
computed_number_format() const566 number_format cell::computed_number_format() const
567 {
568 return xlnt::number_format();
569 }
570
computed_font() const571 font cell::computed_font() const
572 {
573 return xlnt::font();
574 }
575
computed_fill() const576 fill cell::computed_fill() const
577 {
578 return xlnt::fill();
579 }
580
computed_border() const581 border cell::computed_border() const
582 {
583 return xlnt::border();
584 }
585
computed_alignment() const586 alignment cell::computed_alignment() const
587 {
588 return xlnt::alignment();
589 }
590
computed_protection() const591 protection cell::computed_protection() const
592 {
593 return xlnt::protection();
594 }
595
clear_value()596 void cell::clear_value()
597 {
598 d_->value_numeric_ = 0;
599 d_->value_text_.clear();
600 d_->type_ = cell::type::empty;
601 clear_formula();
602 }
603
604 template <>
value() const605 XLNT_API bool cell::value() const
606 {
607 return d_->value_numeric_ != 0.0;
608 }
609
610 template <>
value() const611 XLNT_API int cell::value() const
612 {
613 return static_cast<int>(d_->value_numeric_);
614 }
615
616 template <>
value() const617 XLNT_API long long int cell::value() const
618 {
619 return static_cast<long long int>(d_->value_numeric_);
620 }
621
622 template <>
value() const623 XLNT_API unsigned int cell::value() const
624 {
625 return static_cast<unsigned int>(d_->value_numeric_);
626 }
627
628 template <>
value() const629 XLNT_API unsigned long long cell::value() const
630 {
631 return static_cast<unsigned long long>(d_->value_numeric_);
632 }
633
634 template <>
value() const635 XLNT_API float cell::value() const
636 {
637 return static_cast<float>(d_->value_numeric_);
638 }
639
640 template <>
value() const641 XLNT_API double cell::value() const
642 {
643 return static_cast<double>(d_->value_numeric_);
644 }
645
646 template <>
value() const647 XLNT_API time cell::value() const
648 {
649 return time::from_number(d_->value_numeric_);
650 }
651
652 template <>
value() const653 XLNT_API datetime cell::value() const
654 {
655 return datetime::from_number(d_->value_numeric_, base_date());
656 }
657
658 template <>
value() const659 XLNT_API date cell::value() const
660 {
661 return date::from_number(static_cast<int>(d_->value_numeric_), base_date());
662 }
663
664 template <>
value() const665 XLNT_API timedelta cell::value() const
666 {
667 return timedelta::from_number(d_->value_numeric_);
668 }
669
alignment(const class alignment & alignment_)670 void cell::alignment(const class alignment &alignment_)
671 {
672 auto new_format = has_format() ? modifiable_format() : workbook().create_format();
673 format(new_format.alignment(alignment_, optional<bool>(true)));
674 }
675
border(const class border & border_)676 void cell::border(const class border &border_)
677 {
678 auto new_format = has_format() ? modifiable_format() : workbook().create_format();
679 format(new_format.border(border_, optional<bool>(true)));
680 }
681
fill(const class fill & fill_)682 void cell::fill(const class fill &fill_)
683 {
684 auto new_format = has_format() ? modifiable_format() : workbook().create_format();
685 format(new_format.fill(fill_, optional<bool>(true)));
686 }
687
font(const class font & font_)688 void cell::font(const class font &font_)
689 {
690 auto new_format = has_format() ? modifiable_format() : workbook().create_format();
691 format(new_format.font(font_, optional<bool>(true)));
692 }
693
number_format(const class number_format & number_format_)694 void cell::number_format(const class number_format &number_format_)
695 {
696 auto new_format = has_format() ? modifiable_format() : workbook().create_format();
697 format(new_format.number_format(number_format_, optional<bool>(true)));
698 }
699
protection(const class protection & protection_)700 void cell::protection(const class protection &protection_)
701 {
702 auto new_format = has_format() ? modifiable_format() : workbook().create_format();
703 format(new_format.protection(protection_, optional<bool>(true)));
704 }
705
706 template <>
value() const707 XLNT_API std::string cell::value() const
708 {
709 return value<rich_text>().plain_text();
710 }
711
712 template <>
value() const713 XLNT_API rich_text cell::value() const
714 {
715 if (data_type() == cell::type::shared_string)
716 {
717 return workbook().shared_strings(static_cast<std::size_t>(d_->value_numeric_));
718 }
719
720 return d_->value_text_;
721 }
722
has_value() const723 bool cell::has_value() const
724 {
725 return d_->type_ != cell::type::empty;
726 }
727
to_string() const728 std::string cell::to_string() const
729 {
730 auto nf = computed_number_format();
731
732 switch (data_type())
733 {
734 case cell::type::empty:
735 return "";
736 case cell::type::date:
737 case cell::type::number:
738 return nf.format(value<double>(), base_date());
739 case cell::type::inline_string:
740 case cell::type::shared_string:
741 case cell::type::formula_string:
742 case cell::type::error:
743 return nf.format(value<std::string>());
744 case cell::type::boolean:
745 return value<double>() == 0.0 ? "FALSE" : "TRUE";
746 }
747
748 return "";
749 }
750
has_format() const751 bool cell::has_format() const
752 {
753 return d_->format_.is_set();
754 }
755
format(const class format new_format)756 void cell::format(const class format new_format)
757 {
758 if (has_format())
759 {
760 format().d_->references -= format().d_->references > 0 ? 1 : 0;
761 }
762
763 ++new_format.d_->references;
764 d_->format_ = new_format.d_;
765 }
766
base_date() const767 calendar cell::base_date() const
768 {
769 return workbook().base_date();
770 }
771
operator ==(std::nullptr_t,const cell & cell)772 bool operator==(std::nullptr_t, const cell &cell)
773 {
774 return cell.data_type() == cell::type::empty;
775 }
776
operator ==(const cell & cell,std::nullptr_t)777 bool operator==(const cell &cell, std::nullptr_t)
778 {
779 return nullptr == cell;
780 }
781
operator <<(std::ostream & stream,const xlnt::cell & cell)782 XLNT_API std::ostream &operator<<(std::ostream &stream, const xlnt::cell &cell)
783 {
784 return stream << cell.to_string();
785 }
786
value(const std::string & value_string,bool infer_type)787 void cell::value(const std::string &value_string, bool infer_type)
788 {
789 value(value_string);
790
791 if (!infer_type || value_string.empty())
792 {
793 return;
794 }
795
796 if (value_string.front() == '=' && value_string.size() > 1)
797 {
798 formula(value_string);
799 return;
800 }
801
802 if (value_string.front() == '#' && value_string.size() > 1)
803 {
804 error(value_string);
805 return;
806 }
807
808 auto percentage = cast_percentage(value_string);
809
810 if (percentage.first)
811 {
812 d_->value_numeric_ = percentage.second;
813 d_->type_ = cell::type::number;
814 number_format(xlnt::number_format::percentage());
815 }
816 else
817 {
818 auto time = cast_time(value_string);
819
820 if (time.first)
821 {
822 d_->type_ = cell::type::number;
823 number_format(number_format::date_time6());
824 d_->value_numeric_ = time.second.to_number();
825 }
826 else
827 {
828 auto numeric = cast_numeric(value_string);
829
830 if (numeric.first)
831 {
832 d_->value_numeric_ = numeric.second;
833 d_->type_ = cell::type::number;
834 }
835 }
836 }
837 }
838
clear_format()839 void cell::clear_format()
840 {
841 if (d_->format_.is_set())
842 {
843 format().d_->references -= format().d_->references > 0 ? 1 : 0;
844 d_->format_.clear();
845 }
846 }
847
clear_style()848 void cell::clear_style()
849 {
850 if (has_format())
851 {
852 modifiable_format().clear_style();
853 }
854 }
855
style(const class style & new_style)856 void cell::style(const class style &new_style)
857 {
858 auto new_format = has_format() ? format() : workbook().create_format();
859
860 new_format.border(new_style.border());
861 new_format.fill(new_style.fill());
862 new_format.font(new_style.font());
863 new_format.number_format(new_style.number_format());
864
865 format(new_format.style(new_style));
866 }
867
style(const std::string & style_name)868 void cell::style(const std::string &style_name)
869 {
870 style(workbook().style(style_name));
871 }
872
style()873 style cell::style()
874 {
875 if (!has_format() || !format().has_style())
876 {
877 throw invalid_attribute();
878 }
879
880 auto f = format();
881
882 return f.style();
883 }
884
style() const885 const style cell::style() const
886 {
887 if (!has_format() || !format().has_style())
888 {
889 throw invalid_attribute();
890 }
891
892 return format().style();
893 }
894
has_style() const895 bool cell::has_style() const
896 {
897 return has_format() && format().has_style();
898 }
899
modifiable_format()900 format cell::modifiable_format()
901 {
902 if (!d_->format_.is_set())
903 {
904 throw invalid_attribute();
905 }
906
907 return xlnt::format(d_->format_.get());
908 }
909
format() const910 const format cell::format() const
911 {
912 if (!d_->format_.is_set())
913 {
914 throw invalid_attribute();
915 }
916
917 return xlnt::format(d_->format_.get());
918 }
919
alignment() const920 alignment cell::alignment() const
921 {
922 return format().alignment();
923 }
924
border() const925 border cell::border() const
926 {
927 return format().border();
928 }
929
fill() const930 fill cell::fill() const
931 {
932 return format().fill();
933 }
934
font() const935 font cell::font() const
936 {
937 return format().font();
938 }
939
number_format() const940 number_format cell::number_format() const
941 {
942 return format().number_format();
943 }
944
protection() const945 protection cell::protection() const
946 {
947 return format().protection();
948 }
949
has_hyperlink() const950 bool cell::has_hyperlink() const
951 {
952 return d_->hyperlink_.is_set();
953 }
954
955 // comment
956
has_comment()957 bool cell::has_comment()
958 {
959 return d_->comment_.is_set();
960 }
961
clear_comment()962 void cell::clear_comment()
963 {
964 if (has_comment())
965 {
966 d_->parent_->comments_.erase(reference().to_string());
967 d_->comment_.clear();
968 }
969 }
970
comment()971 class comment cell::comment()
972 {
973 if (!has_comment())
974 {
975 throw xlnt::exception("cell has no comment");
976 }
977
978 return *d_->comment_.get();
979 }
980
comment(const std::string & text,const std::string & author)981 void cell::comment(const std::string &text, const std::string &author)
982 {
983 comment(xlnt::comment(text, author));
984 }
985
comment(const std::string & text,const class font & comment_font,const std::string & author)986 void cell::comment(const std::string &text, const class font &comment_font, const std::string &author)
987 {
988 comment(xlnt::comment(xlnt::rich_text(text, comment_font), author));
989 }
990
comment(const class comment & new_comment)991 void cell::comment(const class comment &new_comment)
992 {
993 if (has_comment())
994 {
995 *d_->comment_.get() = new_comment;
996 }
997 else
998 {
999 d_->parent_->comments_[reference().to_string()] = new_comment;
1000 d_->comment_.set(&d_->parent_->comments_[reference().to_string()]);
1001 }
1002
1003 // offset comment 5 pixels down and 5 pixels right of the top right corner of the cell
1004 auto cell_position = anchor();
1005 cell_position.first += static_cast<int>(width()) + 5;
1006 cell_position.second += 5;
1007
1008 d_->comment_.get()->position(cell_position.first, cell_position.second);
1009
1010 worksheet().register_comments_in_manifest();
1011 }
1012
width() const1013 double cell::width() const
1014 {
1015 return worksheet().column_width(column());
1016 }
1017
height() const1018 double cell::height() const
1019 {
1020 return worksheet().row_height(row());
1021 }
1022
1023 } // namespace xlnt
1024