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 <limits>
28 
29 #include <xlnt/cell/cell.hpp>
30 #include <xlnt/cell/cell_reference.hpp>
31 #include <xlnt/cell/index_types.hpp>
32 #include <xlnt/packaging/relationship.hpp>
33 #include <xlnt/utils/date.hpp>
34 #include <xlnt/utils/datetime.hpp>
35 #include <xlnt/utils/exceptions.hpp>
36 #include <xlnt/utils/numeric.hpp>
37 #include <xlnt/workbook/named_range.hpp>
38 #include <xlnt/workbook/workbook.hpp>
39 #include <xlnt/workbook/worksheet_iterator.hpp>
40 #include <xlnt/worksheet/cell_iterator.hpp>
41 #include <xlnt/worksheet/column_properties.hpp>
42 #include <xlnt/worksheet/header_footer.hpp>
43 #include <xlnt/worksheet/range.hpp>
44 #include <xlnt/worksheet/range_iterator.hpp>
45 #include <xlnt/worksheet/range_reference.hpp>
46 #include <xlnt/worksheet/row_properties.hpp>
47 #include <xlnt/worksheet/worksheet.hpp>
48 #include <detail/constants.hpp>
49 #include <detail/default_case.hpp>
50 #include <detail/implementations/cell_impl.hpp>
51 #include <detail/implementations/workbook_impl.hpp>
52 #include <detail/implementations/worksheet_impl.hpp>
53 #include <detail/unicode.hpp>
54 
55 namespace {
56 
points_to_pixels(double points,double dpi)57 int points_to_pixels(double points, double dpi)
58 {
59     return static_cast<int>(std::ceil(points * dpi / 72));
60 }
61 
62 } // namespace
63 
64 namespace xlnt {
65 
worksheet()66 worksheet::worksheet()
67     : d_(nullptr)
68 {
69 }
70 
worksheet(detail::worksheet_impl * d)71 worksheet::worksheet(detail::worksheet_impl *d)
72     : d_(d)
73 {
74 }
75 
worksheet(const worksheet & rhs)76 worksheet::worksheet(const worksheet &rhs)
77     : d_(rhs.d_)
78 {
79 }
80 
has_frozen_panes() const81 bool worksheet::has_frozen_panes() const
82 {
83     return !d_->views_.empty() && d_->views_.front().has_pane()
84         && (d_->views_.front().pane().state == pane_state::frozen
85             || d_->views_.front().pane().state == pane_state::frozen_split);
86 }
87 
create_named_range(const std::string & name,const std::string & reference_string)88 void worksheet::create_named_range(const std::string &name, const std::string &reference_string)
89 {
90     create_named_range(name, range_reference(reference_string));
91 }
92 
create_named_range(const std::string & name,const range_reference & reference)93 void worksheet::create_named_range(const std::string &name, const range_reference &reference)
94 {
95     try
96     {
97         auto temp = cell_reference::split_reference(name);
98 
99         // name is a valid reference, make sure it's outside the allowed range
100 
101         if (column_t(temp.first).index <= column_t("XFD").index && temp.second <= 1048576)
102         {
103             throw invalid_parameter(); //("named range name must be outside the range A1-XFD1048576");
104         }
105     }
106     catch (xlnt::invalid_cell_reference &)
107     {
108         // name is not a valid reference, that's good
109     }
110 
111     std::vector<named_range::target> targets;
112     targets.push_back({*this, reference});
113 
114     d_->named_ranges_[name] = xlnt::named_range(name, targets);
115 }
116 
operator [](const cell_reference & ref)117 cell worksheet::operator[](const cell_reference &ref)
118 {
119     return cell(ref);
120 }
121 
merged_ranges() const122 std::vector<range_reference> worksheet::merged_ranges() const
123 {
124     return d_->merged_cells_;
125 }
126 
has_page_margins() const127 bool worksheet::has_page_margins() const
128 {
129     return d_->page_margins_.is_set();
130 }
131 
has_page_setup() const132 bool worksheet::has_page_setup() const
133 {
134     return d_->page_setup_.is_set();
135 }
136 
page_margins() const137 page_margins worksheet::page_margins() const
138 {
139     return d_->page_margins_.get();
140 }
141 
page_margins(const class page_margins & margins)142 void worksheet::page_margins(const class page_margins &margins)
143 {
144     d_->page_margins_ = margins;
145 }
146 
auto_filter(const std::string & reference_string)147 void worksheet::auto_filter(const std::string &reference_string)
148 {
149     auto_filter(range_reference(reference_string));
150 }
151 
auto_filter(const range_reference & reference)152 void worksheet::auto_filter(const range_reference &reference)
153 {
154     d_->auto_filter_ = reference;
155 }
156 
auto_filter(const xlnt::range & range)157 void worksheet::auto_filter(const xlnt::range &range)
158 {
159     auto_filter(range.reference());
160 }
161 
auto_filter() const162 range_reference worksheet::auto_filter() const
163 {
164     return d_->auto_filter_.get();
165 }
166 
has_auto_filter() const167 bool worksheet::has_auto_filter() const
168 {
169     return d_->auto_filter_.is_set();
170 }
171 
clear_auto_filter()172 void worksheet::clear_auto_filter()
173 {
174     d_->auto_filter_.clear();
175 }
176 
page_setup(const struct page_setup & setup)177 void worksheet::page_setup(const struct page_setup &setup)
178 {
179     d_->page_setup_ = setup;
180 }
181 
page_setup() const182 page_setup worksheet::page_setup() const
183 {
184     if (!has_page_setup())
185     {
186         throw invalid_attribute();
187     }
188 
189     return d_->page_setup_.get();
190 }
191 
workbook()192 workbook &worksheet::workbook()
193 {
194     return *d_->parent_;
195 }
196 
workbook() const197 const workbook &worksheet::workbook() const
198 {
199     return *d_->parent_;
200 }
201 
garbage_collect()202 void worksheet::garbage_collect()
203 {
204     auto cell_iter = d_->cell_map_.begin();
205 
206     while (cell_iter != d_->cell_map_.end())
207     {
208         if (xlnt::cell(&cell_iter->second).garbage_collectible())
209         {
210             cell_iter = d_->cell_map_.erase(cell_iter);
211         }
212         else
213         {
214             ++cell_iter;
215         }
216     }
217 }
218 
id(std::size_t id)219 void worksheet::id(std::size_t id)
220 {
221     d_->id_ = id;
222 }
223 
id() const224 std::size_t worksheet::id() const
225 {
226     return d_->id_;
227 }
228 
title() const229 std::string worksheet::title() const
230 {
231     return d_->title_;
232 }
233 
title(const std::string & title)234 void worksheet::title(const std::string &title)
235 {
236     // do no work if we don't need to
237     if (d_->title_ == title)
238     {
239         return;
240     }
241     // excel limits worksheet titles to 31 characters
242     if (title.empty() || detail::string_length(title) > 31)
243     {
244         throw invalid_sheet_title(title);
245     }
246     // invalid characters in a worksheet name
247     if (title.find_first_of("*:/\\?[]") != std::string::npos)
248     {
249         throw invalid_sheet_title(title);
250     }
251     // try and insert the new name into the worksheets map
252     // if the insert fails, we have a duplicate sheet name
253     auto insert_result = workbook().d_->sheet_title_rel_id_map_.insert(
254         std::make_pair(title, workbook().d_->sheet_title_rel_id_map_[d_->title_]));
255     if (!insert_result.second) // insert failed, duplication detected
256     {
257         throw invalid_sheet_title(title);
258     }
259     // if the insert succeeded (i.e. wasn't a duplicate sheet name)
260     // update the worksheet title and remove the old relation
261     workbook().d_->sheet_title_rel_id_map_.erase(d_->title_);
262     d_->title_ = title;
263 
264     workbook().update_sheet_properties();
265 }
266 
frozen_panes() const267 cell_reference worksheet::frozen_panes() const
268 {
269     if (!has_frozen_panes())
270     {
271         throw xlnt::invalid_attribute();
272     }
273 
274     return d_->views_.front().pane().top_left_cell.get();
275 }
276 
freeze_panes(xlnt::cell top_left_cell)277 void worksheet::freeze_panes(xlnt::cell top_left_cell)
278 {
279     freeze_panes(top_left_cell.reference());
280 }
281 
freeze_panes(const cell_reference & ref)282 void worksheet::freeze_panes(const cell_reference &ref)
283 {
284     if (ref == "A1")
285     {
286         unfreeze_panes();
287         return;
288     }
289     if (!has_view())
290     {
291         d_->views_.push_back(sheet_view());
292     }
293 
294     auto &primary_view = d_->views_.front();
295     if (!primary_view.has_pane())
296     {
297         primary_view.pane(pane());
298     }
299 
300     primary_view.pane().top_left_cell = ref;
301     primary_view.pane().state = pane_state::frozen;
302 
303     primary_view.clear_selections();
304     if (ref.column() == "A") // no column is frozen
305     {
306         primary_view.add_selection(selection(pane_corner::bottom_left, ref));
307         primary_view.pane().active_pane = pane_corner::bottom_left;
308         primary_view.pane().y_split = ref.row() - 1;
309     }
310     else if (ref.row() == 1) // no row is frozen
311     {
312         primary_view.add_selection(selection(pane_corner::top_right, ref));
313         primary_view.pane().active_pane = pane_corner::top_right;
314         primary_view.pane().x_split = ref.column_index() - 1;
315     }
316     else // column and row is frozen
317     {
318         primary_view.add_selection(selection(pane_corner::top_right, cell_reference(ref.column(), 1)));
319         primary_view.add_selection(selection(pane_corner::bottom_left, cell_reference(1, ref.row())));
320         primary_view.add_selection(selection(pane_corner::bottom_right, ref));
321         primary_view.pane().active_pane = pane_corner::bottom_right;
322         primary_view.pane().x_split = ref.column_index() - 1;
323         primary_view.pane().y_split = ref.row() - 1;
324     }
325 }
326 
unfreeze_panes()327 void worksheet::unfreeze_panes()
328 {
329     if (!has_view()) return;
330 
331     auto &primary_view = d_->views_.front();
332 
333     primary_view.clear_selections();
334     primary_view.clear_pane();
335 }
336 
active_cell(const cell_reference & ref)337 void worksheet::active_cell(const cell_reference &ref)
338 {
339     if (!has_view())
340     {
341         d_->views_.push_back(sheet_view());
342     }
343 
344     auto &primary_view = d_->views_.front();
345 
346     if (!primary_view.has_selections())
347     {
348         primary_view.add_selection(selection(pane_corner::bottom_right, ref));
349     }
350     else
351     {
352         primary_view.selection(0).active_cell(ref);
353     }
354 }
355 
has_active_cell() const356 bool worksheet::has_active_cell() const
357 {
358     if (!has_view()) return false;
359     auto &primary_view = d_->views_.front();
360     if (!primary_view.has_selections()) return false;
361     auto primary_selection = primary_view.selection(0);
362 
363     return primary_selection.has_active_cell();
364 }
365 
active_cell() const366 cell_reference worksheet::active_cell() const
367 {
368     if (!has_view())
369     {
370         throw xlnt::exception("Worksheet has no view.");
371     }
372 
373     auto &primary_view = d_->views_.front();
374 
375     if (!primary_view.has_selections())
376     {
377         throw xlnt::exception("Default worksheet view has no selections.");
378     }
379 
380     return primary_view.selection(0).active_cell();
381 }
382 
cell(const cell_reference & reference)383 cell worksheet::cell(const cell_reference &reference)
384 {
385     auto match = d_->cell_map_.find(reference);
386     if (match == d_->cell_map_.end())
387     {
388         auto impl = detail::cell_impl();
389         impl.parent_ = d_;
390         impl.column_ = reference.column_index();
391         impl.row_ = reference.row();
392 
393         match = d_->cell_map_.emplace(reference, impl).first;
394     }
395     return xlnt::cell(&match->second);
396 }
397 
cell(const cell_reference & reference) const398 const cell worksheet::cell(const cell_reference &reference) const
399 {
400     return xlnt::cell(&d_->cell_map_.at(reference));
401 }
402 
cell(xlnt::column_t column,row_t row)403 cell worksheet::cell(xlnt::column_t column, row_t row)
404 {
405     return cell(cell_reference(column, row));
406 }
407 
cell(xlnt::column_t column,row_t row) const408 const cell worksheet::cell(xlnt::column_t column, row_t row) const
409 {
410     return cell(cell_reference(column, row));
411 }
412 
has_cell(const cell_reference & reference) const413 bool worksheet::has_cell(const cell_reference &reference) const
414 {
415     const auto cell = d_->cell_map_.find(reference);
416     return cell != d_->cell_map_.cend();
417 }
418 
has_row_properties(row_t row) const419 bool worksheet::has_row_properties(row_t row) const
420 {
421     return d_->row_properties_.find(row) != d_->row_properties_.end();
422 }
423 
named_range(const std::string & name)424 range worksheet::named_range(const std::string &name)
425 {
426     if (!workbook().has_named_range(name))
427     {
428         throw key_not_found();
429     }
430 
431     if (!has_named_range(name))
432     {
433         throw key_not_found();
434     }
435 
436     return range(d_->named_ranges_[name].targets()[0].second);
437 }
438 
named_range(const std::string & name) const439 const range worksheet::named_range(const std::string &name) const
440 {
441     if (!workbook().has_named_range(name))
442     {
443         throw key_not_found();
444     }
445 
446     if (!has_named_range(name))
447     {
448         throw key_not_found();
449     }
450 
451     return range(d_->named_ranges_[name].targets()[0].second);
452 }
453 
lowest_column() const454 column_t worksheet::lowest_column() const
455 {
456     if (d_->cell_map_.empty())
457     {
458         return constants::min_column();
459     }
460 
461     auto lowest = constants::max_column();
462 
463     for (auto &cell : d_->cell_map_)
464     {
465         lowest = std::min(lowest, cell.first.column());
466     }
467 
468     return lowest;
469 }
470 
lowest_column_or_props() const471 column_t worksheet::lowest_column_or_props() const
472 {
473     auto lowest = lowest_column();
474 
475     if (d_->cell_map_.empty() && !d_->column_properties_.empty())
476     {
477         lowest = d_->column_properties_.begin()->first;
478     }
479 
480     for (auto &props : d_->column_properties_)
481     {
482         lowest = std::min(lowest, props.first);
483     }
484 
485     return lowest;
486 }
487 
lowest_row() const488 row_t worksheet::lowest_row() const
489 {
490     if (d_->cell_map_.empty())
491     {
492         return constants::min_row();
493     }
494 
495     auto lowest = constants::max_row();
496 
497     for (auto &cell : d_->cell_map_)
498     {
499         lowest = std::min(lowest, cell.first.row());
500     }
501 
502     return lowest;
503 }
504 
lowest_row_or_props() const505 row_t worksheet::lowest_row_or_props() const
506 {
507     auto lowest = lowest_row();
508 
509     if (d_->cell_map_.empty() && !d_->row_properties_.empty())
510     {
511         lowest = d_->row_properties_.begin()->first;
512     }
513 
514     for (auto &props : d_->row_properties_)
515     {
516         lowest = std::min(lowest, props.first);
517     }
518 
519     return lowest;
520 }
521 
highest_row() const522 row_t worksheet::highest_row() const
523 {
524     auto highest = constants::min_row();
525 
526     for (auto &cell : d_->cell_map_)
527     {
528         highest = std::max(highest, cell.first.row());
529     }
530 
531     return highest;
532 }
533 
highest_row_or_props() const534 row_t worksheet::highest_row_or_props() const
535 {
536     auto highest = highest_row();
537 
538     if (d_->cell_map_.empty() && !d_->row_properties_.empty())
539     {
540         highest = d_->row_properties_.begin()->first;
541     }
542 
543     for (auto &props : d_->row_properties_)
544     {
545         highest = std::max(highest, props.first);
546     }
547 
548     return highest;
549 }
550 
highest_column() const551 column_t worksheet::highest_column() const
552 {
553     auto highest = constants::min_column();
554 
555     for (auto &cell : d_->cell_map_)
556     {
557         highest = std::max(highest, cell.first.column());
558     }
559 
560     return highest;
561 }
562 
highest_column_or_props() const563 column_t worksheet::highest_column_or_props() const
564 {
565     auto highest = highest_column();
566 
567     if (d_->cell_map_.empty() && !d_->column_properties_.empty())
568     {
569         highest = d_->column_properties_.begin()->first;
570     }
571 
572     for (auto &props : d_->column_properties_)
573     {
574         highest = std::max(highest, props.first);
575     }
576 
577     return highest;
578 }
579 
calculate_dimension() const580 range_reference worksheet::calculate_dimension() const
581 {
582     // partially optimised version of:
583     // return range_reference(lowest_column(), lowest_row_or_props(),
584     //                        highest_column(), highest_row_or_props());
585     //
586     if (d_->cell_map_.empty() && d_->row_properties_.empty())
587     {
588         return range_reference(constants::min_column(), constants::min_row(),
589             constants::min_column(), constants::min_row());
590     }
591     row_t min_row_prop = constants::max_row();
592     row_t max_row_prop = constants::min_row();
593     for (const auto &row_prop : d_->row_properties_)
594     {
595         min_row_prop = std::min(min_row_prop, row_prop.first);
596         max_row_prop = std::max(max_row_prop, row_prop.first);
597     }
598     if (d_->cell_map_.empty())
599     {
600         return range_reference(constants::min_column(), min_row_prop,
601             constants::min_column(), max_row_prop);
602     }
603     // find min and max row/column in cell map
604     column_t min_col = constants::max_column();
605     column_t max_col = constants::min_column();
606     row_t min_row = min_row_prop;
607     row_t max_row = max_row_prop;
608     for (auto &c : d_->cell_map_)
609     {
610         min_col = std::min(min_col, c.second.column_);
611         max_col = std::max(max_col, c.second.column_);
612         min_row = std::min(min_row, c.second.row_);
613         max_row = std::max(max_row, c.second.row_);
614     }
615     return range_reference(min_col, min_row, max_col, max_row);
616 }
617 
range(const std::string & reference_string)618 range worksheet::range(const std::string &reference_string)
619 {
620     if (has_named_range(reference_string))
621     {
622         return named_range(reference_string);
623     }
624 
625     return range(range_reference(reference_string));
626 }
627 
range(const std::string & reference_string) const628 const range worksheet::range(const std::string &reference_string) const
629 {
630     if (has_named_range(reference_string))
631     {
632         return named_range(reference_string);
633     }
634 
635     return range(range_reference(reference_string));
636 }
637 
range(const range_reference & reference)638 range worksheet::range(const range_reference &reference)
639 {
640     return xlnt::range(*this, reference);
641 }
642 
range(const range_reference & reference) const643 const range worksheet::range(const range_reference &reference) const
644 {
645     return xlnt::range(*this, reference);
646 }
647 
merge_cells(const std::string & reference_string)648 void worksheet::merge_cells(const std::string &reference_string)
649 {
650     merge_cells(range_reference(reference_string));
651 }
652 
unmerge_cells(const std::string & reference_string)653 void worksheet::unmerge_cells(const std::string &reference_string)
654 {
655     unmerge_cells(range_reference(reference_string));
656 }
657 
merge_cells(const range_reference & reference)658 void worksheet::merge_cells(const range_reference &reference)
659 {
660     d_->merged_cells_.push_back(reference);
661     bool first = true;
662 
663     for (auto row : range(reference))
664     {
665         for (auto cell : row)
666         {
667             cell.merged(true);
668 
669             if (!first)
670             {
671                 if (cell.data_type() == cell::type::shared_string)
672                 {
673                     cell.value("");
674                 }
675                 else
676                 {
677                     cell.clear_value();
678                 }
679             }
680 
681             first = false;
682         }
683     }
684 }
685 
unmerge_cells(const range_reference & reference)686 void worksheet::unmerge_cells(const range_reference &reference)
687 {
688     auto match = std::find(d_->merged_cells_.begin(), d_->merged_cells_.end(), reference);
689 
690     if (match == d_->merged_cells_.end())
691     {
692         throw invalid_parameter();
693     }
694 
695     d_->merged_cells_.erase(match);
696 
697     for (auto row : range(reference))
698     {
699         for (auto cell : row)
700         {
701             cell.merged(false);
702         }
703     }
704 }
705 
next_row() const706 row_t worksheet::next_row() const
707 {
708     auto row = highest_row() + 1;
709 
710     if (row == 2 && d_->cell_map_.size() == 0)
711     {
712         row = 1;
713     }
714 
715     return row;
716 }
717 
rows(bool skip_null)718 xlnt::range worksheet::rows(bool skip_null)
719 {
720     return xlnt::range(*this, calculate_dimension(), major_order::row, skip_null);
721 }
722 
rows(bool skip_null) const723 const xlnt::range worksheet::rows(bool skip_null) const
724 {
725     return xlnt::range(*this, calculate_dimension(), major_order::row, skip_null);
726 }
727 
columns(bool skip_null)728 xlnt::range worksheet::columns(bool skip_null)
729 {
730     return xlnt::range(*this, calculate_dimension(), major_order::column, skip_null);
731 }
732 
columns(bool skip_null) const733 const xlnt::range worksheet::columns(bool skip_null) const
734 {
735     return xlnt::range(*this, calculate_dimension(), major_order::column, skip_null);
736 }
737 
738 /*
739 //TODO: finish implementing cell_iterator wrapping before uncommenting
740 
741 cell_vector worksheet::cells(bool skip_null)
742 {
743     const auto dimension = calculate_dimension();
744     return cell_vector(*this, dimension.top_left(), dimension, major_order::row, skip_null, true);
745 }
746 
747 const cell_vector worksheet::cells(bool skip_null) const
748 {
749     const auto dimension = calculate_dimension();
750     return cell_vector(*this, dimension.top_left(), dimension, major_order::row, skip_null, true);
751 }
752 */
753 
clear_cell(const cell_reference & ref)754 void worksheet::clear_cell(const cell_reference &ref)
755 {
756     d_->cell_map_.erase(ref);
757     // TODO: garbage collect newly unreferenced resources such as styles?
758 }
759 
clear_row(row_t row)760 void worksheet::clear_row(row_t row)
761 {
762     for (auto it = d_->cell_map_.begin(); it != d_->cell_map_.end();)
763     {
764         if (it->first.row() == row)
765         {
766             it = d_->cell_map_.erase(it);
767         }
768         else
769         {
770             ++it;
771         }
772     }
773     d_->row_properties_.erase(row);
774     // TODO: garbage collect newly unreferenced resources such as styles?
775 }
776 
insert_rows(row_t row,std::uint32_t amount)777 void worksheet::insert_rows(row_t row, std::uint32_t amount)
778 {
779     move_cells(row, amount, row_or_col_t::row);
780 }
781 
insert_columns(column_t column,std::uint32_t amount)782 void worksheet::insert_columns(column_t column, std::uint32_t amount)
783 {
784     move_cells(column.index, amount, row_or_col_t::column);
785 }
786 
delete_rows(row_t row,std::uint32_t amount)787 void worksheet::delete_rows(row_t row, std::uint32_t amount)
788 {
789     move_cells(row + amount, amount, row_or_col_t::row, true);
790 }
791 
delete_columns(column_t column,std::uint32_t amount)792 void worksheet::delete_columns(column_t column, std::uint32_t amount)
793 {
794     move_cells(column.index + amount, amount, row_or_col_t::column, true);
795 }
796 
move_cells(std::uint32_t min_index,std::uint32_t amount,row_or_col_t row_or_col,bool reverse)797 void worksheet::move_cells(std::uint32_t min_index, std::uint32_t amount, row_or_col_t row_or_col, bool reverse)
798 {
799     if (reverse && amount > min_index)
800     {
801         throw xlnt::invalid_parameter();
802     }
803 
804     if ((!reverse && row_or_col == row_or_col_t::row && min_index > constants::max_row() - amount) || (!reverse && row_or_col == row_or_col_t::column && min_index > constants::max_column() - amount))
805     {
806         throw xlnt::exception("Cannot move cells as they would be outside the maximum bounds of the spreadsheet");
807     }
808 
809     std::vector<detail::cell_impl> cells_to_move;
810 
811     auto cell_iter = d_->cell_map_.cbegin();
812     while (cell_iter != d_->cell_map_.cend())
813     {
814         std::uint32_t current_index;
815         switch (row_or_col)
816         {
817         case row_or_col_t::row:
818             current_index = cell_iter->first.row();
819             break;
820         case row_or_col_t::column:
821             current_index = cell_iter->first.column().index;
822             break;
823         default:
824             throw xlnt::unhandled_switch_case();
825         }
826 
827         if (current_index >= min_index) // extract cells to be moved
828         {
829             auto cell = cell_iter->second;
830             if (row_or_col == row_or_col_t::row)
831             {
832                 cell.row_ = reverse ? cell.row_ - amount : cell.row_ + amount;
833             }
834             else if (row_or_col == row_or_col_t::column)
835             {
836                 cell.column_ = reverse ? cell.column_.index - amount : cell.column_.index + amount;
837             }
838 
839             cells_to_move.push_back(cell);
840             cell_iter = d_->cell_map_.erase(cell_iter);
841         }
842         else if (reverse && current_index >= min_index - amount) // delete destination cells
843         {
844             cell_iter = d_->cell_map_.erase(cell_iter);
845         }
846         else // skip other cells
847         {
848             ++cell_iter;
849         }
850     }
851 
852     for (auto &cell : cells_to_move)
853     {
854         d_->cell_map_[cell_reference(cell.column_, cell.row_)] = cell;
855     }
856 
857     if (row_or_col == row_or_col_t::row)
858     {
859         std::vector<std::pair<row_t, xlnt::row_properties>> properties_to_move;
860 
861         auto row_prop_iter = d_->row_properties_.cbegin();
862         while (row_prop_iter != d_->row_properties_.cend())
863         {
864             auto current_row = row_prop_iter->first;
865             if (current_row >= min_index) // extract properties that need to be moved
866             {
867                 auto tmp_row = reverse ? current_row - amount : current_row + amount;
868                 properties_to_move.push_back({tmp_row, row_prop_iter->second});
869                 row_prop_iter = d_->row_properties_.erase(row_prop_iter);
870             }
871             else if (reverse && current_row >= min_index - amount) // clear properties of destination when in reverse
872             {
873                 row_prop_iter = d_->row_properties_.erase(row_prop_iter);
874             }
875             else // skip the rest
876             {
877                 ++row_prop_iter;
878             }
879         }
880 
881         for (const auto &prop : properties_to_move)
882         {
883             add_row_properties(prop.first, prop.second);
884         }
885     }
886     else if (row_or_col == row_or_col_t::column)
887     {
888         std::vector<std::pair<column_t, xlnt::column_properties>> properties_to_move;
889 
890         auto col_prop_iter = d_->column_properties_.cbegin();
891         while (col_prop_iter != d_->column_properties_.cend())
892         {
893             auto current_col = col_prop_iter->first.index;
894             if (current_col >= min_index) // extract properties that need to be moved
895             {
896                 auto tmp_column = column_t(reverse ? current_col - amount : current_col + amount);
897                 properties_to_move.push_back({tmp_column, col_prop_iter->second});
898                 col_prop_iter = d_->column_properties_.erase(col_prop_iter);
899             }
900             else if (reverse && current_col >= min_index - amount) // clear properties of destination when in reverse
901             {
902                 col_prop_iter = d_->column_properties_.erase(col_prop_iter);
903             }
904             else // skip the rest
905             {
906                 ++col_prop_iter;
907             }
908         }
909 
910         for (auto &prop : properties_to_move)
911         {
912             add_column_properties(prop.first, prop.second);
913         }
914     }
915 
916     // adjust merged cells
917     auto shift_reference = [min_index, amount, row_or_col, reverse](cell_reference &ref) {
918         auto index = row_or_col == row_or_col_t::row ? ref.row() : ref.column_index();
919         if (index >= min_index)
920         {
921             auto new_index = reverse ? index - amount : index + amount;
922             if (row_or_col == row_or_col_t::row)
923             {
924                 ref.row(new_index);
925             }
926             else if (row_or_col == row_or_col_t::column)
927             {
928                 ref.column_index(new_index);
929             }
930         }
931     };
932 
933     for (auto merged_cell = d_->merged_cells_.begin(); merged_cell != d_->merged_cells_.end(); ++merged_cell)
934     {
935         cell_reference new_top_left = merged_cell->top_left();
936         shift_reference(new_top_left);
937 
938         cell_reference new_bottom_right = merged_cell->bottom_right();
939         shift_reference(new_bottom_right);
940 
941         range_reference new_range{new_top_left, new_bottom_right};
942         if (*merged_cell != new_range)
943         {
944             *merged_cell = new_range;
945         }
946     }
947 }
948 
operator ==(const worksheet & other) const949 bool worksheet::operator==(const worksheet &other) const
950 {
951     return compare(other, true);
952 }
953 
compare(const worksheet & other,bool reference) const954 bool worksheet::compare(const worksheet &other, bool reference) const
955 {
956     if (reference)
957     {
958         return d_ == other.d_;
959     }
960 
961     if (d_->parent_ != other.d_->parent_) return false;
962 
963     for (auto &cell : d_->cell_map_)
964     {
965         if (other.d_->cell_map_.find(cell.first) == other.d_->cell_map_.end())
966         {
967             return false;
968         }
969 
970         xlnt::cell this_cell(&cell.second);
971         xlnt::cell other_cell(&other.d_->cell_map_[cell.first]);
972 
973         if (this_cell.data_type() != other_cell.data_type())
974         {
975             return false;
976         }
977 
978         if (this_cell.data_type() == xlnt::cell::type::number
979             && !detail::float_equals(this_cell.value<double>(), other_cell.value<double>()))
980         {
981             return false;
982         }
983     }
984 
985     // todo: missing some comparisons
986 
987     if (d_->auto_filter_ == other.d_->auto_filter_ && d_->views_ == other.d_->views_
988         && d_->merged_cells_ == other.d_->merged_cells_)
989     {
990         return true;
991     }
992 
993     return false;
994 }
995 
operator !=(const worksheet & other) const996 bool worksheet::operator!=(const worksheet &other) const
997 {
998     return !(*this == other);
999 }
1000 
operator ==(std::nullptr_t) const1001 bool worksheet::operator==(std::nullptr_t) const
1002 {
1003     return d_ == nullptr;
1004 }
1005 
operator !=(std::nullptr_t) const1006 bool worksheet::operator!=(std::nullptr_t) const
1007 {
1008     return d_ != nullptr;
1009 }
1010 
operator =(const worksheet & other)1011 void worksheet::operator=(const worksheet &other)
1012 {
1013     d_ = other.d_;
1014 }
1015 
operator [](const cell_reference & ref) const1016 const cell worksheet::operator[](const cell_reference &ref) const
1017 {
1018     return cell(ref);
1019 }
1020 
has_named_range(const std::string & name) const1021 bool worksheet::has_named_range(const std::string &name) const
1022 {
1023     return d_->named_ranges_.find(name) != d_->named_ranges_.end();
1024 }
1025 
remove_named_range(const std::string & name)1026 void worksheet::remove_named_range(const std::string &name)
1027 {
1028     if (!has_named_range(name))
1029     {
1030         throw key_not_found();
1031     }
1032 
1033     d_->named_ranges_.erase(name);
1034 }
1035 
reserve(std::size_t n)1036 void worksheet::reserve(std::size_t n)
1037 {
1038     d_->cell_map_.reserve(n);
1039 }
1040 
header_footer() const1041 class header_footer worksheet::header_footer() const
1042 {
1043     return d_->header_footer_.get();
1044 }
1045 
point_pos(int left,int top) const1046 cell_reference worksheet::point_pos(int left, int top) const
1047 {
1048     column_t current_column = 1;
1049     row_t current_row = 1;
1050 
1051     double left_pos = 0;
1052     double top_pos = 0;
1053 
1054     while (left_pos <= left)
1055     {
1056         left_pos += column_width(current_column++);
1057     }
1058 
1059     while (top_pos <= top)
1060     {
1061         top_pos += row_height(current_row++);
1062     }
1063 
1064     return {current_column - 1, current_row - 1};
1065 }
1066 
sheet_state(xlnt::sheet_state state)1067 void worksheet::sheet_state(xlnt::sheet_state state)
1068 {
1069     page_setup().sheet_state(state);
1070 }
1071 
sheet_state() const1072 sheet_state worksheet::sheet_state() const
1073 {
1074     return page_setup().sheet_state();
1075 }
1076 
add_column_properties(column_t column,const xlnt::column_properties & props)1077 void worksheet::add_column_properties(column_t column, const xlnt::column_properties &props)
1078 {
1079     d_->column_properties_[column] = props;
1080 }
1081 
has_column_properties(column_t column) const1082 bool worksheet::has_column_properties(column_t column) const
1083 {
1084     return d_->column_properties_.find(column) != d_->column_properties_.end();
1085 }
1086 
column_properties(column_t column)1087 column_properties &worksheet::column_properties(column_t column)
1088 {
1089     return d_->column_properties_[column];
1090 }
1091 
column_properties(column_t column) const1092 const column_properties &worksheet::column_properties(column_t column) const
1093 {
1094     return d_->column_properties_.at(column);
1095 }
1096 
row_properties(row_t row)1097 row_properties &worksheet::row_properties(row_t row)
1098 {
1099     return d_->row_properties_[row];
1100 }
1101 
row_properties(row_t row) const1102 const row_properties &worksheet::row_properties(row_t row) const
1103 {
1104     return d_->row_properties_.at(row);
1105 }
1106 
add_row_properties(row_t row,const xlnt::row_properties & props)1107 void worksheet::add_row_properties(row_t row, const xlnt::row_properties &props)
1108 {
1109     d_->row_properties_[row] = props;
1110 }
1111 
begin()1112 worksheet::iterator worksheet::begin()
1113 {
1114     return rows().begin();
1115 }
1116 
end()1117 worksheet::iterator worksheet::end()
1118 {
1119     return rows().end();
1120 }
1121 
cbegin() const1122 worksheet::const_iterator worksheet::cbegin() const
1123 {
1124     return rows().cbegin();
1125 }
1126 
cend() const1127 worksheet::const_iterator worksheet::cend() const
1128 {
1129     return rows().cend();
1130 }
1131 
begin() const1132 worksheet::const_iterator worksheet::begin() const
1133 {
1134     return cbegin();
1135 }
1136 
end() const1137 worksheet::const_iterator worksheet::end() const
1138 {
1139     return cend();
1140 }
1141 
print_title_rows(row_t last_row)1142 void worksheet::print_title_rows(row_t last_row)
1143 {
1144     print_title_rows(1, last_row);
1145 }
1146 
print_title_rows(row_t first_row,row_t last_row)1147 void worksheet::print_title_rows(row_t first_row, row_t last_row)
1148 {
1149     d_->print_title_rows_ = std::to_string(first_row) + ":" + std::to_string(last_row);
1150 }
1151 
print_title_cols(column_t last_column)1152 void worksheet::print_title_cols(column_t last_column)
1153 {
1154     print_title_cols(1, last_column);
1155 }
1156 
print_title_cols(column_t first_column,column_t last_column)1157 void worksheet::print_title_cols(column_t first_column, column_t last_column)
1158 {
1159     d_->print_title_cols_ = first_column.column_string() + ":" + last_column.column_string();
1160 }
1161 
print_titles() const1162 std::string worksheet::print_titles() const
1163 {
1164     if (!d_->print_title_rows_.empty() && !d_->print_title_cols_.empty())
1165     {
1166         return d_->title_ + "!" + d_->print_title_rows_ + "," + d_->title_ + "!" + d_->print_title_cols_;
1167     }
1168     else if (!d_->print_title_cols_.empty())
1169     {
1170         return d_->title_ + "!" + d_->print_title_cols_;
1171     }
1172     else
1173     {
1174         return d_->title_ + "!" + d_->print_title_rows_;
1175     }
1176 }
1177 
print_area(const std::string & print_area)1178 void worksheet::print_area(const std::string &print_area)
1179 {
1180     d_->print_area_ = range_reference::make_absolute(range_reference(print_area));
1181 }
1182 
print_area() const1183 range_reference worksheet::print_area() const
1184 {
1185     return d_->print_area_.get();
1186 }
1187 
has_view() const1188 bool worksheet::has_view() const
1189 {
1190     return !d_->views_.empty();
1191 }
1192 
view(std::size_t index) const1193 sheet_view &worksheet::view(std::size_t index) const
1194 {
1195     return d_->views_.at(index);
1196 }
1197 
add_view(const sheet_view & new_view)1198 void worksheet::add_view(const sheet_view &new_view)
1199 {
1200     d_->views_.push_back(new_view);
1201 }
1202 
register_comments_in_manifest()1203 void worksheet::register_comments_in_manifest()
1204 {
1205     workbook().register_worksheet_part(*this, relationship_type::comments);
1206 }
1207 
register_calc_chain_in_manifest()1208 void worksheet::register_calc_chain_in_manifest()
1209 {
1210     workbook().register_workbook_part(relationship_type::calculation_chain);
1211 }
1212 
has_phonetic_properties() const1213 bool worksheet::has_phonetic_properties() const
1214 {
1215     return d_->phonetic_properties_.is_set();
1216 }
1217 
phonetic_properties() const1218 const phonetic_pr &worksheet::phonetic_properties() const
1219 {
1220     return d_->phonetic_properties_.get();
1221 }
1222 
phonetic_properties(const phonetic_pr & phonetic_props)1223 void worksheet::phonetic_properties(const phonetic_pr &phonetic_props)
1224 {
1225     d_->phonetic_properties_.set(phonetic_props);
1226 }
1227 
has_header_footer() const1228 bool worksheet::has_header_footer() const
1229 {
1230     return d_->header_footer_.is_set();
1231 }
1232 
header_footer(const class header_footer & hf)1233 void worksheet::header_footer(const class header_footer &hf)
1234 {
1235     d_->header_footer_ = hf;
1236 }
1237 
clear_page_breaks()1238 void worksheet::clear_page_breaks()
1239 {
1240     d_->row_breaks_.clear();
1241     d_->column_breaks_.clear();
1242 }
1243 
page_break_at_row(row_t row)1244 void worksheet::page_break_at_row(row_t row)
1245 {
1246     d_->row_breaks_.push_back(row);
1247 }
1248 
page_break_rows() const1249 const std::vector<row_t> &worksheet::page_break_rows() const
1250 {
1251     return d_->row_breaks_;
1252 }
1253 
page_break_at_column(xlnt::column_t column)1254 void worksheet::page_break_at_column(xlnt::column_t column)
1255 {
1256     d_->column_breaks_.push_back(column);
1257 }
1258 
page_break_columns() const1259 const std::vector<column_t> &worksheet::page_break_columns() const
1260 {
1261     return d_->column_breaks_;
1262 }
1263 
column_width(column_t column) const1264 double worksheet::column_width(column_t column) const
1265 {
1266     static const auto DefaultColumnWidth = 51.85;
1267 
1268     if (has_column_properties(column))
1269     {
1270         return column_properties(column).width.get();
1271     }
1272     else
1273     {
1274         return points_to_pixels(DefaultColumnWidth, 96.0);
1275     }
1276 }
1277 
row_height(row_t row) const1278 double worksheet::row_height(row_t row) const
1279 {
1280     static const auto DefaultRowHeight = 15.0;
1281 
1282     if (has_row_properties(row) && row_properties(row).height.is_set())
1283     {
1284         return row_properties(row).height.get();
1285     }
1286     else
1287     {
1288         return points_to_pixels(DefaultRowHeight, 96.0);
1289     }
1290 }
1291 
garbage_collect_formulae()1292 void worksheet::garbage_collect_formulae()
1293 {
1294     workbook().garbage_collect_formulae();
1295 }
1296 
parent(xlnt::workbook & wb)1297 void worksheet::parent(xlnt::workbook &wb)
1298 {
1299     d_->parent_ = &wb;
1300 }
1301 
conditional_format(const range_reference & ref,const condition & when)1302 conditional_format worksheet::conditional_format(const range_reference &ref, const condition &when)
1303 {
1304     return workbook().d_->stylesheet_.get().add_conditional_format_rule(d_, ref, when);
1305 }
1306 
path() const1307 path worksheet::path() const
1308 {
1309     auto rel = referring_relationship();
1310     return xlnt::path(rel.source().path().parent().append(rel.target().path()));
1311 }
1312 
referring_relationship() const1313 relationship worksheet::referring_relationship() const
1314 {
1315     auto &manifest = workbook().manifest();
1316     auto wb_rel = manifest.relationship(xlnt::path("/"),
1317         relationship_type::office_document);
1318     auto ws_rel = manifest.relationship(wb_rel.target().path(),
1319         workbook().d_->sheet_title_rel_id_map_.at(title()));
1320     return ws_rel;
1321 }
1322 
format_properties() const1323 sheet_format_properties worksheet::format_properties() const
1324 {
1325     return d_->format_properties_;
1326 }
1327 
format_properties(const sheet_format_properties & properties)1328 void worksheet::format_properties(const sheet_format_properties &properties)
1329 {
1330     d_->format_properties_ = properties;
1331 }
1332 
has_drawing() const1333 bool worksheet::has_drawing() const
1334 {
1335     return d_->drawing_.is_set();
1336 }
1337 
is_empty() const1338 bool worksheet::is_empty() const
1339 {
1340     return d_->cell_map_.empty();
1341 }
1342 
1343 } // namespace xlnt
1344