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

xlnt-community / xlnt / 7ff9c3f5-3924-4201-9285-53486e877245

01 May 2025 12:06PM UTC coverage: 81.886% (-0.4%) from 82.27%
7ff9c3f5-3924-4201-9285-53486e877245

push

circleci

web-flow
Publishing coverage to github pages (#79)

Alternative to coveralls, to overcome some disadvantages/inconveniences of this external service, i.e:
 - loading file details sometimes fails due to gateway timeout
 - for forked pull requests:
   - github comment is not posted on PR
   - coverage report not compared with master branch

HTML reports are generated based on the fully open source genhtml (lcov) tool.

Disabled samples and benchmarks while generating coverage report, as coverage report should be based on real unit tests. This slightly decreases the coverage. Some extra unit tests added to overcome the most important coverage losses.

Enabled branch coverage, in addition to the existing line coverage. Disabled coverage reporting for exception branches (for now) as it generates too many (less important) compiler generated branches. 
For more information about exception branches, see https://github.com/gcovr/gcovr/issues/431#issuecomment-705518206

14026 of 18604 branches covered (75.39%)

1 of 1 new or added line in 1 file covered. (100.0%)

58 existing lines in 6 files now uncovered.

11500 of 14044 relevant lines covered (81.89%)

10385.36 hits per line

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

76.08
./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 <fstream>
28
#include <functional>
29

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

64
namespace {
65

66
using xlnt::detail::open_stream;
67

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

74
    while (iter != container.end())
631✔
75
    {
76
        result.push_back((iter++)->first);
551✔
77
    }
78

79
    return result;
80✔
80
}
×
81

82
template <typename T>
83
bool contains(const std::vector<std::pair<T, xlnt::variant>> &container, const T key)
658✔
84
{
85
    for (const auto &iter : container)
2,931✔
86
    {
87
        if (iter.first == key)
2,927✔
88
        {
89
            return true;
654✔
90
        }
91
    }
92

93
    return false;
4✔
94
}
95

96
xlnt::path default_path(xlnt::relationship_type type, std::size_t index = 0)
2,530✔
97
{
98
    using xlnt::path;
99
    using xlnt::relationship_type;
100

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

175
    default_case(path("/xl/unknownPart.xml"));
×
176
}
177

178
std::string content_type(xlnt::relationship_type type)
1,265✔
179
{
180
    using xlnt::relationship_type;
181

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

256
    default_case("application/xml");
×
257
}
258

259
} // namespace
260

