• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

xlnt-community / xlnt / aa719a12-330c-47b2-9460-5b2d61336ff6

03 Feb 2025 05:39PM UTC coverage: 81.978% (-1.5%) from 83.482%
aa719a12-330c-47b2-9460-5b2d61336ff6

push

circleci

web-flow
Use C++23 for generating coverage report. (#57)

Using C++23 for coverage reporting, prepares the library for coverage
testing of future post-C++11 features.

This PR makes as few changes as possible to ensure all coverage changes
are due to the changed C++/compiler version used for generating the
coverage report.

Temporarily disable warnings as errors for coverage reporting until
better C++20/23 support is added in PR #55.

11395 of 13900 relevant lines covered (81.98%)

1202684.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.35
./source/workbook/workbook.cpp
1
// Copyright (c) 2014-2022 Thomas Fussell
2
// Copyright (c) 2010-2015 openpyxl
3
// Copyright (c) 2024-2025 xlnt-community
4
//
5
// Permission is hereby granted, free of charge, to any person obtaining a copy
6
// of this software and associated documentation files (the "Software"), to deal
7
// in the Software without restriction, including without limitation the rights
8
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
// copies of the Software, and to permit persons to whom the Software is
10
// furnished to do so, subject to the following conditions:
11
//
12
// The above copyright notice and this permission notice shall be included in
13
// all copies or substantial portions of the Software.
14
//
15
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
// THE SOFTWARE
22
//
23
// @license: http://www.opensource.org/licenses/mit-license.php
24
// @author: see AUTHORS file
25

26
#include <algorithm>
27
#include <array>
28
#include <fstream>
29
#include <functional>
30
#include <set>
31

32
#include <xlnt/cell/cell.hpp>
33
#include <xlnt/packaging/manifest.hpp>
34
#include <xlnt/packaging/relationship.hpp>
35
#include <xlnt/styles/alignment.hpp>
36
#include <xlnt/styles/border.hpp>
37
#include <xlnt/styles/fill.hpp>
38
#include <xlnt/styles/font.hpp>
39
#include <xlnt/styles/format.hpp>
40
#include <xlnt/styles/number_format.hpp>
41
#include <xlnt/styles/protection.hpp>
42
#include <xlnt/styles/style.hpp>
43
#include <xlnt/utils/exceptions.hpp>
44
#include <xlnt/utils/path.hpp>
45
#include <xlnt/utils/variant.hpp>
46
#include <xlnt/workbook/metadata_property.hpp>
47
#include <xlnt/workbook/named_range.hpp>
48
#include <xlnt/workbook/theme.hpp>
49
#include <xlnt/workbook/workbook.hpp>
50
#include <xlnt/workbook/workbook_view.hpp>
51
#include <xlnt/workbook/worksheet_iterator.hpp>
52
#include <xlnt/worksheet/header_footer.hpp>
53
#include <xlnt/worksheet/range.hpp>
54
#include <xlnt/worksheet/worksheet.hpp>
55
#include <detail/constants.hpp>
56
#include <detail/default_case.hpp>
57
#include <detail/implementations/cell_impl.hpp>
58
#include <detail/implementations/workbook_impl.hpp>
59
#include <detail/implementations/worksheet_impl.hpp>
60
#include <detail/serialization/excel_thumbnail.hpp>
61
#include <detail/serialization/open_stream.hpp>
62
#include <detail/serialization/vector_streambuf.hpp>
63
#include <detail/serialization/xlsx_consumer.hpp>
64
#include <detail/serialization/xlsx_producer.hpp>
65

66
namespace {
67

68
using xlnt::detail::open_stream;
69

70
template <typename T>
71
std::vector<T> keys(const std::vector<std::pair<T, xlnt::variant>> &container)
171✔
72
{
73
    auto result = std::vector<T>();
171✔
74
    auto iter = container.begin();
171✔
75

76
    while (iter != container.end())
1,482✔
77
    {
78
        result.push_back((iter++)->first);
1,311✔
79
    }
80

81
    return result;
171✔
82
}
×
83

84
template <typename T>
85
bool contains(const std::vector<std::pair<T, xlnt::variant>> &container, const T key)
765✔
86
{
87
    for (const auto &iter : container)
3,490✔
88
    {
89
        if (iter.first == key)
3,486✔
90
        {
91
            return true;
761✔
92
        }
93
    }
94

95
    return false;
4✔
96
}
97

98
xlnt::path default_path(xlnt::relationship_type type, std::size_t index = 0)
2,740✔
99
{
100
    using xlnt::path;
101
    using xlnt::relationship_type;
102

103
    switch (type)
2,740✔
104
    {
105
    case relationship_type::calculation_chain:
12✔
106
        return path("/xl/calcChain.xml");
24✔
107
    case relationship_type::chartsheet:
×
108
        return path("/xl/sheets/.xml");
×
109
    case relationship_type::comments:
×
110
        return path("/xl/comments.xml");
×
111
    case relationship_type::connections:
×
112
        return path("/xl/connections.xml");
×
113
    case relationship_type::core_properties:
526✔
114
        return path("/docProps/core.xml");
1,052✔
115
    case relationship_type::custom_properties:
2✔
116
        return path("/docProps/custom.xml");
4✔
117
    case relationship_type::custom_property:
×
118
        return path("/xl/customProperty.xml");
×
119
    case relationship_type::custom_xml_mappings:
×
120
        return path("/xl/customXmlMappings.xml");
×
121
    case relationship_type::dialogsheet:
×
122
        return path("/xl/dialogsheets/sheet.xml");
×
123
    case relationship_type::drawings:
×
124
        return path("/xl/drawings/drawing.xml");
×
125
    case relationship_type::extended_properties:
526✔
126
        return path("/docProps/app.xml");
1,052✔
127
    case relationship_type::external_workbook_references:
×
128
        return path("/xl/external.xml");
×
129
    case relationship_type::hyperlink:
×
130
        return path("/xl/hyperlink.xml");
×
131
    case relationship_type::image:
×
132
        return path("?");
×
133
    case relationship_type::office_document:
526✔
134
        return path("/xl/workbook.xml");
1,052✔
135
    case relationship_type::pivot_table:
×
136
        return path("/xl/pivotTable.xml");
×
137
    case relationship_type::pivot_table_cache_definition:
×
138
        return path("?");
×
139
    case relationship_type::pivot_table_cache_records:
×
140
        return path("?");
×
141
    case relationship_type::printer_settings:
×
142
        return path("/xl/printerSettings.xml");
×
143
    case relationship_type::query_table:
×
144
        return path("/xl/queryTable.xml");
×
145
    case relationship_type::revision_log:
×
146
        return path("/xl/revisionLog.xml");
×
147
    case relationship_type::shared_string_table:
96✔
148
        return path("/xl/sharedStrings.xml");
192✔
149
    case relationship_type::shared_workbook:
×
150
        return path("/xl/sharedWorkbook.xml");
×
151
    case relationship_type::shared_workbook_revision_headers:
×
152
        return path("?");
×
153
    case relationship_type::shared_workbook_user_data:
×
154
        return path("?");
×
155
    case relationship_type::single_cell_table_definitions:
×
156
        return path("?");
×
157
    case relationship_type::stylesheet:
526✔
158
        return path("/xl/styles.xml");
1,052✔
159
    case relationship_type::table_definition:
×
160
        return path("/xl/tableDefinition.xml");
×
161
    case relationship_type::theme:
526✔
162
        return path("/xl/theme/theme1.xml");
1,052✔
163
    case relationship_type::thumbnail:
×
164
        return path("/docProps/thumbnail.jpg");
×
165
    case relationship_type::unknown:
×
166
        return path("/xl/unknown.xml");
×
167
    case relationship_type::vml_drawing:
×
168
        return path("/xl/vmlDrawing.xml");
×
169
    case relationship_type::volatile_dependencies:
×
170
        return path("/xl/volatileDependencies.xml");
×
171
    case relationship_type::vbaproject:
×
172
        return path("/xl/vbaProject.bin");
×
173
    case relationship_type::worksheet:
×
174
        return path("/xl/worksheets/sheet" + std::to_string(index) + ".xml");
×
175
    }
176

177
    default_case(path("/xl/unknownPart.xml"));
×
178
}
179

180
std::string content_type(xlnt::relationship_type type)
1,370✔
181
{
182
    using xlnt::relationship_type;
183

184
    switch (type)
1,370✔
185
    {
186
    case relationship_type::calculation_chain:
6✔
187
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml";
12✔
188
    case relationship_type::chartsheet:
×
189
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml";
×
190
    case relationship_type::comments:
×
191
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml";
×
192
    case relationship_type::connections:
×
193
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml";
×
194
    case relationship_type::core_properties:
263✔
195
        return "application/vnd.openxmlformats-package.core-properties+xml";
526✔
196
    case relationship_type::custom_properties:
1✔
197
        return "application/vnd.openxmlformats-officedocument.custom-properties+xml";
2✔
198
    case relationship_type::custom_property:
×
199
        throw xlnt::unhandled_switch_case();
×
200
    case relationship_type::custom_xml_mappings:
×
201
        return "application/xml";
×
202
    case relationship_type::dialogsheet:
×
203
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml";
×
204
    case relationship_type::drawings:
×
205
        return "application/vnd.openxmlformats-officedocument.drawing+xml";
×
206
    case relationship_type::extended_properties:
263✔
207
        return "application/vnd.openxmlformats-officedocument.extended-properties+xml";
526✔
208
    case relationship_type::external_workbook_references:
×
209
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml";
×
210
    case relationship_type::hyperlink:
×
211
        throw xlnt::unhandled_switch_case();
×
212
    case relationship_type::image:
×
213
        throw xlnt::unhandled_switch_case();
×
214
    case relationship_type::office_document:
263✔
215
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
526✔
216
    case relationship_type::pivot_table:
×
217
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml";
×
218
    case relationship_type::pivot_table_cache_definition:
×
219
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml";
×
220
    case relationship_type::pivot_table_cache_records:
×
221
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml";
×
222
    case relationship_type::printer_settings:
×
223
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings";
×
224
    case relationship_type::query_table:
×
225
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml";
×
226
    case relationship_type::revision_log:
×
227
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml";
×
228
    case relationship_type::shared_string_table:
48✔
229
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml";
96✔
230
    case relationship_type::shared_workbook:
×
231
        throw xlnt::unhandled_switch_case();
×
232
    case relationship_type::shared_workbook_revision_headers:
×
233
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml";
×
234
    case relationship_type::shared_workbook_user_data:
×
235
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml";
×
236
    case relationship_type::single_cell_table_definitions:
×
237
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml";
×
238
    case relationship_type::stylesheet:
263✔
239
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml";
526✔
240
    case relationship_type::table_definition:
×
241
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml";
×
242
    case relationship_type::theme:
263✔
243
        return "application/vnd.openxmlformats-officedocument.theme+xml";
526✔
244
    case relationship_type::thumbnail:
×
245
        return "image/jpeg";
×
246
    case relationship_type::unknown:
×
247
        return "";
×
248
    case relationship_type::vml_drawing:
×
249
        return "application/vnd.openxmlformats-officedocument.vmlDrawing";
×
250
    case relationship_type::volatile_dependencies:
×
251
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.volatileDependencies+xml";
×
252
    case relationship_type::vbaproject:
×
253
        return "application/vnd.ms-office.vbaProject";
×
254
    case relationship_type::worksheet:
×
255
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
×
256
    }
257

258
    default_case("application/xml");
×
259
}
260

261
} // namespace
262

263
namespace xlnt {
264

265
bool workbook::has_core_property(xlnt::core_property type) const
×
266
{
267
    return ::contains(d_->core_properties_, type);
×
268
}
269

270
std::vector<xlnt::core_property> workbook::core_properties() const
84✔
271
{
272
    return keys(d_->core_properties_);
84✔
273
}
274

275
variant workbook::core_property(xlnt::core_property type) const
462✔
276
{
277
    for (auto iter : d_->core_properties_)
1,783✔
278
    {
279
        if (iter.first == type)
1,783✔
280
        {
281
            return iter.second;
924✔
282
        }
283
    }
1,783✔
284

285
    throw xlnt::exception("workbook doesn't have core property");
×
286
}
287

288
void workbook::core_property(xlnt::core_property type, const variant &value)
1,613✔
289
{
290
    register_package_part(relationship_type::core_properties);
1,613✔
291

292
    for (auto &iter : d_->core_properties_)
4,827✔
293
    {
294
        if (iter.first == type)
3,224✔
295
        {
296
            iter.second = value;
10✔
297
            return;
10✔
298
        }
299
    }
300

301
    d_->core_properties_.push_back({type, value});
1,603✔
302
}
303

304
bool workbook::has_extended_property(xlnt::extended_property type) const
764✔
305
{
306
    return ::contains(d_->extended_properties_, type);
764✔
307
}
308

309
std::vector<xlnt::extended_property> workbook::extended_properties() const
84✔
310
{
311
    return keys(d_->extended_properties_);
84✔
312
}
313

314
void workbook::extended_property(xlnt::extended_property type, const variant &value)
4,267✔
315
{
316
    register_package_part(relationship_type::extended_properties);
4,267✔
317

318
    for (auto &iter : d_->extended_properties_)
23,084✔
319
    {
320
        if (iter.first == type)
19,505✔
321
        {
322
            iter.second = value;
688✔
323
            return;
688✔
324
        }
325
    }
326

327
    d_->extended_properties_.push_back({type, value});
3,579✔
328
}
329

330
variant workbook::extended_property(xlnt::extended_property type) const
845✔
331
{
332
    for (auto iter : d_->extended_properties_)
4,901✔
333
    {
334
        if (iter.first == type)
4,901✔
335
        {
336
            return iter.second;
1,690✔
337
        }
338
    }
4,901✔
339

340
    throw xlnt::exception("workbook doesn't have extended property");
×
341
}
342

343
bool workbook::has_custom_property(const std::string &property_name) const
1✔
344
{
345
    return ::contains(d_->custom_properties_, property_name);
1✔
346
}
347

348
std::vector<std::string> workbook::custom_properties() const
3✔
349
{
350
    return keys(d_->custom_properties_);
3✔
351
}
352

353
void workbook::custom_property(const std::string &property_name, const variant &value)
6✔
354
{
355
    register_package_part(relationship_type::custom_properties);
6✔
356

357
    for (auto &iter : d_->custom_properties_)
7✔
358
    {
359
        if (iter.first == property_name)
1✔
360
        {
361
            iter.second = value;
×
362
            return;
×
363
        }
364
    }
365

366
    d_->custom_properties_.push_back({property_name, value});
6✔
367
}
368

369
variant workbook::custom_property(const std::string &property_name) const
5✔
370
{
371
    for (auto iter : d_->custom_properties_)
6✔
372
    {
373
        if (iter.first == property_name)
6✔
374
        {
375
            return iter.second;
10✔
376
        }
377
    }
6✔
378

379
    throw xlnt::exception("workbook doesn't have custom property");
×
380
}
381

382
void workbook::abs_path(const std::string &path)
×
383
{
384
    d_->abs_path_ = path;
×
385
}
×
386

387
void workbook::arch_id_flags(const std::size_t flags)
×
388
{
389
    d_->arch_id_flags_ = flags;
×
390
}
×
391

392
workbook workbook::empty()
263✔
393
{
394
    auto impl = new detail::workbook_impl();
263✔
395
    workbook wb(impl);
263✔
396

397
    wb.register_package_part(relationship_type::office_document);
263✔
398

399
    wb.d_->manifest_.register_default_type("rels",
1,052✔
400
        "application/vnd.openxmlformats-package.relationships+xml");
401
    wb.d_->manifest_.register_default_type("xml",
1,315✔
402
        "application/xml");
403

404
    wb.thumbnail(excel_thumbnail(), "jpeg", "image/jpeg");
789✔
405

406
    wb.core_property(xlnt::core_property::creator, "Microsoft Office User");
263✔
407
    wb.core_property(xlnt::core_property::last_modified_by, "Microsoft Office User");
263✔
408
    wb.core_property(xlnt::core_property::created, datetime(2016, 8, 12, 3, 16, 56));
263✔
409
    wb.core_property(xlnt::core_property::modified, datetime(2016, 8, 12, 3, 17, 16));
263✔
410

411
    wb.extended_property(xlnt::extended_property::application, "Microsoft Macintosh Excel");
263✔
412
    wb.extended_property(xlnt::extended_property::doc_security, 0);
263✔
413
    wb.extended_property(xlnt::extended_property::scale_crop, false);
263✔
414
    wb.extended_property(xlnt::extended_property::heading_pairs, std::vector<variant>{variant("Worksheets"), variant(1)});
1,052✔
415
    wb.extended_property(xlnt::extended_property::titles_of_parts, {"Sheet1"});
263✔
416
    wb.extended_property(xlnt::extended_property::company, "");
263✔
417
    wb.extended_property(xlnt::extended_property::links_up_to_date, false);
263✔
418
    wb.extended_property(xlnt::extended_property::shared_doc, false);
263✔
419
    wb.extended_property(xlnt::extended_property::hyperlinks_changed, false);
263✔
420
    wb.extended_property(xlnt::extended_property::app_version, "15.0300");
263✔
421

422
    detail::workbook_impl::file_version_t file_version;
263✔
423
    file_version.app_name = "xl";
263✔
424
    file_version.last_edited = 6;
263✔
425
    file_version.lowest_edited = 6;
263✔
426
    file_version.rup_build = 26709;
263✔
427
    wb.d_->file_version_ = file_version;
263✔
428

429
    xlnt::workbook_view wb_view;
263✔
430
    wb_view.active_tab = 0;
263✔
431
    wb_view.x_window = 0;
263✔
432
    wb_view.y_window = 460;
263✔
433
    wb_view.window_width = 28800;
263✔
434
    wb_view.window_height = 17460;
263✔
435
    wb_view.tab_ratio = 500;
263✔
436
    wb.view(wb_view);
263✔
437

438
    auto ws = wb.create_sheet();
263✔
439

440
    page_margins margins;
263✔
441
    margins.left(0.7);
263✔
442
    margins.right(0.7);
263✔
443
    margins.top(0.75);
263✔
444
    margins.bottom(0.75);
263✔
445
    margins.header(0.3);
263✔
446
    margins.footer(0.3);
263✔
447
    ws.page_margins(margins);
263✔
448

449
    sheet_view view;
263✔
450
    ws.add_view(view);
263✔
451

452
    auto &format_properties = ws.d_->format_properties_;
263✔
453
    format_properties.base_col_width = 10.0;
263✔
454
    format_properties.default_row_height = 16.0;
263✔
455

456
    wb.theme(xlnt::theme());
263✔
457

458
    wb.d_->stylesheet_ = detail::stylesheet();
263✔
459
    auto &stylesheet = wb.d_->stylesheet_.get();
263✔
460
    stylesheet.parent = &wb;
263✔
461

462
    auto default_border = border()
263✔
463
                              .side(border_side::bottom, border::border_property())
526✔
464
                              .side(border_side::top, border::border_property())
526✔
465
                              .side(border_side::start, border::border_property())
526✔
466
                              .side(border_side::end, border::border_property())
526✔
467
                              .side(border_side::diagonal, border::border_property());
263✔
468
    wb.d_->stylesheet_.get().borders.push_back(default_border);
263✔
469

470
    auto default_fill = fill(pattern_fill()
526✔
471
                                 .type(pattern_fill_type::none));
263✔
472
    stylesheet.fills.push_back(default_fill);
263✔
473
    auto gray125_fill = pattern_fill()
526✔
474
                            .type(pattern_fill_type::gray125);
263✔
475
    stylesheet.fills.push_back(gray125_fill);
263✔
476

477
    auto default_font = font()
263✔
478
                            .name("Calibri")
526✔
479
                            .size(12)
263✔
480
                            .scheme("minor")
526✔
481
                            .family(2)
263✔
482
                            .color(theme_color(1));
263✔
483
    stylesheet.fonts.push_back(default_font);
263✔
484

485
    wb.create_builtin_style(0)
263✔
486
        .border(default_border)
263✔
487
        .fill(default_fill)
526✔
488
        .font(default_font)
526✔
489
        .number_format(xlnt::number_format::general());
263✔
490

491
    wb.create_format(true)
263✔
492
        .border(default_border)
263✔
493
        .fill(default_fill)
526✔
494
        .font(default_font)
526✔
495
        .number_format(xlnt::number_format::general())
526✔
496
        .style("Normal");
526✔
497

498
    xlnt::calculation_properties calc_props;
263✔
499
    calc_props.calc_id = 150000;
263✔
500
    calc_props.concurrent_calc = false;
263✔
501
    wb.calculation_properties(calc_props);
263✔
502

503
    return wb;
263✔
504
}
526✔
505

506
workbook::workbook()
260✔
507
{
508
    auto wb_template = empty();
260✔
509
    swap(wb_template);
260✔
510
}
260✔
511

512
workbook::workbook(const xlnt::path &file)
2✔
513
{
514
    *this = empty();
2✔
515
    load(file);
2✔
516
}
2✔
517

518
workbook::workbook(const xlnt::path &file, const std::string &password)
×
519
{
520
    *this = empty();
×
521
    load(file, password);
×
522
}
×
523

524
workbook::workbook(std::istream &data)
1✔
525
{
526
    *this = empty();
1✔
527
    load(data);
1✔
528
}
1✔
529

530
workbook::workbook(std::istream &data, const std::string &password)
×
531
{
532
    *this = empty();
×
533
    load(data, password);
×
534
}
×
535

536
workbook::workbook(detail::workbook_impl *impl)
266✔
537
    : d_(impl)
266✔
538
{
539
    if (impl != nullptr)
266✔
540
    {
541
        if (d_->stylesheet_.is_set())
263✔
542
        {
543
            d_->stylesheet_.get().parent = this;
×
544
        }
545
    }
546
}
266✔
547

548
void workbook::register_package_part(relationship_type type)
6,149✔
549
{
550
    if (!manifest().has_relationship(path("/"), type))
18,447✔
551
    {
552
        manifest().register_override_type(default_path(type), content_type(type));
790✔
553
        manifest().register_relationship(uri("/"), type,
1,580✔
554
            uri(default_path(type).relative_to(path("/")).string()),
3,160✔
555
            target_mode::internal);
556
    }
557
}
6,149✔
558

559
void workbook::register_workbook_part(relationship_type type)
41,940✔
560
{
561
    auto wb_rel = manifest().relationship(path("/"), relationship_type::office_document);
83,880✔
562
    auto wb_path = manifest().canonicalize({wb_rel});
167,760✔
563

564
    if (!manifest().has_relationship(wb_path, type))
41,940✔
565
    {
566
        manifest().register_override_type(default_path(type), content_type(type));
580✔
567
        manifest().register_relationship(uri(wb_path.string()), type,
1,160✔
568
            uri(default_path(type).relative_to(wb_path.resolve(path("/"))).string()),
2,320✔
569
            target_mode::internal);
570
    }
571
}
83,880✔
572

573
void workbook::register_worksheet_part(worksheet ws, relationship_type type)
34✔
574
{
575
    auto wb_rel = manifest().relationship(path("/"),
102✔
576
        relationship_type::office_document);
34✔
577
    auto ws_rel = manifest().relationship(wb_rel.target().path(),
34✔
578
        d_->sheet_title_rel_id_map_.at(ws.title()));
34✔
579
    path ws_path(ws_rel.source().path().parent().append(ws_rel.target().path()));
34✔
580

581
    if (type == relationship_type::comments)
34✔
582
    {
583
        if (!manifest().has_relationship(ws_path, relationship_type::vml_drawing))
34✔
584
        {
585
            std::size_t file_number = 1;
3✔
586
            path filename("vmlDrawing1.vml");
3✔
587
            bool filename_exists = true;
3✔
588

589
            while (filename_exists)
7✔
590
            {
591
                filename_exists = false;
4✔
592

593
                for (auto current_ws_rel :
4✔
594
                    manifest().relationships(wb_rel.target().path(), xlnt::relationship_type::worksheet))
14✔
595
                {
596
                    path current_ws_path(current_ws_rel.source().path().parent().append(current_ws_rel.target().path()));
6✔
597
                    if (!manifest().has_relationship(current_ws_path, xlnt::relationship_type::vml_drawing)) continue;
6✔
598

599
                    for (auto current_ws_child_rel :
2✔
600
                        manifest().relationships(current_ws_path, xlnt::relationship_type::vml_drawing))
5✔
601
                    {
602
                        if (current_ws_child_rel.target().path() == path("../drawings").append(filename))
4✔
603
                        {
604
                            filename_exists = true;
1✔
605
                            break;
1✔
606
                        }
607
                    }
3✔
608
                }
14✔
609

610
                if (filename_exists)
4✔
611
                {
612
                    file_number++;
1✔
613
                    filename = path("vmlDrawing" + std::to_string(file_number) + ".vml");
1✔
614
                }
615
            }
616

617
            manifest().register_default_type("vml", "application/vnd.openxmlformats-officedocument.vmlDrawing");
15✔
618

619
            const path relative_path(path("../drawings").append(filename));
3✔
620
            manifest().register_relationship(
6✔
621
                uri(ws_path.string()), relationship_type::vml_drawing, uri(relative_path.string()), target_mode::internal);
6✔
622
        }
3✔
623

624
        if (!manifest().has_relationship(ws_path, relationship_type::comments))
34✔
625
        {
626
            std::size_t file_number = 1;
3✔
627
            path filename("comments1.xml");
6✔
628

629
            while (true)
630
            {
631
                if (!manifest().has_override_type(constants::package_xl().append(filename))) break;
4✔
632

633
                file_number++;
1✔
634
                filename = path("comments" + std::to_string(file_number) + ".xml");
1✔
635
            }
636

637
            const path absolute_path(constants::package_xl().append(filename));
3✔
638
            manifest().register_override_type(
9✔
639
                absolute_path, "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml");
640

641
            const path relative_path(path("..").append(filename));
3✔
642
            manifest().register_relationship(
6✔
643
                uri(ws_path.string()), relationship_type::comments, uri(relative_path.string()), target_mode::internal);
6✔
644
        }
3✔
645
    }
646
}
34✔
647

648
const worksheet workbook::sheet_by_title(const std::string &title) const
128✔
649
{
650
    for (auto &impl : d_->worksheets_)
172✔
651
    {
652
        if (impl.title_ == title)
171✔
653
        {
654
            return worksheet(&impl);
127✔
655
        }
656
    }
657

658
    throw key_not_found();
1✔
659
}
660

661
worksheet workbook::sheet_by_title(const std::string &title)
16✔
662
{
663
    for (auto &impl : d_->worksheets_)
28✔
664
    {
665
        if (impl.title_ == title)
25✔
666
        {
667
            return worksheet(&impl);
13✔
668
        }
669
    }
670

671
    throw key_not_found();
3✔
672
}
673

674
worksheet workbook::sheet_by_index(std::size_t index)
596✔
675
{
676
    if (index >= d_->worksheets_.size())
596✔
677
    {
678
        throw invalid_parameter();
1✔
679
    }
680

681
    auto iter = d_->worksheets_.begin();
595✔
682

683
    for (std::size_t i = 0; i < index; ++i)
677✔
684
    {
685
        ++iter;
82✔
686
    }
687

688
    return worksheet(&*iter);
1,190✔
689
}
690

691
const worksheet workbook::sheet_by_index(std::size_t index) const
1,457✔
692
{
693
    if (index >= d_->worksheets_.size())
1,457✔
694
    {
695
        throw invalid_parameter();
1✔
696
    }
697

698
    auto iter = d_->worksheets_.begin();
1,456✔
699

700
    for (std::size_t i = 0; i < index; ++i, ++iter)
1,847✔
701
    {
702
    }
703

704
    return worksheet(&*iter);
2,912✔
705
}
706

707
worksheet workbook::sheet_by_id(std::size_t id)
×
708
{
709
    for (auto &impl : d_->worksheets_)
×
710
    {
711
        if (impl.id_ == id)
×
712
        {
713
            return worksheet(&impl);
×
714
        }
715
    }
716

717
    throw key_not_found();
×
718
}
719

720
const worksheet workbook::sheet_by_id(std::size_t id) const
×
721
{
722
    for (auto &impl : d_->worksheets_)
×
723
    {
724
        if (impl.id_ == id)
×
725
        {
726
            return worksheet(&impl);
×
727
        }
728
    }
729

730
    throw key_not_found();
×
731
}
732

733
bool workbook::sheet_hidden_by_index(std::size_t index) const
1✔
734
{
735
    if (index >= d_->sheet_hidden_.size())
1✔
736
    {
737
        throw invalid_parameter();
×
738
    }
739

740
    return d_->sheet_hidden_.at(index);
1✔
741
}
742

743
worksheet workbook::active_sheet()
157✔
744
{
745
    return sheet_by_index(d_->active_sheet_index_.is_set() ? d_->active_sheet_index_.get() : 0);
157✔
746
}
747
void workbook::active_sheet(std::size_t index)
1✔
748
{
749
    d_->active_sheet_index_.set(index);
1✔
750
    d_->view_.get().active_tab = index;
1✔
751
}
1✔
752

753
bool workbook::has_named_range(const std::string &name) const
11✔
754
{
755
    for (auto worksheet : *this)
27✔
756
    {
757
        if (worksheet.has_named_range(name))
17✔
758
        {
759
            return true;
9✔
760
        }
761
    }
762
    return false;
2✔
763
}
764

765
worksheet workbook::create_sheet()
319✔
766
{
767
    std::string title = "Sheet1";
319✔
768
    int index = 1;
319✔
769

770
    // make a unique sheet name. Sheet<1...n>
771
    while (contains(title))
383✔
772
    {
773
        title = "Sheet" + std::to_string(++index);
64✔
774
    }
775
    // unique sheet id
776
    size_t sheet_id = 1;
319✔
777
    for (const auto ws : *this)
387✔
778
    {
779
        sheet_id = std::max(sheet_id, ws.id() + 1);
68✔
780
    }
781
    d_->worksheets_.push_back(detail::worksheet_impl(this, sheet_id, title));
319✔
782
    // unique sheet file name
783
    auto workbook_rel = d_->manifest_.relationship(path("/"), relationship_type::office_document);
638✔
784
    auto workbook_files = d_->manifest_.relationships(workbook_rel.target().path());
319✔
785
    auto rel_vec_contains = [&workbook_files](const xlnt::path &new_file_id) {
320✔
786
        return workbook_files.end() != std::find_if(workbook_files.begin(), workbook_files.end(), [&new_file_id](const xlnt::relationship &rel) {
640✔
787
            return rel.target().path() == new_file_id;
184✔
788
        });
640✔
789
    };
319✔
790

791
    size_t file_id = sheet_id;
319✔
792
    xlnt::path sheet_relative_path;
319✔
793
    do
794
    {
795
        sheet_relative_path = path("worksheets").append("sheet" + std::to_string(file_id++) + ".xml");
320✔
796
    } while (rel_vec_contains(sheet_relative_path));
320✔
797

798
    uri relative_sheet_uri(sheet_relative_path.string());
319✔
799
    auto absolute_sheet_path = path("/xl").append(relative_sheet_uri.path());
319✔
800
    d_->manifest_.register_override_type(
638✔
801
        absolute_sheet_path, "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
802
    auto ws_rel = d_->manifest_.register_relationship(
319✔
803
        workbook_rel.target(), relationship_type::worksheet, relative_sheet_uri, target_mode::internal);
319✔
804
    d_->sheet_title_rel_id_map_[title] = ws_rel;
319✔
805

806
    update_sheet_properties();
319✔
807
    reorder_relationships();
319✔
808

809
    return worksheet(&d_->worksheets_.back());
638✔
810
}
319✔
811

812
worksheet workbook::copy_sheet(worksheet to_copy)
3✔
813
{
814
    if (to_copy.d_->parent_ != this) throw invalid_parameter();
3✔
815

816
    detail::worksheet_impl impl(*to_copy.d_);
2✔
817
    auto new_sheet = create_sheet();
2✔
818
    impl.title_ = new_sheet.title();
2✔
819
    impl.id_ = new_sheet.id();
2✔
820
    *new_sheet.d_ = impl;
2✔
821

822
    return new_sheet;
4✔
823
}
2✔
824

825
worksheet workbook::copy_sheet(worksheet to_copy, std::size_t index)
1✔
826
{
827
    copy_sheet(to_copy);
1✔
828

829
    if (index != d_->worksheets_.size() - 1)
1✔
830
    {
831
        auto iter = d_->worksheets_.begin();
1✔
832

833
        for (std::size_t i = 0; i < index; ++i, ++iter)
1✔
834
        {
835
        }
836

837
        d_->worksheets_.insert(iter, d_->worksheets_.back());
1✔
838
        d_->worksheets_.pop_back();
1✔
839
    }
840

841
    return sheet_by_index(index);
1✔
842
}
843

844
std::size_t workbook::index(worksheet ws)
3✔
845
{
846
    auto match = std::find(begin(), end(), ws);
3✔
847

848
    if (match == end())
3✔
849
    {
850
        throw invalid_parameter();
1✔
851
    }
852

853
    return static_cast<std::size_t>(std::distance(begin(), match));
6✔
854
}
855

856
void workbook::create_named_range(const std::string &name, worksheet range_owner, const std::string &reference_string)
7✔
857
{
858
    create_named_range(name, range_owner, range_reference(reference_string));
7✔
859
}
6✔
860

861
void workbook::create_named_range(const std::string &name, worksheet range_owner, const range_reference &reference)
7✔
862
{
863
    sheet_by_title(range_owner.title()).create_named_range(name, reference);
8✔
864
}
6✔
865

866
void workbook::remove_named_range(const std::string &name)
2✔
867
{
868
    for (auto ws : *this)
8✔
869
    {
870
        if (ws.has_named_range(name))
4✔
871
        {
872
            ws.remove_named_range(name);
1✔
873
            return;
1✔
874
        }
875
    }
876

877
    throw key_not_found();
1✔
878
}
879

880
range workbook::named_range(const std::string &name)
2✔
881
{
882
    for (auto ws : *this)
8✔
883
    {
884
        if (ws.has_named_range(name))
4✔
885
        {
886
            return ws.named_range(name);
1✔
887
        }
888
    }
889

890
    throw key_not_found();
1✔
891
}
892

893
void workbook::load(std::istream &stream)
99✔
894
{
895
    clear();
99✔
896
    detail::xlsx_consumer consumer(*this);
99✔
897

898
    try
899
    {
900
        consumer.read(stream);
99✔
901
    }
902
    catch (xlnt::exception &e)
1✔
903
    {
904
        if (e.what() == std::string("xlnt::exception : encrypted xlsx, password required"))
3✔
905
        {
906
            stream.seekg(0, std::ios::beg);
×
907
            consumer.read(stream, "VelvetSweatshop");
×
908
        }
909
        else
910
        {
911
            throw;
1✔
912
        }
913
    }
1✔
914
}
99✔
915

916
void workbook::load(const std::vector<std::uint8_t> &data)
23✔
917
{
918
    if (data.size() < 22) // the shortest ZIP file is 22 bytes
23✔
919
    {
920
        throw xlnt::exception("file is empty or malformed");
×
921
    }
922

923
    xlnt::detail::vector_istreambuf data_buffer(data);
23✔
924
    std::istream data_stream(&data_buffer);
23✔
925
    load(data_stream);
23✔
926
}
23✔
927

928
void workbook::load(const std::string &filename)
12✔
929
{
930
    return load(path(filename));
12✔
931
}
932

933
void workbook::load(const path &filename)
74✔
934
{
935
    std::ifstream file_stream;
74✔
936
    open_stream(file_stream, filename.string());
74✔
937

938
    if (!file_stream.good())
74✔
939
    {
940
        throw xlnt::exception("file not found " + filename.string());
×
941
    }
942

943
    load(file_stream);
74✔
944
}
74✔
945

946
void workbook::load(const std::string &filename, const std::string &password)
×
947
{
948
    return load(path(filename), password);
×
949
}
950

951
void workbook::load(const path &filename, const std::string &password)
9✔
952
{
953
    std::ifstream file_stream;
9✔
954
    open_stream(file_stream, filename.string());
9✔
955

956
    if (!file_stream.good())
9✔
957
    {
958
        throw xlnt::exception("file not found " + filename.string());
×
959
    }
960

961
    return load(file_stream, password);
14✔
962
}
9✔
963

964
void workbook::load(const std::vector<std::uint8_t> &data, const std::string &password)
4✔
965
{
966
    if (data.size() < 22) // the shortest ZIP file is 22 bytes
4✔
967
    {
968
        throw xlnt::exception("file is empty or malformed");
×
969
    }
970

971
    xlnt::detail::vector_istreambuf data_buffer(data);
4✔
972
    std::istream data_stream(&data_buffer);
4✔
973
    load(data_stream, password);
4✔
974
}
4✔
975

976
void workbook::load(std::istream &stream, const std::string &password)
13✔
977
{
978
    clear();
13✔
979
    detail::xlsx_consumer consumer(*this);
13✔
980
    consumer.read(stream, password);
13✔
981
}
13✔
982

983
void workbook::save(std::vector<std::uint8_t> &data) const
13✔
984
{
985
    xlnt::detail::vector_ostreambuf data_buffer(data);
13✔
986
    std::ostream data_stream(&data_buffer);
13✔
987
    save(data_stream);
13✔
988
}
13✔
989

990
void workbook::save(std::vector<std::uint8_t> &data, const std::string &password) const
×
991
{
992
    xlnt::detail::vector_ostreambuf data_buffer(data);
×
993
    std::ostream data_stream(&data_buffer);
×
994
    save(data_stream, password);
×
995
}
×
996

997
void workbook::save(const std::string &filename) const
49✔
998
{
999
    save(path(filename));
49✔
1000
}
49✔
1001

1002
void workbook::save(const std::string &filename, const std::string &password) const
4✔
1003
{
1004
    save(path(filename), password);
4✔
1005
}
4✔
1006

1007
void workbook::save(const path &filename) const
69✔
1008
{
1009
    std::ofstream file_stream;
69✔
1010
    open_stream(file_stream, filename.string());
69✔
1011
    save(file_stream);
69✔
1012
}
69✔
1013

1014
void workbook::save(const path &filename, const std::string &password) const
4✔
1015
{
1016
    std::ofstream file_stream;
4✔
1017
    open_stream(file_stream, filename.string());
4✔
1018
    save(file_stream, password);
4✔
1019
}
4✔
1020

1021
void workbook::save(std::ostream &stream) const
82✔
1022
{
1023
    detail::xlsx_producer producer(*this);
82✔
1024
    producer.write(stream);
82✔
1025
}
82✔
1026

1027
void workbook::save(std::ostream &stream, const std::string &password) const
4✔
1028
{
1029
    detail::xlsx_producer producer(*this);
4✔
1030
    producer.write(stream, password);
4✔
1031
}
4✔
1032

1033
#ifdef _MSC_VER
1034
void workbook::save(const std::wstring &filename) const
1035
{
1036
    std::ofstream file_stream;
1037
    open_stream(file_stream, filename);
1038
    save(file_stream);
1039
}
1040

1041
void workbook::save(const std::wstring &filename, const std::string &password) const
1042
{
1043
    std::ofstream file_stream;
1044
    open_stream(file_stream, filename);
1045
    save(file_stream, password);
1046
}
1047

1048
void workbook::load(const std::wstring &filename)
1049
{
1050
    std::ifstream file_stream;
1051
    open_stream(file_stream, filename);
1052
    load(file_stream);
1053
}
1054

1055
void workbook::load(const std::wstring &filename, const std::string &password)
1056
{
1057
    std::ifstream file_stream;
1058
    open_stream(file_stream, filename);
1059
    load(file_stream, password);
1060
}
1061
#endif
1062

1063
void workbook::remove_sheet(worksheet ws)
8✔
1064
{
1065
    auto match_iter = std::find_if(d_->worksheets_.begin(), d_->worksheets_.end(),
8✔
1066
        [=](detail::worksheet_impl &comp) { return &comp == ws.d_; });
23✔
1067

1068
    if (match_iter == d_->worksheets_.end())
8✔
1069
    {
1070
        throw invalid_parameter();
×
1071
    }
1072

1073
    auto ws_rel_id = d_->sheet_title_rel_id_map_.at(ws.title());
8✔
1074
    auto wb_rel = d_->manifest_.relationship(path("/"), xlnt::relationship_type::office_document);
16✔
1075
    auto ws_rel = d_->manifest_.relationship(wb_rel.target().path(), ws_rel_id);
8✔
1076
    auto ws_part = d_->manifest_.canonicalize({wb_rel, ws_rel}).resolve(path("/"));
56✔
1077
    d_->manifest_.unregister_override_type(ws_part);
8✔
1078
    auto rel_id_map = d_->manifest_.unregister_relationship(wb_rel.target(), ws_rel_id);
8✔
1079
    d_->sheet_title_rel_id_map_.erase(ws.title());
8✔
1080
    d_->worksheets_.erase(match_iter);
8✔
1081

1082
    // Shift sheet title->ID mappings down as a result of manifest::unregister_relationship above.
1083
    for (auto &title_rel_id_pair : d_->sheet_title_rel_id_map_)
32✔
1084
    {
1085
        title_rel_id_pair.second = rel_id_map.count(title_rel_id_pair.second) > 0
24✔
1086
            ? rel_id_map[title_rel_id_pair.second]
19✔
1087
            : title_rel_id_pair.second;
43✔
1088
    }
1089

1090
    update_sheet_properties();
8✔
1091
}
16✔
1092

1093
worksheet workbook::create_sheet(std::size_t index)
1✔
1094
{
1095
    create_sheet();
1✔
1096

1097
    if (index != d_->worksheets_.size() - 1)
1✔
1098
    {
1099
        auto iter = d_->worksheets_.begin();
×
1100

1101
        for (std::size_t i = 0; i < index; ++i, ++iter)
×
1102
        {
1103
        }
1104

1105
        d_->worksheets_.insert(iter, d_->worksheets_.back());
×
1106
        d_->worksheets_.pop_back();
×
1107
    }
1108

1109
    return sheet_by_index(index);
1✔
1110
}
1111

1112
worksheet workbook::create_sheet_with_rel(const std::string &title, const relationship &rel)
×
1113
{
1114
    auto sheet_id = d_->worksheets_.size() + 1;
×
1115
    d_->worksheets_.push_back(detail::worksheet_impl(this, sheet_id, title));
×
1116

1117
    auto workbook_rel = d_->manifest_.relationship(path("/"), relationship_type::office_document);
×
1118
    auto sheet_absoulute_path = workbook_rel.target().path().parent().append(rel.target().path());
×
1119
    d_->manifest_.register_override_type(sheet_absoulute_path,
×
1120
        "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
1121
    auto ws_rel = d_->manifest_.register_relationship(workbook_rel.target(),
×
1122
        relationship_type::worksheet, rel.target(), target_mode::internal);
×
1123
    d_->sheet_title_rel_id_map_[title] = ws_rel;
×
1124

1125
    update_sheet_properties();
×
1126

1127
    return worksheet(&d_->worksheets_.back());
×
1128
}
×
1129

1130
workbook::iterator workbook::begin()
631✔
1131
{
1132
    return iterator(*this, 0);
631✔
1133
}
1134

1135
workbook::iterator workbook::end()
625✔
1136
{
1137
    return iterator(*this, d_->worksheets_.size());
625✔
1138
}
1139

1140
workbook::const_iterator workbook::begin() const
1,382✔
1141
{
1142
    return cbegin();
1,382✔
1143
}
1144

1145
workbook::const_iterator workbook::end() const
1,382✔
1146
{
1147
    return cend();
1,382✔
1148
}
1149

1150
workbook::const_iterator workbook::cbegin() const
1,392✔
1151
{
1152
    return const_iterator(*this, 0);
1,392✔
1153
}
1154

1155
workbook::const_iterator workbook::cend() const
1,383✔
1156
{
1157
    return const_iterator(*this, d_->worksheets_.size());
1,383✔
1158
}
1159

1160
std::vector<std::string> workbook::sheet_titles() const
669✔
1161
{
1162
    std::vector<std::string> names;
669✔
1163

1164
    for (auto ws : *this)
1,501✔
1165
    {
1166
        names.push_back(ws.title());
832✔
1167
    }
1168

1169
    return names;
669✔
1170
}
×
1171

1172
std::size_t workbook::sheet_count() const
348✔
1173
{
1174
    return d_->worksheets_.size();
348✔
1175
}
1176

1177
worksheet workbook::operator[](const std::string &name)
2✔
1178
{
1179
    return sheet_by_title(name);
2✔
1180
}
1181

1182
worksheet workbook::operator[](std::size_t index)
426✔
1183
{
1184
    return sheet_by_index(index);
426✔
1185
}
1186

1187
void workbook::clear()
244✔
1188
{
1189
    *d_ = detail::workbook_impl();
244✔
1190
    d_->stylesheet_.clear();
244✔
1191
}
244✔
1192

1193
bool workbook::operator==(const workbook &rhs) const
12✔
1194
{
1195
    return *d_ == *rhs.d_;
12✔
1196
}
1197

1198
bool workbook::operator!=(const workbook &rhs) const
3✔
1199
{
1200
    return !operator==(rhs);
3✔
1201
}
1202

1203
void workbook::swap(workbook &right)
269✔
1204
{
1205
    auto &left = *this;
269✔
1206

1207
    using std::swap;
1208
    swap(left.d_, right.d_);
269✔
1209

1210
    if (left.d_ != nullptr)
269✔
1211
    {
1212
        for (auto ws : left)
807✔
1213
        {
1214
            ws.parent(left);
269✔
1215
        }
1216

1217
        if (left.d_->stylesheet_.is_set())
269✔
1218
        {
1219
            left.d_->stylesheet_.get().parent = &left;
269✔
1220
        }
1221
    }
1222

1223
    if (right.d_ != nullptr)
269✔
1224
    {
1225
        for (auto ws : right)
3✔
1226
        {
1227
            ws.parent(right);
1✔
1228
        }
1229

1230
        if (right.d_->stylesheet_.is_set())
1✔
1231
        {
1232
            right.d_->stylesheet_.get().parent = &right;
1✔
1233
        }
1234
    }
1235
}
269✔
1236

1237
workbook &workbook::operator=(workbook other)
6✔
1238
{
1239
    swap(other);
6✔
1240
    d_->stylesheet_.get().parent = this;
6✔
1241

1242
    return *this;
6✔
1243
}
1244

1245
workbook::workbook(workbook &&other)
3✔
1246
    : workbook(nullptr)
3✔
1247
{
1248
    swap(other);
3✔
1249
}
3✔
1250

1251
workbook::workbook(const workbook &other)
2✔
1252
    : workbook()
2✔
1253
{
1254
    *d_.get() = *other.d_.get();
2✔
1255

1256
    for (auto ws : *this)
10✔
1257
    {
1258
        ws.parent(*this);
4✔
1259
    }
1260

1261
    d_->stylesheet_.get().parent = this;
2✔
1262
}
2✔
1263

1264
workbook::~workbook() = default;
529✔
1265

1266
bool workbook::has_theme() const
1✔
1267
{
1268
    return d_->theme_.is_set();
1✔
1269
}
1270

1271
const theme &workbook::theme() const
×
1272
{
1273
    return d_->theme_.get();
×
1274
}
1275

1276
void workbook::theme(const class theme &value)
358✔
1277
{
1278
    d_->theme_ = value;
358✔
1279
    register_workbook_part(relationship_type::theme);
358✔
1280
}
358✔
1281

1282
std::vector<named_range> workbook::named_ranges() const
87✔
1283
{
1284
    std::vector<xlnt::named_range> named_ranges;
87✔
1285

1286
    for (auto ws : *this)
339✔
1287
    {
1288
        for (auto &ws_named_range : ws.d_->named_ranges_)
126✔
1289
        {
1290
            named_ranges.push_back(ws_named_range.second);
×
1291
        }
1292
    }
1293

1294
    return named_ranges;
87✔
1295
}
×
1296

1297
format workbook::create_format(bool default_format)
10,553✔
1298
{
1299
    register_workbook_part(relationship_type::stylesheet);
10,553✔
1300
    return d_->stylesheet_.get().create_format(default_format);
10,553✔
1301
}
1302

1303
bool workbook::has_style(const std::string &name) const
256✔
1304
{
1305
    return d_->stylesheet_.get().has_style(name);
256✔
1306
}
1307

1308
void workbook::clear_styles()
1✔
1309
{
1310
    apply_to_cells([](cell c) { c.clear_style(); });
2✔
1311
}
1✔
1312

1313
void workbook::default_slicer_style(const std::string &value)
2✔
1314
{
1315
    d_->stylesheet_.get().default_slicer_style = value;
2✔
1316
}
2✔
1317

1318
std::string workbook::default_slicer_style() const
×
1319
{
1320
    return d_->stylesheet_.get().default_slicer_style.get();
×
1321
}
1322

1323
void workbook::enable_known_fonts()
51✔
1324
{
1325
    d_->stylesheet_.get().known_fonts_enabled = true;
51✔
1326
}
51✔
1327

1328
void workbook::disable_known_fonts()
×
1329
{
1330
    d_->stylesheet_.get().known_fonts_enabled = false;
×
1331
}
×
1332

1333
bool workbook::known_fonts_enabled() const
×
1334
{
1335
    return d_->stylesheet_.get().known_fonts_enabled;
×
1336
}
1337

1338
void workbook::clear_formats()
1✔
1339
{
1340
    apply_to_cells([](cell c) { c.clear_format(); });
2✔
1341
}
1✔
1342

1343
void workbook::apply_to_cells(std::function<void(cell)> f)
2✔
1344
{
1345
    for (auto ws : *this)
6✔
1346
    {
1347
        for (auto row = ws.lowest_row(); row <= ws.highest_row(); ++row)
4✔
1348
        {
1349
            for (auto column = ws.lowest_column(); column <= ws.highest_column(); ++column)
6✔
1350
            {
1351
                if (ws.has_cell(cell_reference(column, row)))
2✔
1352
                {
1353
                    f.operator()(ws.cell(cell_reference(column, row)));
2✔
1354
                }
1355
            }
1356
        }
1357
    }
1358
}
2✔
1359

1360
format workbook::format(std::size_t format_index)
10,874✔
1361
{
1362
    return d_->stylesheet_.get().format(format_index);
10,874✔
1363
}
1364

1365
const format workbook::format(std::size_t format_index) const
×
1366
{
1367
    return d_->stylesheet_.get().format(format_index);
×
1368
}
1369

1370
manifest &workbook::manifest()
139,040✔
1371
{
1372
    return d_->manifest_;
139,040✔
1373
}
1374

1375
const manifest &workbook::manifest() const
1,663✔
1376
{
1377
    return d_->manifest_;
1,663✔
1378
}
1379

1380
const rich_text &workbook::shared_strings(std::size_t index) const
10,201✔
1381
{
1382
    if (index < d_->shared_strings_values_.size())
10,201✔
1383
    {
1384
        return d_->shared_strings_values_.at(index);
10,201✔
1385
    }
1386

1387
    static rich_text empty;
×
1388
    return empty;
×
1389
}
1390

1391
std::vector<rich_text> &workbook::shared_strings()
×
1392
{
1393
    return d_->shared_strings_values_;
×
1394
}
1395

1396
const std::vector<rich_text> &workbook::shared_strings() const
108✔
1397
{
1398
    return d_->shared_strings_values_;
108✔
1399
}
1400

1401
std::size_t workbook::add_shared_string(const rich_text &shared, bool allow_duplicates)
31,012✔
1402
{
1403
    register_workbook_part(relationship_type::shared_string_table);
31,012✔
1404

1405
    if (!allow_duplicates)
31,012✔
1406
    {
1407
        auto it = d_->shared_strings_ids_.find(shared);
20,293✔
1408

1409
        if (it != d_->shared_strings_ids_.end())
20,293✔
1410
        {
1411
            return it->second;
9,904✔
1412
        }
1413
    }
1414

1415
    auto sz = d_->shared_strings_ids_.size();
21,108✔
1416
    d_->shared_strings_ids_[shared] = sz;
21,108✔
1417
    d_->shared_strings_values_.push_back(shared);
21,108✔
1418

1419
    return sz;
21,108✔
1420
}
1421

1422
bool workbook::contains(const std::string &sheet_title) const
385✔
1423
{
1424
    for (auto ws : *this)
545✔
1425
    {
1426
        if (ws.title() == sheet_title) return true;
145✔
1427
    }
1428

1429
    return false;
320✔
1430
}
1431

1432
void workbook::thumbnail(const std::vector<std::uint8_t> &thumbnail,
263✔
1433
    const std::string &extension, const std::string &content_type)
1434
{
1435
    if (!d_->manifest_.has_relationship(path("/"), relationship_type::thumbnail))
789✔
1436
    {
1437
        d_->manifest_.register_default_type(extension, content_type);
263✔
1438
        d_->manifest_.register_relationship(uri("/"), relationship_type::thumbnail,
526✔
1439
            uri("docProps/thumbnail.jpeg"), target_mode::internal);
789✔
1440
    }
1441

1442
    auto thumbnail_rel = d_->manifest_.relationship(path("/"), relationship_type::thumbnail);
526✔
1443
    d_->images_[thumbnail_rel.target().to_string()] = thumbnail;
263✔
1444
}
263✔
1445

1446
const std::vector<std::uint8_t> &workbook::thumbnail() const
×
1447
{
1448
    auto thumbnail_rel = d_->manifest_.relationship(path("/"), relationship_type::thumbnail);
×
1449
    return d_->images_.at(thumbnail_rel.target().to_string());
×
1450
}
×
1451

1452
const std::unordered_map<std::string, std::vector<std::uint8_t>> &workbook::binaries() const
×
1453
{
1454
    return d_->binaries_;
×
1455
}
1456

1457
style workbook::create_style(const std::string &name)
17✔
1458
{
1459
    return d_->stylesheet_.get().create_style(name);
17✔
1460
}
1461

1462
style workbook::create_builtin_style(const std::size_t builtin_id)
264✔
1463
{
1464
    return d_->stylesheet_.get().create_builtin_style(builtin_id);
264✔
1465
}
1466

1467
style workbook::style(const std::string &name)
259✔
1468
{
1469
    return d_->stylesheet_.get().style(name);
259✔
1470
}
1471

1472
const style workbook::style(const std::string &name) const
1✔
1473
{
1474
    return d_->stylesheet_.get().style(name);
1✔
1475
}
1476

1477
calendar workbook::base_date() const
101✔
1478
{
1479
    return d_->base_date_;
101✔
1480
}
1481

1482
void workbook::base_date(calendar base_date)
110✔
1483
{
1484
    d_->base_date_ = base_date;
110✔
1485
}
110✔
1486

1487
bool workbook::has_title() const
×
1488
{
1489
    return d_->title_.is_set();
×
1490
}
1491

1492
std::string workbook::title() const
×
1493
{
1494
    return d_->title_.get();
×
1495
}
1496

1497
void workbook::title(const std::string &title)
×
1498
{
1499
    d_->title_ = title;
×
1500
}
×
1501

1502
detail::workbook_impl &workbook::impl()
206✔
1503
{
1504
    return *d_;
206✔
1505
}
1506

1507
const detail::workbook_impl &workbook::impl() const
335✔
1508
{
1509
    return *d_;
335✔
1510
}
1511

1512
bool workbook::has_view() const
190✔
1513
{
1514
    return d_->view_.is_set();
190✔
1515
}
1516

1517
workbook_view workbook::view() const
186✔
1518
{
1519
    if (!d_->view_.is_set())
186✔
1520
    {
1521
        throw invalid_attribute();
×
1522
    }
1523

1524
    return d_->view_.get();
186✔
1525
}
1526

1527
void workbook::view(const workbook_view &view)
361✔
1528
{
1529
    d_->view_ = view;
361✔
1530
}
361✔
1531

1532
bool workbook::has_code_name() const
87✔
1533
{
1534
    return d_->code_name_.is_set();
87✔
1535
}
1536

1537
std::string workbook::code_name() const
×
1538
{
1539
    if (has_code_name())
×
1540
    {
1541
        throw invalid_attribute();
×
1542
    }
1543

1544
    return d_->code_name_.get();
×
1545
}
1546

1547
void workbook::code_name(const std::string &code_name)
×
1548
{
1549
    d_->code_name_ = code_name;
×
1550
}
×
1551

1552
bool workbook::has_file_version() const
87✔
1553
{
1554
    return d_->file_version_.is_set();
87✔
1555
}
1556

1557
std::string workbook::app_name() const
83✔
1558
{
1559
    return d_->file_version_.get().app_name;
83✔
1560
}
1561

1562
std::size_t workbook::last_edited() const
83✔
1563
{
1564
    return d_->file_version_.get().last_edited;
83✔
1565
}
1566

1567
std::size_t workbook::lowest_edited() const
83✔
1568
{
1569
    return d_->file_version_.get().lowest_edited;
83✔
1570
}
1571

1572
std::size_t workbook::rup_build() const
83✔
1573
{
1574
    return d_->file_version_.get().rup_build;
83✔
1575
}
1576

1577
bool workbook::has_calculation_properties() const
87✔
1578
{
1579
    return d_->calculation_properties_.is_set();
87✔
1580
}
1581

1582
class calculation_properties workbook::calculation_properties() const
168✔
1583
{
1584
    return d_->calculation_properties_.get();
168✔
1585
}
1586

1587
void workbook::calculation_properties(const class calculation_properties &props)
359✔
1588
{
1589
    d_->calculation_properties_ = props;
359✔
1590
}
359✔
1591

1592
void workbook::garbage_collect_formulae()
18✔
1593
{
1594
    auto any_with_formula = false;
18✔
1595

1596
    for (auto ws : *this)
36✔
1597
    {
1598
        for (auto row : ws.rows(true))
104✔
1599
        {
1600
            for (auto cell : row)
512✔
1601
            {
1602
                if (cell.has_formula())
426✔
1603
                {
1604
                    any_with_formula = true;
136✔
1605
                }
1606
            }
1607
        }
18✔
1608
    }
1609

1610
    if (any_with_formula) return;
18✔
1611

1612
    auto wb_rel = manifest().relationship(path("/"), relationship_type::office_document);
4✔
1613

1614
    if (manifest().has_relationship(wb_rel.target().path(), relationship_type::calculation_chain))
2✔
1615
    {
1616
        auto calc_chain_rel = manifest().relationship(wb_rel.target().path(), relationship_type::calculation_chain);
2✔
1617
        auto calc_chain_part = manifest().canonicalize({wb_rel, calc_chain_rel});
10✔
1618
        manifest().unregister_override_type(calc_chain_part);
2✔
1619
        manifest().unregister_relationship(wb_rel.target(), calc_chain_rel.id());
2✔
1620
    }
2✔
1621
}
4✔
1622

1623
void workbook::update_sheet_properties()
339✔
1624
{
1625
    if (has_extended_property(xlnt::extended_property::titles_of_parts))
339✔
1626
    {
1627
        extended_property(xlnt::extended_property::titles_of_parts, sheet_titles());
339✔
1628
    }
1629

1630
    if (has_extended_property(xlnt::extended_property::heading_pairs))
339✔
1631
    {
1632
        extended_property(xlnt::extended_property::heading_pairs,
339✔
1633
            std::vector<variant>{variant("Worksheets"), variant(static_cast<int>(sheet_count()))});
1,695✔
1634
    }
1635
}
678✔
1636

1637
namespace {
1638
// true if a sheet index is != worksheet relationship index
1639
bool needs_reorder(const std::unordered_map<std::string, std::string> &title_to_rels,
319✔
1640
    const std::vector<std::string> &titles,
1641
    std::vector<std::string> &relation_ids)
1642
{
1643
    bool all_match = true;
319✔
1644
    for (std::size_t title_index = 0; title_index < titles.size(); ++title_index)
706✔
1645
    {
1646
        const auto &rel = title_to_rels.at(titles[title_index]);
387✔
1647
        relation_ids.push_back(rel);
387✔
1648
        const auto expected_rel_id = "rId" + std::to_string(title_index + 1);
387✔
1649
        if (rel != expected_rel_id)
387✔
1650
        {
1651
            all_match = false;
57✔
1652
        }
1653
    }
387✔
1654
    return !all_match; // if all are as expected, reorder not required
319✔
1655
};
1656

1657
struct rel_id_sorter
1658
{
1659
    // true if lhs < rhs
1660
    bool operator()(const xlnt::relationship &lhs, const xlnt::relationship &rhs)
325✔
1661
    {
1662
        // format is rTd<decimal number 1..n>
1663
        if (lhs.id().size() != rhs.id().size()) // a number with more digits will be larger
325✔
1664
        {
1665
            return lhs.id().size() < rhs.id().size();
×
1666
        }
1667
        return lhs.id() < rhs.id();
325✔
1668
    }
1669
};
1670
} // namespace
1671

1672
void workbook::reorder_relationships()
319✔
1673
{
1674
    const auto titles = sheet_titles();
319✔
1675
    // the relation ID corresponding to the title at the same index is copied into here
1676
    std::vector<std::string> worksheet_rel_ids;
319✔
1677
    worksheet_rel_ids.reserve(titles.size());
319✔
1678
    if (!needs_reorder(d_->sheet_title_rel_id_map_, titles, worksheet_rel_ids))
319✔
1679
    {
1680
        return;
263✔
1681
    }
1682
    // copy of existing relations
1683
    const auto wb_rel_target = manifest().relationship(path("/"), relationship_type::office_document).target();
112✔
1684
    auto rel_copy = manifest().relationships(wb_rel_target.path());
56✔
1685
    std::sort(rel_copy.begin(), rel_copy.end(), rel_id_sorter{});
56✔
1686
    // clear existing relations
1687
    for (const auto &rel : rel_copy)
295✔
1688
    {
1689
        manifest().unregister_relationship(wb_rel_target, rel.id());
239✔
1690
    }
1691
    // create new relations
1692
    std::size_t index = 0;
56✔
1693
    auto new_id = [&index]() { return "rId" + std::to_string(++index); }; // ids start from 1
239✔
1694
    // worksheets first
1695
    while (index < worksheet_rel_ids.size())
180✔
1696
    {
1697
        auto rel_it = std::find_if(rel_copy.begin(), rel_copy.end(),
124✔
1698
            [&](const relationship &rel) { return rel.id() == worksheet_rel_ids[index]; });
323✔
1699

1700
        std::string rel_id = new_id();
124✔
1701
        d_->sheet_title_rel_id_map_.at(titles[index - 1]) = rel_id; // update title -> relation mapping
124✔
1702
        manifest().register_relationship(relationship(rel_id, rel_it->type(),
248✔
1703
            rel_it->source(), rel_it->target(), rel_it->target_mode()));
124✔
1704
    }
124✔
1705
    // then all the other relations in the same order they started (just new indices)
1706
    for (const auto &old_rel : rel_copy)
295✔
1707
    {
1708
        if (old_rel.type() == relationship_type::worksheet)
239✔
1709
        {
1710
            continue;
124✔
1711
        }
1712
        manifest().register_relationship(relationship(new_id(), old_rel.type(),
230✔
1713
            old_rel.source(), old_rel.target(), old_rel.target_mode()));
115✔
1714
    }
1715
}
582✔
1716

1717
} // namespace xlnt
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc