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