261
namespace xlnt {
262

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

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

273
variant workbook::core_property(xlnt::core_property type) const
171✔
274
{
275
    for (auto iter : d_->core_properties_)
523!
276
    {
277
        if (iter.first == type)
523✔
278
        {
279
            return iter.second;
342✔
280
        }
281
    }
523✔
282

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

286
void workbook::core_property(xlnt::core_property type, const variant &value)
1,376✔
287
{
288
    register_package_part(relationship_type::core_properties);
1,376✔
289

290
    for (auto &iter : d_->core_properties_)
3,837✔
291
    {
292
        if (iter.first == type)
2,467✔
293
        {
294
            iter.second = value;
6✔
295
            return;
6✔
296
        }
297
    }
298

299
    d_->core_properties_.push_back({type, value});
1,370✔
300
}
301

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

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

312
void workbook::extended_property(xlnt::extended_property type, const variant &value)
3,744✔
313
{
314
    register_package_part(relationship_type::extended_properties);
3,744✔
315

316
    for (auto &iter : d_->extended_properties_)
19,965✔
317
    {
318
        if (iter.first == type)
16,837✔
319
        {
320
            iter.second = value;
616✔
321
            return;
616✔
322
        }
323
    }
324

325
    d_->extended_properties_.push_back({type, value});
3,128✔
326
}
327

328
variant workbook::extended_property(xlnt::extended_property type) const
378✔
329
{
330
    for (auto iter : d_->extended_properties_)
2,103!
331
    {
332
        if (iter.first == type)
2,103✔
333
        {
334
            return iter.second;
756✔
335
        }
336
    }
2,103✔
337

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

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

346
std::vector<std::string> workbook::custom_properties() const
2✔
347
{
348
    return keys(d_->custom_properties_);
2✔
349
}
350

351
void workbook::custom_property(const std::string &property_name, const variant &value)
4✔
352
{
353
    register_package_part(relationship_type::custom_properties);
4✔
354

355
    for (auto &iter : d_->custom_properties_)
4!
356
    {
UNCOV
357
        if (iter.first == property_name)
×
358
        {
359
            iter.second = value;
×
360
            return;
×
361
        }
362
    }
363

364
    d_->custom_properties_.push_back({property_name, value});
4✔
365
}
366

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

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

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

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

390
workbook workbook::empty()
243✔
391
{
392
    workbook wb(std::make_shared<detail::workbook_impl>());
243✔
393

394
    wb.register_package_part(relationship_type::office_document);
243✔
395

396
    wb.d_->manifest_.register_default_type("rels",
972✔
397
        "application/vnd.openxmlformats-package.relationships+xml");
398
    wb.d_->manifest_.register_default_type("xml",
1,215✔
399
        "application/xml");
400

401
    wb.thumbnail(excel_thumbnail(), "jpeg", "image/jpeg");
729✔
402

403
    wb.core_property(xlnt::core_property::creator, "Microsoft Office User");
243✔
404
    wb.core_property(xlnt::core_property::last_modified_by, "Microsoft Office User");
243✔
405
    wb.core_property(xlnt::core_property::created, datetime(2016, 8, 12, 3, 16, 56));
243✔
406
    wb.core_property(xlnt::core_property::modified, datetime(2016, 8, 12, 3, 17, 16));
243✔
407

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

419
    detail::workbook_impl::file_version_t file_version;
243✔
420
    file_version.app_name = "xl";
243✔
421
    file_version.last_edited = 6;
243✔
422
    file_version.lowest_edited = 6;
243✔
423
    file_version.rup_build = 26709;
243✔
424
    wb.d_->file_version_ = file_version;
243✔
425

426
    xlnt::workbook_view wb_view;
243✔
427
    wb_view.active_tab = 0;
243✔
428
    wb_view.x_window = 0;
243✔
429
    wb_view.y_window = 460;
243✔
430
    wb_view.window_width = 28800;
243✔
431
    wb_view.window_height = 17460;
243✔
432
    wb_view.tab_ratio = 500;
243✔
433
    wb.view(wb_view);
243✔
434

435
    auto ws = wb.create_sheet();
243✔
436

437
    page_margins margins;
243✔
438
    margins.left(0.7);
243✔
439
    margins.right(0.7);
243✔
440
    margins.top(0.75);
243✔
441
    margins.bottom(0.75);
243✔
442
    margins.header(0.3);
243✔
443
    margins.footer(0.3);
243✔
444
    ws.page_margins(margins);
243✔
445

446
    sheet_view view;
243✔
447
    ws.add_view(view);
243✔
448

449
    auto &format_properties = ws.d_->format_properties_;
243✔
450
    format_properties.base_col_width = 10.0;
243✔
451
    format_properties.default_row_height = 16.0;
243✔
452

453
    wb.theme(xlnt::theme());
243✔
454

455
    wb.d_->stylesheet_ = detail::stylesheet();
243✔
456
    auto &stylesheet = wb.d_->stylesheet_.get();
243✔
457
    stylesheet.parent = wb.d_;
243✔
458

459
    auto default_border = border()
243✔
460
                              .side(border_side::bottom, border::border_property())
486✔
461
                              .side(border_side::top, border::border_property())
486✔
462
                              .side(border_side::start, border::border_property())
486✔
463
                              .side(border_side::end, border::border_property())
486✔
464
                              .side(border_side::diagonal, border::border_property());
243✔
465
    wb.d_->stylesheet_.get().borders.push_back(default_border);
243✔
466

467
    auto default_fill = fill(pattern_fill()
486✔
468
                                 .type(pattern_fill_type::none));
243✔
469
    stylesheet.fills.push_back(default_fill);
243✔
470
    auto gray125_fill = pattern_fill()
486✔
471
                            .type(pattern_fill_type::gray125);
243✔
472
    stylesheet.fills.push_back(gray125_fill);
243✔
473

474
    auto default_font = font()
243✔
475
                            .name("Calibri")
486✔
476
                            .size(12)
243✔
477
                            .scheme("minor")
486✔
478
                            .family(2)
243✔
479
                            .color(theme_color(1));
243✔
480
    stylesheet.fonts.push_back(default_font);
243✔
481

482
    wb.create_builtin_style(0)
243✔
483
        .border(default_border)
243✔
484
        .fill(default_fill)
486✔
485
        .font(default_font)
486✔
486
        .number_format(xlnt::number_format::general());
243✔
487

488
    wb.create_format(true)
243✔
489
        .border(default_border)
243✔
490
        .fill(default_fill)
486✔
491
        .font(default_font)
486✔
492
        .number_format(xlnt::number_format::general())
486✔
493
        .style("Normal");
486✔
494

495
    xlnt::calculation_properties calc_props;
243✔
496
    calc_props.calc_id = 150000;
243✔
497
    calc_props.concurrent_calc = false;
243✔
498
    wb.calculation_properties(calc_props);
243✔
499

500
    return wb;
243✔
501
}
486!
502

503
workbook::workbook()
236✔
504
{
505
    auto wb_template = empty();
236✔
506
    swap(wb_template);
236✔
507
}
236✔
508

509
workbook::workbook(const xlnt::path &file)
2✔
510
{
511
    *this = empty();
2✔
512
    load(file);
2✔
513
}
2✔
514

515
template <typename T>
516
void workbook::construct(const xlnt::path &file, const T &password)
3✔
517
{
518
    *this = empty();
3✔
519
    load(file, password);
3✔
520
}
2✔
521

522
workbook::workbook(const xlnt::path &file, const std::string &password)
1✔
523
{
524
    construct(file, password);
1!
525
}
1✔
526

527
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
528
workbook::workbook(const xlnt::path &file, std::u8string_view password)
2✔
529
{
530
    construct(file, password);
2✔
531
}
2✔
532
#endif
533

534
workbook::workbook(std::istream &data)
1✔
535
{
536
    *this = empty();
1✔
537
    load(data);
1✔
538
}
1✔
539

540
template <typename T>
541
void workbook::construct(std::istream &data, const T &password)
1✔
542
{
543
    *this = empty();
1✔
544
    load(data, password);
1✔
545
}
1✔
546

547
workbook::workbook(std::istream &data, const std::string &password)
×
548
{
549
    construct(data, password);
×
550
}
×
551

552
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
553
workbook::workbook(std::istream &data, std::u8string_view password)
1✔
554
{
555
    construct(data, password);
1✔
556
}
1✔
557
#endif
558

559
workbook::workbook(std::shared_ptr<detail::workbook_impl> impl)
244✔
560
{
561
    set_impl(std::move(impl));
244✔
562
}
244✔
563

564
workbook::workbook(std::weak_ptr<detail::workbook_impl> impl)
841✔
565
{
566
    set_impl(impl.lock());
841✔
567
}
841✔
568

569
void workbook::set_impl(std::shared_ptr<detail::workbook_impl> impl)
1,085✔
570
{
571
    if (impl == nullptr)
1,085!
572
    {
573
        throw xlnt::invalid_parameter("invalid workbook pointer");
×
574
    }
575

576
    d_ = std::move(impl);
1,085✔
577
}
1,085✔
578

579
void workbook::register_package_part(relationship_type type)
5,367✔
580
{
581
    if (!manifest().has_relationship(path("/"), type))
16,101✔
582
    {
583
        manifest().register_override_type(default_path(type), content_type(type));
729✔
584
        manifest().register_relationship(uri("/"), type,
1,458✔
585
            uri(default_path(type).relative_to(path("/")).string()),
2,916✔
586
            target_mode::internal);
587
    }
588
}
5,367✔
589

590
void workbook::register_workbook_part(relationship_type type)
1,626✔
591
{
592
    auto wb_rel = manifest().relationship(path("/"), relationship_type::office_document);
3,252✔
593
    auto wb_path = manifest().canonicalize({wb_rel});
6,504!
594

595
    if (!manifest().has_relationship(wb_path, type))
1,626✔
596
    {
597
        manifest().register_override_type(default_path(type), content_type(type));
536✔
598
        manifest().register_relationship(uri(wb_path.string()), type,
1,072✔
599
            uri(default_path(type).relative_to(wb_path.resolve(path("/"))).string()),
2,144✔
600
            target_mode::internal);
601
    }
602
}
3,252!
603

604
void workbook::register_worksheet_part(worksheet ws, relationship_type type)
34✔
605
{
606
    auto wb_rel = manifest().relationship(path("/"),
102✔
607
        relationship_type::office_document);
34✔
608
    auto ws_rel = manifest().relationship(wb_rel.target().path(),
34✔
609
        d_->sheet_title_rel_id_map_.at(ws.title()));
34✔
610
    path ws_path(ws_rel.source().path().parent().append(ws_rel.target().path()));
34✔
611

612
    if (type == relationship_type::comments)
34!
613
    {
614
        if (!manifest().has_relationship(ws_path, relationship_type::vml_drawing))
34✔
615
        {
616
            std::size_t file_number = 1;
3✔
617
            path filename("vmlDrawing1.vml");
3✔
618
            bool filename_exists = true;
3✔
619

620
            while (filename_exists)
7✔
621
            {
622
                filename_exists = false;
4✔
623

624
                for (auto current_ws_rel :
4✔
625
                    manifest().relationships(wb_rel.target().path(), xlnt::relationship_type::worksheet))
14✔
626
                {
627
                    path current_ws_path(current_ws_rel.source().path().parent().append(current_ws_rel.target().path()));
6✔
628
                    if (!manifest().has_relationship(current_ws_path, xlnt::relationship_type::vml_drawing)) continue;
6✔
629

630
                    for (auto current_ws_child_rel :
2✔
631
                        manifest().relationships(current_ws_path, xlnt::relationship_type::vml_drawing))
5✔
632
                    {
633
                        if (current_ws_child_rel.target().path() == path("../drawings").append(filename))
4✔
634
                        {
635
                            filename_exists = true;
1✔
636
                            break;
1✔
637
                        }
638
                    }
3✔
639
                }
14✔
640

641
                if (filename_exists)
4✔
642
                {
643
                    file_number++;
1✔
644
                    filename = path("vmlDrawing" + std::to_string(file_number) + ".vml");
1✔
645
                }
646
            }
647

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

650
            const path relative_path(path("../drawings").append(filename));
3✔
651
            manifest().register_relationship(
6✔
652
                uri(ws_path.string()), relationship_type::vml_drawing, uri(relative_path.string()), target_mode::internal);
6✔
653
        }
3✔
654

655
        if (!manifest().has_relationship(ws_path, relationship_type::comments))
34✔
656
        {
657
            std::size_t file_number = 1;
3✔
658
            path filename("comments1.xml");
6✔
659

660
            while (true)
661
            {
662
                if (!manifest().has_override_type(constants::package_xl().append(filename))) break;
4✔
663

664
                file_number++;
1✔
665
                filename = path("comments" + std::to_string(file_number) + ".xml");
1✔
666
            }
667

668
            const path absolute_path(constants::package_xl().append(filename));
3✔
669
            manifest().register_override_type(
9✔
670
                absolute_path, "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml");
671

672
            const path relative_path(path("..").append(filename));
3✔
673
            manifest().register_relationship(
6✔
674
                uri(ws_path.string()), relationship_type::comments, uri(relative_path.string()), target_mode::internal);
6✔
675
        }
3✔
676
    }
677
}
34✔
678

679
const worksheet workbook::sheet_by_title(const std::string &title) const
53✔
680
{
681
    for (auto &impl : d_->worksheets_)
67✔
682
    {
683
        if (impl.title_ == title)
66✔
684
        {
685
            return worksheet(&impl);
52✔
686
        }
687
    }
688

689
    throw key_not_found();
1✔
690
}
691

692
worksheet workbook::sheet_by_title(const std::string &title)
25✔
693
{
694
    for (auto &impl : d_->worksheets_)
48✔
695
    {
696
        if (impl.title_ == title)
43✔
697
        {
698
            return worksheet(&impl);
20✔
699
        }
700
    }
701

702
    throw key_not_found();
5✔
703
}
704

705
worksheet workbook::sheet_by_index(std::size_t index)
293✔
706
{
707
    if (index >= d_->worksheets_.size())
293✔
708
    {
709
        throw invalid_parameter();
1✔
710
    }
711

712
    auto iter = d_->worksheets_.begin();
292✔
713

714
    for (std::size_t i = 0; i < index; ++i)
368✔
715
    {
716
        ++iter;
76✔
717
    }
718

719
    return worksheet(&*iter);
584✔
720
}
721

722
const worksheet workbook::sheet_by_index(std::size_t index) const
1,075✔
723
{
724
    if (index >= d_->worksheets_.size())
1,075✔
725
    {
726
        throw invalid_parameter();
1✔
727
    }
728

729
    auto iter = d_->worksheets_.begin();
1,074✔
730

731
    for (std::size_t i = 0; i < index; ++i, ++iter)
1,342✔
732
    {
733
    }
734

735
    return worksheet(&*iter);
2,148✔
736
}
737

738
worksheet workbook::sheet_by_id(std::size_t id)
×
739
{
740
    for (auto &impl : d_->worksheets_)
×
741
    {
742
        if (impl.id_ == id)
×
743
        {
744
            return worksheet(&impl);
×
745
        }
746
    }
747

748
    throw key_not_found();
×
749
}
750

751
const worksheet workbook::sheet_by_id(std::size_t id) const
×
752
{
753
    for (auto &impl : d_->worksheets_)
×
754
    {
755
        if (impl.id_ == id)
×
756
        {
757
            return worksheet(&impl);
×
758
        }
759
    }
760

761
    throw key_not_found();
×
762
}
763

764
bool workbook::sheet_hidden_by_index(std::size_t index) const
1✔
765
{
766
    if (index >= d_->sheet_hidden_.size())
1!
767
    {
768
        throw invalid_parameter();
×
769
    }
770

771
    return d_->sheet_hidden_.at(index);
1✔
772
}
773

774
worksheet workbook::active_sheet()
151✔
775
{
776
    return sheet_by_index(d_->active_sheet_index_.is_set() ? d_->active_sheet_index_.get() : 0);
151✔
777
}
778
void workbook::active_sheet(std::size_t index)
1✔
779
{
780
    d_->active_sheet_index_.set(index);
1✔
781
    d_->view_.get().active_tab = index;
1✔
782
}
1✔
783

784
bool workbook::has_named_range(const std::string &name) const
11✔
785
{
786
    for (auto worksheet : *this)
27✔
787
    {
788
        if (worksheet.has_named_range(name))
17✔
789
        {
790
            return true;
9✔
791
        }
792
    }
793
    return false;
2✔
794
}
795

796
worksheet workbook::create_sheet()
282✔
797
{
798
    std::string title = "Sheet1";
282✔
799
    int index = 1;
282✔
800

801
    // make a unique sheet name. Sheet<1...n>
802
    while (contains(title))
329✔
803
    {
804
        title = "Sheet" + std::to_string(++index);
47✔
805
    }
806
    // unique sheet id
807
    size_t sheet_id = 1;
282✔
808
    for (const auto ws : *this)
333✔
809
    {
810
        sheet_id = std::max(sheet_id, ws.id() + 1);
51✔
811
    }
812
    d_->worksheets_.push_back(detail::worksheet_impl(this, sheet_id, title));
282✔
813
    // unique sheet file name
814
    auto workbook_rel = d_->manifest_.relationship(path("/"), relationship_type::office_document);
564✔
815
    auto workbook_files = d_->manifest_.relationships(workbook_rel.target().path());
282✔
816
    auto rel_vec_contains = [&workbook_files](const xlnt::path &new_file_id) {
283✔
817
        return workbook_files.end() != std::find_if(workbook_files.begin(), workbook_files.end(), [&new_file_id](const xlnt::relationship &rel) {
566✔
818
            return rel.target().path() == new_file_id;
133✔
819
        });
566✔
820
    };
282✔
821

822
    size_t file_id = sheet_id;
282✔
823
    xlnt::path sheet_relative_path;
282✔
824
    do
825
    {
826
        sheet_relative_path = path("worksheets").append("sheet" + std::to_string(file_id++) + ".xml");
283✔
827
    } while (rel_vec_contains(sheet_relative_path));
283✔
828

829
    uri relative_sheet_uri(sheet_relative_path.string());
282✔
830
    auto absolute_sheet_path = path("/xl").append(relative_sheet_uri.path());
282✔
831
    d_->manifest_.register_override_type(
564✔
832
        absolute_sheet_path, "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
833
    auto ws_rel = d_->manifest_.register_relationship(
282✔
834
        workbook_rel.target(), relationship_type::worksheet, relative_sheet_uri, target_mode::internal);
282✔
835
    d_->sheet_title_rel_id_map_[title] = ws_rel;
282✔
836

837
    update_sheet_properties();
282✔
838
    reorder_relationships();
282✔
839

840
    return worksheet(&d_->worksheets_.back());
564✔
841
}
282✔
842

843
worksheet workbook::copy_sheet(worksheet to_copy)
3✔
844
{
845
    if (to_copy.d_->parent_.lock() != d_) throw invalid_parameter();
3✔
846

847
    detail::worksheet_impl impl(*to_copy.d_);
2✔
848
    auto new_sheet = create_sheet();
2✔
849
    impl.title_ = new_sheet.title();
2✔
850
    impl.id_ = new_sheet.id();
2✔
851
    *new_sheet.d_ = impl;
2✔
852

853
    return new_sheet;
4✔
854
}
2✔
855

856
worksheet workbook::copy_sheet(worksheet to_copy, std::size_t index)
1✔
857
{
858
    copy_sheet(to_copy);
1✔
859

860
    if (index != d_->worksheets_.size() - 1)
1!
861
    {
862
        auto iter = d_->worksheets_.begin();
1✔
863

864
        for (std::size_t i = 0; i < index; ++i, ++iter)
1!
865
        {
866
        }
867

868
        d_->worksheets_.insert(iter, d_->worksheets_.back());
1✔
869
        d_->worksheets_.pop_back();
1✔
870
    }
871

872
    return sheet_by_index(index);
1✔
873
}
874

875
std::size_t workbook::index(worksheet ws) const
3✔
876
{
877
    auto match = std::find(begin(), end(), ws);
3✔
878

879
    if (match == end())
3✔
880
    {
881
        throw invalid_parameter();
1✔
882
    }
883

884
    return static_cast<std::size_t>(std::distance(begin(), match));
6✔
885
}
886

887
void workbook::create_named_range(const std::string &name, worksheet range_owner, const std::string &reference_string)
7✔
888
{
889
    create_named_range(name, range_owner, range_reference(reference_string));
7✔
890
}
6✔
891

892
void workbook::create_named_range(const std::string &name, worksheet range_owner, const range_reference &reference)
7✔
893
{
894
    sheet_by_title(range_owner.title()).create_named_range(name, reference);
8✔
895
}
6✔
896

897
void workbook::remove_named_range(const std::string &name)
2✔
898
{
899
    for (auto ws : *this)
8✔
900
    {
901
        if (ws.has_named_range(name))
4✔
902
        {
903
            ws.remove_named_range(name);
1✔
904
            return;
1✔
905
        }
906
    }
907

908
    throw key_not_found();
1✔
909
}
910

911
range workbook::named_range(const std::string &name)
2✔
912
{
913
    for (auto ws : *this)
8✔
914
    {
915
        if (ws.has_named_range(name))
4✔
916
        {
917
            return ws.named_range(name);
1✔
918
        }
919
    }
920

921
    throw key_not_found();
1✔
922
}
923

924
void workbook::load(std::istream &stream)
74✔
925
{
926
    clear();
74✔
927
    detail::xlsx_consumer consumer(*this);
74✔
928

929
    try
930
    {
931
        consumer.read(stream);
74✔
932
    }
933
    catch (xlnt::exception &e)
1!
934
    {
935
        if (e.what() == std::string("xlnt::exception : encrypted xlsx, password required"))
3!
936
        {
937
            stream.seekg(0, std::ios::beg);
×
938
            consumer.read(stream, "VelvetSweatshop");
×
939
        }
940
        else
941
        {
942
            throw;
1✔
943
        }
944
    }
1!
945
}
74✔
946

947
void workbook::load(const std::vector<std::uint8_t> &data)
23✔
948
{
949
    if (data.size() < 22) // the shortest ZIP file is 22 bytes
23!
950
    {
951
        throw xlnt::exception("file is empty or malformed");
×
952
    }
953

954
    xlnt::detail::vector_istreambuf data_buffer(data);
23✔
955
    std::istream data_stream(&data_buffer);
23✔
956
    load(data_stream);
23✔
957
}
23✔
958

959
template <typename T>
960
void workbook::load_internal(const T &filename)
11✔
961
{
962
    return load(path(filename));
11✔
963
}
964

965
template <typename T>
966
void workbook::load_internal(const T &filename, const T &password)
2✔
967
{
968
    return load(path(filename), password);
2✔
969
}
970

971
void workbook::load(const std::string &filename)
10✔
972
{
973
    return load_internal(filename);
10✔
974
}
975

976
void workbook::load(const path &filename)
49✔
977
{
978
    std::ifstream file_stream;
49✔
979
    open_stream(file_stream, filename.string());
49✔
980

981
    if (!file_stream.good())
49!
982
    {
983
        throw xlnt::exception("file not found " + filename.string());
×
984
    }
985

986
    load(file_stream);
49✔
987
}
49✔
988

989
void workbook::load(const std::string &filename, const std::string &password)
×
990
{
991
    return load_internal(filename, password);
×
992
}
993

994
template <typename T>
995
void workbook::load_internal(const xlnt::path &filename, const T &password)
14✔
996
{
997
    std::ifstream file_stream;
14✔
998
    open_stream(file_stream, filename.string());
14✔
999

1000
    if (!file_stream.good())
14!
1001
    {
1002
        throw xlnt::exception("file not found " + filename.string());
×
1003
    }
1004

1005
    return load(file_stream, password);
23✔
1006
}
14✔
1007

1008
void workbook::load(const path &filename, const std::string &password)
8✔
1009
{
1010
    load_internal(filename, password);
8✔
1011
}
3✔
1012

1013
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
1014
void workbook::load(const xlnt::path &filename, std::u8string_view password)
6✔
1015
{
1016
    load_internal(filename, password);
6✔
1017
}
6✔
1018
#endif
1019

1020
template <typename T>
1021
void workbook::load_internal(const std::vector<std::uint8_t> &data, const T &password)
5✔
1022
{
1023
    if (data.size() < 22) // the shortest ZIP file is 22 bytes
5!
1024
    {
1025
        throw xlnt::exception("file is empty or malformed");
×
1026
    }
1027

1028
    xlnt::detail::vector_istreambuf data_buffer(data);
5✔
1029
    std::istream data_stream(&data_buffer);
5✔
1030
    load(data_stream, password);
5✔
1031
}
5✔
1032

1033
void workbook::load(const std::vector<std::uint8_t> &data, const std::string &password)
3✔
1034
{
1035
    load_internal(data, password);
3✔
1036
}
3✔
1037

1038
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
1039
void workbook::load(const std::vector<std::uint8_t> &data, std::u8string_view password)
2✔
1040
{
1041
    load_internal(data, password);
2✔
1042
}
2✔
1043
#endif
1044

1045
template <typename T>
1046
void workbook::load_internal(std::istream &stream, const T &password)
21✔
1047
{
1048
    clear();
21✔
1049
    detail::xlsx_consumer consumer(*this);
21✔
1050
    consumer.read(stream, password);
21✔
1051
}
21✔
1052

1053
void workbook::load(std::istream &stream, const std::string &password)
11✔
1054
{
1055
    load_internal(stream, password);
11✔
1056
}
6✔
1057

1058
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
1059
void workbook::load(std::istream &stream, std::u8string_view password)
10✔
1060
{
1061
    load_internal(stream, password);
10✔
1062
}
10✔
1063
#endif
1064

1065
void workbook::save(std::vector<std::uint8_t> &data) const
13✔
1066
{
1067
    xlnt::detail::vector_ostreambuf data_buffer(data);
13✔
1068
    std::ostream data_stream(&data_buffer);
13✔
1069
    save(data_stream);
13✔
1070
}
13✔
1071

1072
template <typename T>
1073
void workbook::save_internal(std::vector<std::uint8_t> &data, const T &password) const
×
1074
{
1075
    xlnt::detail::vector_ostreambuf data_buffer(data);
×
1076
    std::ostream data_stream(&data_buffer);
×
1077
    save(data_stream, password);
×
1078
}
×
1079

1080
void workbook::save(std::vector<std::uint8_t> &data, const std::string &password) const
×
1081
{
1082
    save_internal(data, password);
×
1083
}
×
1084

1085
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
1086
void workbook::save(std::vector<std::uint8_t> &data, std::u8string_view password) const
×
1087
{
1088
    save_internal(data, password);
×
1089
}
×
1090
#endif
1091

1092
template <typename T>
1093
void workbook::save_internal(const T &filename) const
24✔
1094
{
1095
    save(path(filename));
24✔
1096
}
24✔
1097

1098
template <typename T>
1099
void workbook::save_internal(const T &filename, const T &password) const
×
1100
{
1101
    save(path(filename), password);
×
1102
}
×
1103

1104
void workbook::save(const std::string &filename) const
23✔
1105
{
1106
    save_internal(filename);
23✔
1107
}
23✔
1108

1109
void workbook::save(const std::string &filename, const std::string &password) const
×
1110
{
1111
    save_internal(filename, password);
×
1112
}
×
1113

1114
void workbook::save(const path &filename) const
24✔
1115
{
1116
    std::ofstream file_stream;
24✔
1117
    open_stream(file_stream, filename.string());
24✔
1118
    save(file_stream);
24✔
1119
}
24✔
1120

1121
template <typename T>
1122
void workbook::save_internal(const xlnt::path &filename, const T &password) const
4✔
1123
{
1124
    std::ofstream file_stream;
4✔
1125
    open_stream(file_stream, filename.string());
4✔
1126
    save(file_stream, password);
4✔
1127
}
4✔
1128

1129
void workbook::save(const path &filename, const std::string &password) const
3✔
1130
{
1131
    save_internal(filename, password);
3✔
1132
}
3✔
1133

1134
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
1135
void workbook::save(const xlnt::path &filename, std::u8string_view password) const
1✔
1136
{
1137
    save_internal(filename, password);
1✔
1138
}
1✔
1139
#endif
1140

1141
void workbook::save(std::ostream &stream) const
37✔
1142
{
1143
    detail::xlsx_producer producer(*this);
37✔
1144
    producer.write(stream);
37✔
1145
}
37✔
1146

1147
template <typename T>
1148
void workbook::save_internal(std::ostream &stream, const T &password) const
4✔
1149
{
1150
    detail::xlsx_producer producer(*this);
4✔
1151
    producer.write(stream, password);
4✔
1152
}
4✔
1153

1154
void workbook::save(std::ostream &stream, const std::string &password) const
3✔
1155
{
1156
    save_internal(stream, password);
3✔
1157
}
3✔
1158

1159
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
1160
void workbook::save(std::ostream &stream, std::u8string_view password) const
1✔
1161
{
1162
    save_internal(stream, password);
1✔
1163
}
1✔
1164
#endif
1165

1166
#ifdef _MSC_VER
1167
void workbook::save(const std::wstring &filename) const
1168
{
1169
    std::ofstream file_stream;
1170
    open_stream(file_stream, filename);
1171
    save(file_stream);
1172
}
1173

1174
void workbook::save(const std::wstring &filename, const std::string &password) const
1175
{
1176
    std::ofstream file_stream;
1177
    open_stream(file_stream, filename);
1178
    save(file_stream, password);
1179
}
1180

1181
void workbook::load(const std::wstring &filename)
1182
{
1183
    std::ifstream file_stream;
1184
    open_stream(file_stream, filename);
1185
    load(file_stream);
1186
}
1187

1188
void workbook::load(const std::wstring &filename, const std::string &password)
1189
{
1190
    std::ifstream file_stream;
1191
    open_stream(file_stream, filename);
1192
    load(file_stream, password);
1193
}
1194
#endif
1195

1196
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
1197
void workbook::save(std::u8string_view filename) const
1✔
1198
{
1199
    save_internal(filename);
1✔
1200
}
1✔
1201

1202
void workbook::save(std::u8string_view filename, std::u8string_view password) const
×
1203
{
1204
    save_internal(filename, password);
×
1205
}
×
1206

1207
void workbook::load(std::u8string_view filename)
1✔
1208
{
1209
    load_internal(filename);
1✔
1210
}
1✔
1211

1212
void workbook::load(std::u8string_view filename, std::u8string_view password)
2✔
1213
{
1214
    load_internal(filename, password);
2✔
1215
}
2✔
1216
#endif
1217

1218
void workbook::remove_sheet(worksheet ws)
8✔
1219
{
1220
    auto match_iter = std::find_if(d_->worksheets_.begin(), d_->worksheets_.end(),
8✔
1221
        [=](detail::worksheet_impl &comp) { return &comp == ws.d_; });
23✔
1222

1223
    if (match_iter == d_->worksheets_.end())
8!
1224
    {
1225
        throw invalid_parameter();
×
1226
    }
1227

1228
    auto ws_rel_id = d_->sheet_title_rel_id_map_.at(ws.title());
8✔
1229
    auto wb_rel = d_->manifest_.relationship(path("/"), xlnt::relationship_type::office_document);
16✔
1230
    auto ws_rel = d_->manifest_.relationship(wb_rel.target().path(), ws_rel_id);
8✔
1231
    auto ws_part = d_->manifest_.canonicalize({wb_rel, ws_rel}).resolve(path("/"));
56!
1232
    d_->manifest_.unregister_override_type(ws_part);
8✔
1233
    auto rel_id_map = d_->manifest_.unregister_relationship(wb_rel.target(), ws_rel_id);
8✔
1234
    d_->sheet_title_rel_id_map_.erase(ws.title());
8✔
1235
    d_->worksheets_.erase(match_iter);
8✔
1236

1237
    // Shift sheet title->ID mappings down as a result of manifest::unregister_relationship above.
1238
    for (auto &title_rel_id_pair : d_->sheet_title_rel_id_map_)
32✔
1239
    {
1240
        title_rel_id_pair.second = rel_id_map.count(title_rel_id_pair.second) > 0
24✔
1241
            ? rel_id_map[title_rel_id_pair.second]
19✔
1242
            : title_rel_id_pair.second;
43✔
1243
    }
1244

1245
    update_sheet_properties();
8✔
1246
}
16!
1247

1248
worksheet workbook::create_sheet(std::size_t index)
1✔
1249
{
1250
    create_sheet();
1✔
1251

1252
    if (index != d_->worksheets_.size() - 1)
1!
1253
    {
1254
        auto iter = d_->worksheets_.begin();
×
1255

1256
        for (std::size_t i = 0; i < index; ++i, ++iter)
×
1257
        {
1258
        }
1259

1260
        d_->worksheets_.insert(iter, d_->worksheets_.back());
×
1261
        d_->worksheets_.pop_back();
×
1262
    }
1263

1264
    return sheet_by_index(index);
1✔
1265
}
1266

1267
worksheet workbook::create_sheet_with_rel(const std::string &title, const relationship &rel)
×
1268
{
1269
    auto sheet_id = d_->worksheets_.size() + 1;
×
1270
    d_->worksheets_.push_back(detail::worksheet_impl(this, sheet_id, title));
×
1271

1272
    auto workbook_rel = d_->manifest_.relationship(path("/"), relationship_type::office_document);
×
1273
    auto sheet_absoulute_path = workbook_rel.target().path().parent().append(rel.target().path());
×
1274
    d_->manifest_.register_override_type(sheet_absoulute_path,
×
1275
        "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
1276
    auto ws_rel = d_->manifest_.register_relationship(workbook_rel.target(),
×
1277
        relationship_type::worksheet, rel.target(), target_mode::internal);
×
1278
    d_->sheet_title_rel_id_map_[title] = ws_rel;
×
1279

1280
    update_sheet_properties();
×
1281

1282
    return worksheet(&d_->worksheets_.back());
×
1283
}
×
1284

1285
workbook::iterator workbook::begin()
318✔
1286
{
1287
    return iterator(*this, 0);
318✔
1288
}
1289

1290
workbook::iterator workbook::end()
311✔
1291
{
1292
    return iterator(*this, d_->worksheets_.size());
311✔
1293
}
1294

1295
workbook::const_iterator workbook::begin() const
1,106✔
1296
{
1297
    return cbegin();
1,106✔
1298
}
1299

1300
workbook::const_iterator workbook::end() const
1,107✔
1301
{
1302
    return cend();
1,107✔
1303
}
1304

1305
workbook::const_iterator workbook::cbegin() const
1,116✔
1306
{
1307
    return const_iterator(*this, 0);
1,116✔
1308
}
1309

1310
workbook::const_iterator workbook::cend() const
1,108✔
1311
{
1312
    return const_iterator(*this, d_->worksheets_.size());
1,108✔
1313
}
1314

1315
std::vector<std::string> workbook::sheet_titles() const
601✔
1316
{
1317
    std::vector<std::string> names;
601✔
1318

1319
    for (auto ws : *this)
1,338✔
1320
    {
1321
        names.push_back(ws.title());
737✔
1322
    }
1323

1324
    return names;
601✔
1325
}
×
1326

1327
std::size_t workbook::sheet_count() const
315✔
1328
{
1329
    return d_->worksheets_.size();
315✔
1330
}
1331

1332
worksheet workbook::operator[](const std::string &name)
2✔
1333
{
1334
    return sheet_by_title(name);
2✔
1335
}
1336

1337
worksheet workbook::operator[](std::size_t index)
129✔
1338
{
1339
    return sheet_by_index(index);
129✔
1340
}
1341

1342
void workbook::clear()
189✔
1343
{
1344
    *d_ = detail::workbook_impl();
189✔
1345
    d_->stylesheet_.clear();
189✔
1346
}
189✔
1347

1348
bool workbook::compare(const workbook &other, bool compare_by_reference) const
23✔
1349
{
1350
    if (compare_by_reference)
23✔
1351
    {
1352
        return d_ == other.d_;
9✔
1353
    }
1354
    else
1355
    {
1356
        return *d_ == *other.d_;
14✔
1357
    }
1358
}
1359

1360
bool workbook::operator==(const workbook &rhs) const
7✔
1361
{
1362
    return compare(rhs, true);
7✔
1363
}
1364

1365
bool workbook::operator!=(const workbook &rhs) const
4✔
1366
{
1367
    return !operator==(rhs);
4✔
1368
}
1369

1370
void workbook::swap(workbook &right)
236✔
1371
{
1372
    std::swap(d_, right.d_);
236✔
1373
}
236✔
1374

1375
workbook workbook::clone(clone_method method) const
2✔
1376
{
1377
    switch (method)
2!
1378
    {
1379
    case clone_method::deep_copy:
1✔
1380
    {
1381
        workbook wb;
1!
1382
        *wb.d_ = *d_;
1✔
1383

1384
        for (auto ws : wb)
5✔
1385
        {
1386
            ws.parent(wb);
2✔
1387
        }
1388

1389
        wb.d_->stylesheet_.get().parent = wb.d_;
1✔
1390

1391
        return wb;
1!
1392
    }
×
1393
    case clone_method::shallow_copy:
1✔
1394
    {
1395
        return workbook(d_);
1✔
1396
    }
1397
    default:
×
1398
        throw xlnt::invalid_parameter("clone method not supported");
×
1399
    }
1400
}
1401

1402
bool workbook::has_theme() const
1✔
1403
{
1404
    return d_->theme_.is_set();
1✔
1405
}
1406

1407
const theme &workbook::theme() const
×
1408
{
1409
    return d_->theme_.get();
×
1410
}
1411

1412
void workbook::theme(const class theme &value)
312✔
1413
{
1414
    d_->theme_ = value;
312✔
1415
    register_workbook_part(relationship_type::theme);
312✔
1416
}
312✔
1417

1418
std::vector<named_range> workbook::named_ranges() const
42✔
1419
{
1420
    std::vector<xlnt::named_range> named_ranges;
42✔
1421

1422
    for (auto ws : *this)
144✔
1423
    {
1424
        for (auto &ws_named_range : ws.d_->named_ranges_)
51!
1425
        {
1426
            named_ranges.push_back(ws_named_range.second);
×
1427
        }
1428
    }
1429

1430
    return named_ranges;
42✔
1431
}
×
1432

1433
format workbook::create_format(bool default_format)
292✔
1434
{
1435
    register_workbook_part(relationship_type::stylesheet);
292✔
1436
    return d_->stylesheet_.get().create_format(default_format);
292✔
1437
}
1438

1439
bool workbook::has_style(const std::string &name) const
2✔
1440
{
1441
    return d_->stylesheet_.get().has_style(name);
2✔
1442
}
1443

1444
void workbook::clear_styles()
1✔
1445
{
1446
    apply_to_cells([](cell c) { c.clear_style(); });
2✔
1447
}
1✔
1448

1449
void workbook::default_slicer_style(const std::string &value)
2✔
1450
{
1451
    d_->stylesheet_.get().default_slicer_style = value;
2✔
1452
}
2✔
1453

1454
std::string workbook::default_slicer_style() const
×
1455
{
1456
    return d_->stylesheet_.get().default_slicer_style.get();
×
1457
}
1458

1459
void workbook::enable_known_fonts()
48✔
1460
{
1461
    d_->stylesheet_.get().known_fonts_enabled = true;
48✔
1462
}
48✔
1463

1464
void workbook::disable_known_fonts()
×
1465
{
1466
    d_->stylesheet_.get().known_fonts_enabled = false;
×
1467
}
×
1468

1469
bool workbook::known_fonts_enabled() const
×
1470
{
1471
    return d_->stylesheet_.get().known_fonts_enabled;
×
1472
}
1473

1474
void workbook::clear_formats()
1✔
1475
{
1476
    apply_to_cells([](cell c) { c.clear_format(); });
2✔
1477
}
1✔
1478

1479
void workbook::apply_to_cells(std::function<void(cell)> f)
2✔
1480
{
1481
    for (auto ws : *this)
6✔
1482
    {
1483
        for (auto row = ws.lowest_row(); row <= ws.highest_row(); ++row)
4✔
1484
        {
1485
            for (auto column = ws.lowest_column(); column <= ws.highest_column(); ++column)
6✔
1486
            {
1487
                if (ws.has_cell(cell_reference(column, row)))
2!
1488
                {
1489
                    f.operator()(ws.cell(cell_reference(column, row)));
2✔
1490
                }
1491
            }
1492
        }
1493
    }
1494
}
2✔
1495

1496
format workbook::format(std::size_t format_index)
882✔
1497
{
1498
    return d_->stylesheet_.get().format(format_index);
882✔
1499
}
1500

1501
const format workbook::format(std::size_t format_index) const
×
1502
{
1503
    return d_->stylesheet_.get().format(format_index);
×
1504
}
1505

1506
manifest &workbook::manifest()
16,328✔
1507
{
1508
    return d_->manifest_;
16,328✔
1509
}
1510

1511
const manifest &workbook::manifest() const
770✔
1512
{
1513
    return d_->manifest_;
770✔
1514
}
1515

1516
const rich_text &workbook::shared_strings(std::size_t index) const
189✔
1517
{
1518
    if (index < d_->shared_strings_values_.size())
189!
1519
    {
1520
        return d_->shared_strings_values_.at(index);
189✔
1521
    }
1522

1523
    static rich_text empty;
×
1524
    return empty;
×
1525
}
1526

1527
std::vector<rich_text> &workbook::shared_strings()
×
1528
{
1529
    return d_->shared_strings_values_;
×
1530
}
1531

1532
const std::vector<rich_text> &workbook::shared_strings() const
60✔
1533
{
1534
    return d_->shared_strings_values_;
60✔
1535
}
1536

1537
std::size_t workbook::add_shared_string(const rich_text &shared, bool allow_duplicates)
1,006✔
1538
{
1539
    register_workbook_part(relationship_type::shared_string_table);
1,006✔
1540

1541
    if (!allow_duplicates)
1,006✔
1542
    {
1543
        auto it = d_->shared_strings_ids_.find(shared);
292✔
1544

1545
        if (it != d_->shared_strings_ids_.end())
292✔
1546
        {
1547
            return it->second;
4✔
1548
        }
1549
    }
1550

1551
    auto sz = d_->shared_strings_ids_.size();
1,002✔
1552
    d_->shared_strings_ids_[shared] = sz;
1,002✔
1553
    d_->shared_strings_values_.push_back(shared);
1,002✔
1554

1555
    return sz;
1,002✔
1556
}
1557

1558
bool workbook::contains(const std::string &sheet_title) const
331✔
1559
{
1560
    for (auto ws : *this)
457✔
1561
    {
1562
        if (ws.title() == sheet_title) return true;
111✔
1563
    }
1564

1565
    return false;
283✔
1566
}
1567

1568
void workbook::thumbnail(const std::vector<std::uint8_t> &thumbnail,
243✔
1569
    const std::string &extension, const std::string &content_type)
1570
{
1571
    if (!d_->manifest_.has_relationship(path("/"), relationship_type::thumbnail))
729!
1572
    {
1573
        d_->manifest_.register_default_type(extension, content_type);
243✔
1574
        d_->manifest_.register_relationship(uri("/"), relationship_type::thumbnail,
486✔
1575
            uri("docProps/thumbnail.jpeg"), target_mode::internal);
729✔
1576
    }
1577

1578
    auto thumbnail_rel = d_->manifest_.relationship(path("/"), relationship_type::thumbnail);
486✔
1579
    d_->images_[thumbnail_rel.target().to_string()] = thumbnail;
243✔
1580
}
243✔
1581

1582
const std::vector<std::uint8_t> &workbook::thumbnail() const
×
1583
{
1584
    auto thumbnail_rel = d_->manifest_.relationship(path("/"), relationship_type::thumbnail);
×
1585
    return d_->images_.at(thumbnail_rel.target().to_string());
×
1586
}
×
1587

1588
const std::unordered_map<std::string, std::vector<std::uint8_t>> &workbook::binaries() const
×
1589
{
1590
    return d_->binaries_;
×
1591
}
1592

1593
style workbook::create_style(const std::string &name)
7✔
1594
{
1595
    return d_->stylesheet_.get().create_style(name);
7✔
1596
}
1597

1598
style workbook::create_builtin_style(const std::size_t builtin_id)
244✔
1599
{
1600
    return d_->stylesheet_.get().create_builtin_style(builtin_id);
244✔
1601
}
1602

1603
style workbook::style(const std::string &name)
3✔
1604
{
1605
    return d_->stylesheet_.get().style(name);
3✔
1606
}
1607

1608
const style workbook::style(const std::string &name) const
1✔
1609
{
1610
    return d_->stylesheet_.get().style(name);
1✔
1611
}
1612

1613
calendar workbook::base_date() const
55✔
1614
{
1615
    return d_->base_date_;
55✔
1616
}
1617

1618
void workbook::base_date(calendar base_date)
92✔
1619
{
1620
    d_->base_date_ = base_date;
92✔
1621
}
92✔
1622

1623
bool workbook::has_title() const
×
1624
{
1625
    return d_->title_.is_set();
×
1626
}
1627

1628
std::string workbook::title() const
×
1629
{
1630
    return d_->title_.get();
×
1631
}
1632

1633
void workbook::title(const std::string &title)
×
1634
{
1635
    d_->title_ = title;
×
1636
}
×
1637

1638
detail::workbook_impl &workbook::impl()
170✔
1639
{
1640
    return *d_;
170✔
1641
}
1642

1643
const detail::workbook_impl &workbook::impl() const
140✔
1644
{
1645
    return *d_;
140✔
1646
}
1647

1648
bool workbook::has_view() const
90✔
1649
{
1650
    return d_->view_.is_set();
90✔
1651
}
1652

1653
workbook_view workbook::view() const
86✔
1654
{
1655
    if (!d_->view_.is_set())
86!
1656
    {
1657
        throw invalid_attribute();
×
1658
    }
1659

1660
    return d_->view_.get();
86✔
1661
}
1662

1663
void workbook::view(const workbook_view &view)
323✔
1664
{
1665
    d_->view_ = view;
323✔
1666
}
323✔
1667

1668
bool workbook::has_code_name() const
42✔
1669
{
1670
    return d_->code_name_.is_set();
42✔
1671
}
1672

1673
std::string workbook::code_name() const
×
1674
{
1675
    if (has_code_name())
×
1676
    {
1677
        throw invalid_attribute();
×
1678
    }
1679

1680
    return d_->code_name_.get();
×
1681
}
1682

1683
void workbook::code_name(const std::string &code_name)
×
1684
{
1685
    d_->code_name_ = code_name;
×
1686
}
×
1687

1688
bool workbook::has_file_version() const
42✔
1689
{
1690
    return d_->file_version_.is_set();
42✔
1691
}
1692

1693
std::string workbook::app_name() const
38✔
1694
{
1695
    return d_->file_version_.get().app_name;
38✔
1696
}
1697

1698
std::size_t workbook::last_edited() const
38✔
1699
{
1700
    return d_->file_version_.get().last_edited;
38✔
1701
}
1702

1703
std::size_t workbook::lowest_edited() const
38✔
1704
{
1705
    return d_->file_version_.get().lowest_edited;
38✔
1706
}
1707

1708
std::size_t workbook::rup_build() const
38✔
1709
{
1710
    return d_->file_version_.get().rup_build;
38✔
1711
}
1712

1713
bool workbook::has_calculation_properties() const
42✔
1714
{
1715
    return d_->calculation_properties_.is_set();
42✔
1716
}
1717

1718
class calculation_properties workbook::calculation_properties() const
78✔
1719
{
1720
    return d_->calculation_properties_.get();
78✔
1721
}
1722

1723
void workbook::calculation_properties(const class calculation_properties &props)
321✔
1724
{
1725
    d_->calculation_properties_ = props;
321✔
1726
}
321✔
1727

1728
void workbook::garbage_collect_formulae()
18✔
1729
{
1730
    auto any_with_formula = false;
18✔
1731

1732
    for (auto ws : *this)
36✔
1733
    {
1734
        for (auto row : ws.rows(true))
104✔
1735
        {
1736
            for (auto cell : row)
512✔
1737
            {
1738
                if (cell.has_formula())
426✔
1739
                {
1740
                    any_with_formula = true;
136✔
1741
                }
1742
            }
1743
        }
18✔
1744
    }
1745

1746
    if (any_with_formula) return;
18✔
1747

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

1750
    if (manifest().has_relationship(wb_rel.target().path(), relationship_type::calculation_chain))
2!
1751
    {
1752
        auto calc_chain_rel = manifest().relationship(wb_rel.target().path(), relationship_type::calculation_chain);
2✔
1753
        auto calc_chain_part = manifest().canonicalize({wb_rel, calc_chain_rel});
10!
1754
        manifest().unregister_override_type(calc_chain_part);
2✔
1755
        manifest().unregister_relationship(wb_rel.target(), calc_chain_rel.id());
2✔
1756
    }
2✔
1757
}
4!
1758

1759
void workbook::update_sheet_properties()
308✔
1760
{
1761
    if (has_extended_property(xlnt::extended_property::titles_of_parts))
308!
1762
    {
1763
        extended_property(xlnt::extended_property::titles_of_parts, sheet_titles());
308✔
1764
    }
1765

1766
    if (has_extended_property(xlnt::extended_property::heading_pairs))
308!
1767
    {
1768
        extended_property(xlnt::extended_property::heading_pairs,
308✔
1769
            std::vector<variant>{variant("Worksheets"), variant(static_cast<int>(sheet_count()))});
1,540!
1770
    }
1771
}
616!
1772

1773
namespace {
1774
// true if a sheet index is != worksheet relationship index
1775
bool needs_reorder(const std::unordered_map<std::string, std::string> &title_to_rels,
282✔
1776
    const std::vector<std::string> &titles,
1777
    std::vector<std::string> &relation_ids)
1778
{
1779
    bool all_match = true;
282✔
1780
    for (std::size_t title_index = 0; title_index < titles.size(); ++title_index)
615✔
1781
    {
1782
        const auto &rel = title_to_rels.at(titles[title_index]);
333✔
1783
        relation_ids.push_back(rel);
333✔
1784
        const auto expected_rel_id = "rId" + std::to_string(title_index + 1);
333✔
1785
        if (rel != expected_rel_id)
333✔
1786
        {
1787
            all_match = false;
40✔
1788
        }
1789
    }
333✔
1790
    return !all_match; // if all are as expected, reorder not required
282✔
1791
};
1792

1793
struct rel_id_sorter
1794
{
1795
    // true if lhs < rhs
1796
    bool operator()(const xlnt::relationship &lhs, const xlnt::relationship &rhs)
240✔
1797
    {
1798
        // format is rTd<decimal number 1..n>
1799
        if (lhs.id().size() != rhs.id().size()) // a number with more digits will be larger
240!
1800
        {
1801
            return lhs.id().size() < rhs.id().size();
×
1802
        }
1803
        return lhs.id() < rhs.id();
240✔
1804
    }
1805
};
1806
} // namespace
1807

1808
void workbook::reorder_relationships()
282✔
1809
{
1810
    const auto titles = sheet_titles();
282✔
1811
    // the relation ID corresponding to the title at the same index is copied into here
1812
    std::vector<std::string> worksheet_rel_ids;
282✔
1813
    worksheet_rel_ids.reserve(titles.size());
282✔
1814
    if (!needs_reorder(d_->sheet_title_rel_id_map_, titles, worksheet_rel_ids))
282✔
1815
    {
1816
        return;
243✔
1817
    }
1818
    // copy of existing relations
1819
    const auto wb_rel_target = manifest().relationship(path("/"), relationship_type::office_document).target();
78✔
1820
    auto rel_copy = manifest().relationships(wb_rel_target.path());
39✔
1821
    std::sort(rel_copy.begin(), rel_copy.end(), rel_id_sorter{});
39✔
1822
    // clear existing relations
1823
    for (const auto &rel : rel_copy)
210✔
1824
    {
1825
        manifest().unregister_relationship(wb_rel_target, rel.id());
171✔
1826
    }
1827
    // create new relations
1828
    std::size_t index = 0;
39✔
1829
    auto new_id = [&index]() { return "rId" + std::to_string(++index); }; // ids start from 1
171✔
1830
    // worksheets first
1831
    while (index < worksheet_rel_ids.size())
129✔
1832
    {
1833
        auto rel_it = std::find_if(rel_copy.begin(), rel_copy.end(),
90✔
1834
            [&](const relationship &rel) { return rel.id() == worksheet_rel_ids[index]; });
238✔
1835

1836
        std::string rel_id = new_id();
90✔
1837
        d_->sheet_title_rel_id_map_.at(titles[index - 1]) = rel_id; // update title -> relation mapping
90✔
1838
        manifest().register_relationship(relationship(rel_id, rel_it->type(),
180✔
1839
            rel_it->source(), rel_it->target(), rel_it->target_mode()));
90✔
1840
    }
90✔
1841
    // then all the other relations in the same order they started (just new indices)
1842
    for (const auto &old_rel : rel_copy)
210✔
1843
    {
1844
        if (old_rel.type() == relationship_type::worksheet)
171✔
1845
        {
1846
            continue;
90✔
1847
        }
1848
        manifest().register_relationship(relationship(new_id(), old_rel.type(),
162✔
1849
            old_rel.source(), old_rel.target(), old_rel.target_mode()));
81✔
1850
    }
1851
}
525✔
1852

1853
} // 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