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

xlnt-community / xlnt / dddba1e1-baca-4b00-9de9-02b3cdf93330

04 Feb 2025 10:00PM UTC coverage: 81.87% (-0.1%) from 81.978%
dddba1e1-baca-4b00-9de9-02b3cdf93330

Pull #55

circleci

doomlaur
Fixed compilation under MSVC
Pull Request #55: Add support for C++20 and C++23, and experimental support for C++26

108 of 150 new or added lines in 8 files covered. (72.0%)

4 existing lines in 2 files now uncovered.

11488 of 14032 relevant lines covered (81.87%)

1191666.72 hits per line

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

75.79
./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)
173✔
70
{
71
    auto result = std::vector<T>();
173✔
72
    auto iter = container.begin();
173✔
73

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

79
    return result;
173✔
80
}
×
81

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

93
    return false;
4✔
94
}
95

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

101
    switch (type)
2,770✔
102
    {
103
    case relationship_type::calculation_chain:
12✔
104
        return path("/xl/calcChain.xml");
24✔
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:
532✔
112
        return path("/docProps/core.xml");
1,064✔
113
    case relationship_type::custom_properties:
2✔
114
        return path("/docProps/custom.xml");
4✔
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:
532✔
124
        return path("/docProps/app.xml");
1,064✔
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:
532✔
132
        return path("/xl/workbook.xml");
1,064✔
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:
96✔
146
        return path("/xl/sharedStrings.xml");
192✔
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:
532✔
156
        return path("/xl/styles.xml");
1,064✔
157
    case relationship_type::table_definition:
×
158
        return path("/xl/tableDefinition.xml");
×
159
    case relationship_type::theme:
532✔
160
        return path("/xl/theme/theme1.xml");
1,064✔
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,385✔
179
{
180
    using xlnt::relationship_type;
181

182
    switch (type)
1,385✔
183
    {
184
    case relationship_type::calculation_chain:
6✔
185
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml";
12✔
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:
266✔
193
        return "application/vnd.openxmlformats-package.core-properties+xml";
532✔
194
    case relationship_type::custom_properties:
1✔
195
        return "application/vnd.openxmlformats-officedocument.custom-properties+xml";
2✔
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:
266✔
205
        return "application/vnd.openxmlformats-officedocument.extended-properties+xml";
532✔
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:
266✔
213
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
532✔
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:
48✔
227
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml";
96✔
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:
266✔
237
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml";
532✔
238
    case relationship_type::table_definition:
×
239
        return "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml";
×
240
    case relationship_type::theme:
266✔
241
        return "application/vnd.openxmlformats-officedocument.theme+xml";
532✔
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
85✔
269
{
270
    return keys(d_->core_properties_);
85✔
271
}
272

273
variant workbook::core_property(xlnt::core_property type) const
466✔
274
{
275
    for (auto iter : d_->core_properties_)
1,793✔
276
    {
277
        if (iter.first == type)
1,793✔
278
        {
279
            return iter.second;
932✔
280
        }
281
    }
1,793✔
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,634✔
287
{
288
    register_package_part(relationship_type::core_properties);
1,634✔
289

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

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

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

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

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

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

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

328
variant workbook::extended_property(xlnt::extended_property type) const
855✔
329
{
330
    for (auto iter : d_->extended_properties_)
4,956✔
331
    {
332
        if (iter.first == type)
4,956✔
333
        {
334
            return iter.second;
1,710✔
335
        }
336
    }
4,956✔
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
3✔
347
{
348
    return keys(d_->custom_properties_);
3✔
349
}
350

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

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

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

367
variant workbook::custom_property(const std::string &property_name) const
5✔
368
{
369
    for (auto iter : d_->custom_properties_)
6✔
370
    {
371
        if (iter.first == property_name)
6✔
372
        {
373
            return iter.second;
10✔
374
        }
375
    }
6✔
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()
266✔
391
{
392
    auto impl = new detail::workbook_impl();
266✔
393
    workbook wb(impl);
266✔
394

395
    wb.register_package_part(relationship_type::office_document);
266✔
396

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

402
    wb.thumbnail(excel_thumbnail(), "jpeg", "image/jpeg");
798✔
403

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

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

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

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

436
    auto ws = wb.create_sheet();
266✔
437

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

447
    sheet_view view;
266✔
448
    ws.add_view(view);
266✔
449

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

454
    wb.theme(xlnt::theme());
266✔
455

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

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

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

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

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

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

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

501
    return wb;
266✔
502
}
532✔
503

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

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

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

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

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

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

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

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

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

560
workbook::workbook(detail::workbook_impl *impl)
269✔
561
    : d_(impl)
269✔
562
{
563
    if (impl != nullptr)
269✔
564
    {
565
        if (d_->stylesheet_.is_set())
266✔
566
        {
567
            d_->stylesheet_.get().parent = this;
×
568
        }
569
    }
570
}
269✔
571

572
void workbook::register_package_part(relationship_type type)
6,212✔
573
{
574
    if (!manifest().has_relationship(path("/"), type))
18,636✔
575
    {
576
        manifest().register_override_type(default_path(type), content_type(type));
799✔
577
        manifest().register_relationship(uri("/"), type,
1,598✔
578
            uri(default_path(type).relative_to(path("/")).string()),
3,196✔
579
            target_mode::internal);
580
    }
581
}
6,212✔
582

583
void workbook::register_workbook_part(relationship_type type)
41,947✔
584
{
585
    auto wb_rel = manifest().relationship(path("/"), relationship_type::office_document);
83,894✔
586
    auto wb_path = manifest().canonicalize({wb_rel});
167,788✔
587

588
    if (!manifest().has_relationship(wb_path, type))
41,947✔
589
    {
590
        manifest().register_override_type(default_path(type), content_type(type));
586✔
591
        manifest().register_relationship(uri(wb_path.string()), type,
1,172✔
592
            uri(default_path(type).relative_to(wb_path.resolve(path("/"))).string()),
2,344✔
593
            target_mode::internal);
594
    }
595
}
83,894✔
596

597
void workbook::register_worksheet_part(worksheet ws, relationship_type type)
34✔
598
{
599
    auto wb_rel = manifest().relationship(path("/"),
102✔
600
        relationship_type::office_document);
34✔
601
    auto ws_rel = manifest().relationship(wb_rel.target().path(),
34✔
602
        d_->sheet_title_rel_id_map_.at(ws.title()));
34✔
603
    path ws_path(ws_rel.source().path().parent().append(ws_rel.target().path()));
34✔
604

605
    if (type == relationship_type::comments)
34✔
606
    {
607
        if (!manifest().has_relationship(ws_path, relationship_type::vml_drawing))
34✔
608
        {
609
            std::size_t file_number = 1;
3✔
610
            path filename("vmlDrawing1.vml");
3✔
611
            bool filename_exists = true;
3✔
612

613
            while (filename_exists)
7✔
614
            {
615
                filename_exists = false;
4✔
616

617
                for (auto current_ws_rel :
4✔
618
                    manifest().relationships(wb_rel.target().path(), xlnt::relationship_type::worksheet))
14✔
619
                {
620
                    path current_ws_path(current_ws_rel.source().path().parent().append(current_ws_rel.target().path()));
6✔
621
                    if (!manifest().has_relationship(current_ws_path, xlnt::relationship_type::vml_drawing)) continue;
6✔
622

623
                    for (auto current_ws_child_rel :
2✔
624
                        manifest().relationships(current_ws_path, xlnt::relationship_type::vml_drawing))
5✔
625
                    {
626
                        if (current_ws_child_rel.target().path() == path("../drawings").append(filename))
4✔
627
                        {
628
                            filename_exists = true;
1✔
629
                            break;
1✔
630
                        }
631
                    }
3✔
632
                }
14✔
633

634
                if (filename_exists)
4✔
635
                {
636
                    file_number++;
1✔
637
                    filename = path("vmlDrawing" + std::to_string(file_number) + ".vml");
1✔
638
                }
639
            }
640

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

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

648
        if (!manifest().has_relationship(ws_path, relationship_type::comments))
34✔
649
        {
650
            std::size_t file_number = 1;
3✔
651
            path filename("comments1.xml");
6✔
652

653
            while (true)
654
            {
655
                if (!manifest().has_override_type(constants::package_xl().append(filename))) break;
4✔
656

657
                file_number++;
1✔
658
                filename = path("comments" + std::to_string(file_number) + ".xml");
1✔
659
            }
660

661
            const path absolute_path(constants::package_xl().append(filename));
3✔
662
            manifest().register_override_type(
9✔
663
                absolute_path, "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml");
664

665
            const path relative_path(path("..").append(filename));
3✔
666
            manifest().register_relationship(
6✔
667
                uri(ws_path.string()), relationship_type::comments, uri(relative_path.string()), target_mode::internal);
6✔
668
        }
3✔
669
    }
670
}
34✔
671

672
const worksheet workbook::sheet_by_title(const std::string &title) const
129✔
673
{
674
    for (auto &impl : d_->worksheets_)
173✔
675
    {
676
        if (impl.title_ == title)
172✔
677
        {
678
            return worksheet(&impl);
128✔
679
        }
680
    }
681

682
    throw key_not_found();
1✔
683
}
684

685
worksheet workbook::sheet_by_title(const std::string &title)
16✔
686
{
687
    for (auto &impl : d_->worksheets_)
28✔
688
    {
689
        if (impl.title_ == title)
25✔
690
        {
691
            return worksheet(&impl);
13✔
692
        }
693
    }
694

695
    throw key_not_found();
3✔
696
}
697

698
worksheet workbook::sheet_by_index(std::size_t index)
599✔
699
{
700
    if (index >= d_->worksheets_.size())
599✔
701
    {
702
        throw invalid_parameter();
1✔
703
    }
704

705
    auto iter = d_->worksheets_.begin();
598✔
706

707
    for (std::size_t i = 0; i < index; ++i)
680✔
708
    {
709
        ++iter;
82✔
710
    }
711

712
    return worksheet(&*iter);
1,196✔
713
}
714

715
const worksheet workbook::sheet_by_index(std::size_t index) const
1,466✔
716
{
717
    if (index >= d_->worksheets_.size())
1,466✔
718
    {
719
        throw invalid_parameter();
1✔
720
    }
721

722
    auto iter = d_->worksheets_.begin();
1,465✔
723

724
    for (std::size_t i = 0; i < index; ++i, ++iter)
1,856✔
725
    {
726
    }
727

728
    return worksheet(&*iter);
2,930✔
729
}
730

731
worksheet workbook::sheet_by_id(std::size_t id)
×
732
{
733
    for (auto &impl : d_->worksheets_)
×
734
    {
735
        if (impl.id_ == id)
×
736
        {
737
            return worksheet(&impl);
×
738
        }
739
    }
740

741
    throw key_not_found();
×
742
}
743

744
const worksheet workbook::sheet_by_id(std::size_t id) const
×
745
{
746
    for (auto &impl : d_->worksheets_)
×
747
    {
748
        if (impl.id_ == id)
×
749
        {
750
            return worksheet(&impl);
×
751
        }
752
    }
753

754
    throw key_not_found();
×
755
}
756

757
bool workbook::sheet_hidden_by_index(std::size_t index) const
1✔
758
{
759
    if (index >= d_->sheet_hidden_.size())
1✔
760
    {
761
        throw invalid_parameter();
×
762
    }
763

764
    return d_->sheet_hidden_.at(index);
1✔
765
}
766

767
worksheet workbook::active_sheet()
157✔
768
{
769
    return sheet_by_index(d_->active_sheet_index_.is_set() ? d_->active_sheet_index_.get() : 0);
157✔
770
}
771
void workbook::active_sheet(std::size_t index)
1✔
772
{
773
    d_->active_sheet_index_.set(index);
1✔
774
    d_->view_.get().active_tab = index;
1✔
775
}
1✔
776

777
bool workbook::has_named_range(const std::string &name) const
11✔
778
{
779
    for (auto worksheet : *this)
27✔
780
    {
781
        if (worksheet.has_named_range(name))
17✔
782
        {
783
            return true;
9✔
784
        }
785
    }
786
    return false;
2✔
787
}
788

789
worksheet workbook::create_sheet()
322✔
790
{
791
    std::string title = "Sheet1";
322✔
792
    int index = 1;
322✔
793

794
    // make a unique sheet name. Sheet<1...n>
795
    while (contains(title))
386✔
796
    {
797
        title = "Sheet" + std::to_string(++index);
64✔
798
    }
799
    // unique sheet id
800
    size_t sheet_id = 1;
322✔
801
    for (const auto ws : *this)
390✔
802
    {
803
        sheet_id = std::max(sheet_id, ws.id() + 1);
68✔
804
    }
805
    d_->worksheets_.push_back(detail::worksheet_impl(this, sheet_id, title));
322✔
806
    // unique sheet file name
807
    auto workbook_rel = d_->manifest_.relationship(path("/"), relationship_type::office_document);
644✔
808
    auto workbook_files = d_->manifest_.relationships(workbook_rel.target().path());
322✔
809
    auto rel_vec_contains = [&workbook_files](const xlnt::path &new_file_id) {
323✔
810
        return workbook_files.end() != std::find_if(workbook_files.begin(), workbook_files.end(), [&new_file_id](const xlnt::relationship &rel) {
646✔
811
            return rel.target().path() == new_file_id;
184✔
812
        });
646✔
813
    };
322✔
814

815
    size_t file_id = sheet_id;
322✔
816
    xlnt::path sheet_relative_path;
322✔
817
    do
818
    {
819
        sheet_relative_path = path("worksheets").append("sheet" + std::to_string(file_id++) + ".xml");
323✔
820
    } while (rel_vec_contains(sheet_relative_path));
323✔
821

822
    uri relative_sheet_uri(sheet_relative_path.string());
322✔
823
    auto absolute_sheet_path = path("/xl").append(relative_sheet_uri.path());
322✔
824
    d_->manifest_.register_override_type(
644✔
825
        absolute_sheet_path, "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
826
    auto ws_rel = d_->manifest_.register_relationship(
322✔
827
        workbook_rel.target(), relationship_type::worksheet, relative_sheet_uri, target_mode::internal);
322✔
828
    d_->sheet_title_rel_id_map_[title] = ws_rel;
322✔
829

830
    update_sheet_properties();
322✔
831
    reorder_relationships();
322✔
832

833
    return worksheet(&d_->worksheets_.back());
644✔
834
}
322✔
835

836
worksheet workbook::copy_sheet(worksheet to_copy)
3✔
837
{
838
    if (to_copy.d_->parent_ != this) throw invalid_parameter();
3✔
839

840
    detail::worksheet_impl impl(*to_copy.d_);
2✔
841
    auto new_sheet = create_sheet();
2✔
842
    impl.title_ = new_sheet.title();
2✔
843
    impl.id_ = new_sheet.id();
2✔
844
    *new_sheet.d_ = impl;
2✔
845

846
    return new_sheet;
4✔
847
}
2✔
848

849
worksheet workbook::copy_sheet(worksheet to_copy, std::size_t index)
1✔
850
{
851
    copy_sheet(to_copy);
1✔
852

853
    if (index != d_->worksheets_.size() - 1)
1✔
854
    {
855
        auto iter = d_->worksheets_.begin();
1✔
856

857
        for (std::size_t i = 0; i < index; ++i, ++iter)
1✔
858
        {
859
        }
860

861
        d_->worksheets_.insert(iter, d_->worksheets_.back());
1✔
862
        d_->worksheets_.pop_back();
1✔
863
    }
864

865
    return sheet_by_index(index);
1✔
866
}
867

868
std::size_t workbook::index(worksheet ws)
3✔
869
{
870
    auto match = std::find(begin(), end(), ws);
3✔
871

872
    if (match == end())
3✔
873
    {
874
        throw invalid_parameter();
1✔
875
    }
876

877
    return static_cast<std::size_t>(std::distance(begin(), match));
6✔
878
}
879

880
void workbook::create_named_range(const std::string &name, worksheet range_owner, const std::string &reference_string)
7✔
881
{
882
    create_named_range(name, range_owner, range_reference(reference_string));
7✔
883
}
6✔
884

885
void workbook::create_named_range(const std::string &name, worksheet range_owner, const range_reference &reference)
7✔
886
{
887
    sheet_by_title(range_owner.title()).create_named_range(name, reference);
8✔
888
}
6✔
889

890
void workbook::remove_named_range(const std::string &name)
2✔
891
{
892
    for (auto ws : *this)
8✔
893
    {
894
        if (ws.has_named_range(name))
4✔
895
        {
896
            ws.remove_named_range(name);
1✔
897
            return;
1✔
898
        }
899
    }
900

901
    throw key_not_found();
1✔
902
}
903

904
range workbook::named_range(const std::string &name)
2✔
905
{
906
    for (auto ws : *this)
8✔
907
    {
908
        if (ws.has_named_range(name))
4✔
909
        {
910
            return ws.named_range(name);
1✔
911
        }
912
    }
913

914
    throw key_not_found();
1✔
915
}
916

917
void workbook::load(std::istream &stream)
99✔
918
{
919
    clear();
99✔
920
    detail::xlsx_consumer consumer(*this);
99✔
921

922
    try
923
    {
924
        consumer.read(stream);
99✔
925
    }
926
    catch (xlnt::exception &e)
1✔
927
    {
928
        if (e.what() == std::string("xlnt::exception : encrypted xlsx, password required"))
3✔
929
        {
930
            stream.seekg(0, std::ios::beg);
×
931
            consumer.read(stream, "VelvetSweatshop");
×
932
        }
933
        else
934
        {
935
            throw;
1✔
936
        }
937
    }
1✔
938
}
99✔
939

940
void workbook::load(const std::vector<std::uint8_t> &data)
23✔
941
{
942
    if (data.size() < 22) // the shortest ZIP file is 22 bytes
23✔
943
    {
944
        throw xlnt::exception("file is empty or malformed");
×
945
    }
946

947
    xlnt::detail::vector_istreambuf data_buffer(data);
23✔
948
    std::istream data_stream(&data_buffer);
23✔
949
    load(data_stream);
23✔
950
}
23✔
951

952
template <typename T>
953
void workbook::load_internal(const T &filename)
12✔
954
{
955
    return load(path(filename));
12✔
956
}
957

958
template <typename T>
NEW
959
void workbook::load_internal(const T &filename, const T &password)
×
960
{
NEW
961
    return load(path(filename), password);
×
962
}
963

964
void workbook::load(const std::string &filename)
11✔
965
{
966
    return load_internal(filename);
11✔
967
}
968

969
void workbook::load(const path &filename)
74✔
970
{
971
    std::ifstream file_stream;
74✔
972
    open_stream(file_stream, filename.string());
74✔
973

974
    if (!file_stream.good())
74✔
975
    {
976
        throw xlnt::exception("file not found " + filename.string());
×
977
    }
978

979
    load(file_stream);
74✔
980
}
74✔
981

982
void workbook::load(const std::string &filename, const std::string &password)
×
983
{
NEW
984
    return load_internal(filename, password);
×
985
}
986

987
template <typename T>
988
void workbook::load_internal(const xlnt::path &filename, const T &password)
11✔
989
{
990
    std::ifstream file_stream;
11✔
991
    open_stream(file_stream, filename.string());
11✔
992

993
    if (!file_stream.good())
11✔
994
    {
995
        throw xlnt::exception("file not found " + filename.string());
×
996
    }
997

998
    return load(file_stream, password);
17✔
999
}
11✔
1000

1001
void workbook::load(const path &filename, const std::string &password)
9✔
1002
{
1003
    load_internal(filename, password);
9✔
1004
}
4✔
1005

1006
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
1007
void workbook::load(const xlnt::path &filename, std::u8string_view password)
2✔
1008
{
1009
    load_internal(filename, password);
2✔
1010
}
2✔
1011
#endif
1012

1013
template <typename T>
1014
void workbook::load_internal(const std::vector<std::uint8_t> &data, const T &password)
4✔
1015
{
1016
    if (data.size() < 22) // the shortest ZIP file is 22 bytes
4✔
1017
    {
1018
        throw xlnt::exception("file is empty or malformed");
×
1019
    }
1020

1021
    xlnt::detail::vector_istreambuf data_buffer(data);
4✔
1022
    std::istream data_stream(&data_buffer);
4✔
1023
    load(data_stream, password);
4✔
1024
}
4✔
1025

1026
void workbook::load(const std::vector<std::uint8_t> &data, const std::string &password)
3✔
1027
{
1028
    load_internal(data, password);
3✔
1029
}
3✔
1030

1031
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
1032
void workbook::load(const std::vector<std::uint8_t> &data, std::u8string_view password)
1✔
1033
{
1034
    load_internal(data, password);
1✔
1035
}
1✔
1036
#endif
1037

1038
template <typename T>
1039
void workbook::load_internal(std::istream &stream, const T &password)
15✔
1040
{
1041
    clear();
15✔
1042
    detail::xlsx_consumer consumer(*this);
15✔
1043
    consumer.read(stream, password);
15✔
1044
}
15✔
1045

1046
void workbook::load(std::istream &stream, const std::string &password)
12✔
1047
{
1048
    load_internal(stream, password);
12✔
1049
}
7✔
1050

1051
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
1052
void workbook::load(std::istream &stream, std::u8string_view password)
3✔
1053
{
1054
    load_internal(stream, password);
3✔
1055
}
3✔
1056
#endif
1057

1058
void workbook::save(std::vector<std::uint8_t> &data) const
13✔
1059
{
1060
    xlnt::detail::vector_ostreambuf data_buffer(data);
13✔
1061
    std::ostream data_stream(&data_buffer);
13✔
1062
    save(data_stream);
13✔
1063
}
13✔
1064

1065
template <typename T>
NEW
1066
void workbook::save_internal(std::vector<std::uint8_t> &data, const T &password) const
×
1067
{
1068
    xlnt::detail::vector_ostreambuf data_buffer(data);
×
1069
    std::ostream data_stream(&data_buffer);
×
1070
    save(data_stream, password);
×
1071
}
×
1072

NEW
1073
void workbook::save(std::vector<std::uint8_t> &data, const std::string &password) const
×
1074
{
NEW
1075
    save_internal(data, password);
×
NEW
1076
}
×
1077

1078
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
NEW
1079
void workbook::save(std::vector<std::uint8_t> &data, std::u8string_view password) const
×
1080
{
NEW
1081
    save_internal(data, password);
×
NEW
1082
}
×
1083
#endif
1084

1085
template <typename T>
1086
void workbook::save_internal(const T &filename) const
50✔
1087
{
1088
    save(path(filename));
50✔
1089
}
50✔
1090

1091
template <typename T>
NEW
1092
void workbook::save_internal(const T &filename, const T &password) const
×
1093
{
UNCOV
1094
    save(path(filename), password);
×
UNCOV
1095
}
×
1096

1097
void workbook::save(const std::string &filename) const
49✔
1098
{
1099
    save_internal(filename);
49✔
1100
}
49✔
1101

NEW
1102
void workbook::save(const std::string &filename, const std::string &password) const
×
1103
{
NEW
1104
    save_internal(filename, password);
×
NEW
1105
}
×
1106

1107
void workbook::save(const path &filename) const
70✔
1108
{
1109
    std::ofstream file_stream;
70✔
1110
    open_stream(file_stream, filename.string());
70✔
1111
    save(file_stream);
70✔
1112
}
70✔
1113

1114
template <typename T>
1115
void workbook::save_internal(const xlnt::path &filename, const T &password) const
4✔
1116
{
1117
    std::ofstream file_stream;
4✔
1118
    open_stream(file_stream, filename.string());
4✔
1119
    save(file_stream, password);
4✔
1120
}
4✔
1121

1122
void workbook::save(const path &filename, const std::string &password) const
3✔
1123
{
1124
    save_internal(filename, password);
3✔
1125
}
3✔
1126

1127
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
1128
void workbook::save(const xlnt::path &filename, std::u8string_view password) const
1✔
1129
{
1130
    save_internal(filename, password);
1✔
1131
}
1✔
1132
#endif
1133

1134
void workbook::save(std::ostream &stream) const
83✔
1135
{
1136
    detail::xlsx_producer producer(*this);
83✔
1137
    producer.write(stream);
83✔
1138
}
83✔
1139

1140
template <typename T>
1141
void workbook::save_internal(std::ostream &stream, const T &password) const
4✔
1142
{
1143
    detail::xlsx_producer producer(*this);
4✔
1144
    producer.write(stream, password);
4✔
1145
}
4✔
1146

1147
void workbook::save(std::ostream &stream, const std::string &password) const
3✔
1148
{
1149
    save_internal(stream, password);
3✔
1150
}
3✔
1151

1152
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
1153
void workbook::save(std::ostream &stream, std::u8string_view password) const
1✔
1154
{
1155
    save_internal(stream, password);
1✔
1156
}
1✔
1157
#endif
1158

1159
#ifdef _MSC_VER
1160
void workbook::save(const std::wstring &filename) const
1161
{
1162
    std::ofstream file_stream;
1163
    open_stream(file_stream, filename);
1164
    save(file_stream);
1165
}
1166

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

1174
void workbook::load(const std::wstring &filename)
1175
{
1176
    std::ifstream file_stream;
1177
    open_stream(file_stream, filename);
1178
    load(file_stream);
1179
}
1180

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

1189
#if XLNT_HAS_FEATURE(U8_STRING_VIEW)
1190
void workbook::save(std::u8string_view filename) const
1✔
1191
{
1192
    save_internal(filename);
1✔
1193
}
1✔
1194

NEW
1195
void workbook::save(std::u8string_view filename, std::u8string_view password) const
×
1196
{
NEW
1197
    save_internal(filename, password);
×
NEW
1198
}
×
1199

1200
void workbook::load(std::u8string_view filename)
1✔
1201
{
1202
    load_internal(filename);
1✔
1203
}
1✔
1204

NEW
1205
void workbook::load(std::u8string_view filename, std::u8string_view password)
×
1206
{
NEW
1207
    load_internal(filename, password);
×
NEW
1208
}
×
1209
#endif
1210

1211
void workbook::remove_sheet(worksheet ws)
8✔
1212
{
1213
    auto match_iter = std::find_if(d_->worksheets_.begin(), d_->worksheets_.end(),
8✔
1214
        [=](detail::worksheet_impl &comp) { return &comp == ws.d_; });
23✔
1215

1216
    if (match_iter == d_->worksheets_.end())
8✔
1217
    {
1218
        throw invalid_parameter();
×
1219
    }
1220

1221
    auto ws_rel_id = d_->sheet_title_rel_id_map_.at(ws.title());
8✔
1222
    auto wb_rel = d_->manifest_.relationship(path("/"), xlnt::relationship_type::office_document);
16✔
1223
    auto ws_rel = d_->manifest_.relationship(wb_rel.target().path(), ws_rel_id);
8✔
1224
    auto ws_part = d_->manifest_.canonicalize({wb_rel, ws_rel}).resolve(path("/"));
56✔
1225
    d_->manifest_.unregister_override_type(ws_part);
8✔
1226
    auto rel_id_map = d_->manifest_.unregister_relationship(wb_rel.target(), ws_rel_id);
8✔
1227
    d_->sheet_title_rel_id_map_.erase(ws.title());
8✔
1228
    d_->worksheets_.erase(match_iter);
8✔
1229

1230
    // Shift sheet title->ID mappings down as a result of manifest::unregister_relationship above.
1231
    for (auto &title_rel_id_pair : d_->sheet_title_rel_id_map_)
32✔
1232
    {
1233
        title_rel_id_pair.second = rel_id_map.count(title_rel_id_pair.second) > 0
24✔
1234
            ? rel_id_map[title_rel_id_pair.second]
19✔
1235
            : title_rel_id_pair.second;
43✔
1236
    }
1237

1238
    update_sheet_properties();
8✔
1239
}
16✔
1240

1241
worksheet workbook::create_sheet(std::size_t index)
1✔
1242
{
1243
    create_sheet();
1✔
1244

1245
    if (index != d_->worksheets_.size() - 1)
1✔
1246
    {
1247
        auto iter = d_->worksheets_.begin();
×
1248

1249
        for (std::size_t i = 0; i < index; ++i, ++iter)
×
1250
        {
1251
        }
1252

1253
        d_->worksheets_.insert(iter, d_->worksheets_.back());
×
1254
        d_->worksheets_.pop_back();
×
1255
    }
1256

1257
    return sheet_by_index(index);
1✔
1258
}
1259

1260
worksheet workbook::create_sheet_with_rel(const std::string &title, const relationship &rel)
×
1261
{
1262
    auto sheet_id = d_->worksheets_.size() + 1;
×
1263
    d_->worksheets_.push_back(detail::worksheet_impl(this, sheet_id, title));
×
1264

1265
    auto workbook_rel = d_->manifest_.relationship(path("/"), relationship_type::office_document);
×
1266
    auto sheet_absoulute_path = workbook_rel.target().path().parent().append(rel.target().path());
×
1267
    d_->manifest_.register_override_type(sheet_absoulute_path,
×
1268
        "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
1269
    auto ws_rel = d_->manifest_.register_relationship(workbook_rel.target(),
×
1270
        relationship_type::worksheet, rel.target(), target_mode::internal);
×
1271
    d_->sheet_title_rel_id_map_[title] = ws_rel;
×
1272

1273
    update_sheet_properties();
×
1274

1275
    return worksheet(&d_->worksheets_.back());
×
1276
}
×
1277

1278
workbook::iterator workbook::begin()
637✔
1279
{
1280
    return iterator(*this, 0);
637✔
1281
}
1282

1283
workbook::iterator workbook::end()
631✔
1284
{
1285
    return iterator(*this, d_->worksheets_.size());
631✔
1286
}
1287

1288
workbook::const_iterator workbook::begin() const
1,394✔
1289
{
1290
    return cbegin();
1,394✔
1291
}
1292

1293
workbook::const_iterator workbook::end() const
1,394✔
1294
{
1295
    return cend();
1,394✔
1296
}
1297

1298
workbook::const_iterator workbook::cbegin() const
1,404✔
1299
{
1300
    return const_iterator(*this, 0);
1,404✔
1301
}
1302

1303
workbook::const_iterator workbook::cend() const
1,395✔
1304
{
1305
    return const_iterator(*this, d_->worksheets_.size());
1,395✔
1306
}
1307

1308
std::vector<std::string> workbook::sheet_titles() const
675✔
1309
{
1310
    std::vector<std::string> names;
675✔
1311

1312
    for (auto ws : *this)
1,513✔
1313
    {
1314
        names.push_back(ws.title());
838✔
1315
    }
1316

1317
    return names;
675✔
1318
}
×
1319

1320
std::size_t workbook::sheet_count() const
351✔
1321
{
1322
    return d_->worksheets_.size();
351✔
1323
}
1324

1325
worksheet workbook::operator[](const std::string &name)
2✔
1326
{
1327
    return sheet_by_title(name);
2✔
1328
}
1329

1330
worksheet workbook::operator[](std::size_t index)
429✔
1331
{
1332
    return sheet_by_index(index);
429✔
1333
}
1334

1335
void workbook::clear()
247✔
1336
{
1337
    *d_ = detail::workbook_impl();
247✔
1338
    d_->stylesheet_.clear();
247✔
1339
}
247✔
1340

1341
bool workbook::operator==(const workbook &rhs) const
12✔
1342
{
1343
    return *d_ == *rhs.d_;
12✔
1344
}
1345

1346
bool workbook::operator!=(const workbook &rhs) const
3✔
1347
{
1348
    return !operator==(rhs);
3✔
1349
}
1350

1351
void workbook::swap(workbook &right)
272✔
1352
{
1353
    auto &left = *this;
272✔
1354

1355
    using std::swap;
1356
    swap(left.d_, right.d_);
272✔
1357

1358
    if (left.d_ != nullptr)
272✔
1359
    {
1360
        for (auto ws : left)
816✔
1361
        {
1362
            ws.parent(left);
272✔
1363
        }
1364

1365
        if (left.d_->stylesheet_.is_set())
272✔
1366
        {
1367
            left.d_->stylesheet_.get().parent = &left;
272✔
1368
        }
1369
    }
1370

1371
    if (right.d_ != nullptr)
272✔
1372
    {
1373
        for (auto ws : right)
3✔
1374
        {
1375
            ws.parent(right);
1✔
1376
        }
1377

1378
        if (right.d_->stylesheet_.is_set())
1✔
1379
        {
1380
            right.d_->stylesheet_.get().parent = &right;
1✔
1381
        }
1382
    }
1383
}
272✔
1384

1385
workbook &workbook::operator=(workbook other)
8✔
1386
{
1387
    swap(other);
8✔
1388
    d_->stylesheet_.get().parent = this;
8✔
1389

1390
    return *this;
8✔
1391
}
1392

1393
workbook::workbook(workbook &&other)
3✔
1394
    : workbook(nullptr)
3✔
1395
{
1396
    swap(other);
3✔
1397
}
3✔
1398

1399
workbook::workbook(const workbook &other)
2✔
1400
    : workbook()
2✔
1401
{
1402
    *d_.get() = *other.d_.get();
2✔
1403

1404
    for (auto ws : *this)
10✔
1405
    {
1406
        ws.parent(*this);
4✔
1407
    }
1408

1409
    d_->stylesheet_.get().parent = this;
2✔
1410
}
2✔
1411

1412
workbook::~workbook() = default;
534✔
1413

1414
bool workbook::has_theme() const
1✔
1415
{
1416
    return d_->theme_.is_set();
1✔
1417
}
1418

1419
const theme &workbook::theme() const
×
1420
{
1421
    return d_->theme_.get();
×
1422
}
1423

1424
void workbook::theme(const class theme &value)
361✔
1425
{
1426
    d_->theme_ = value;
361✔
1427
    register_workbook_part(relationship_type::theme);
361✔
1428
}
361✔
1429

1430
std::vector<named_range> workbook::named_ranges() const
88✔
1431
{
1432
    std::vector<xlnt::named_range> named_ranges;
88✔
1433

1434
    for (auto ws : *this)
342✔
1435
    {
1436
        for (auto &ws_named_range : ws.d_->named_ranges_)
127✔
1437
        {
1438
            named_ranges.push_back(ws_named_range.second);
×
1439
        }
1440
    }
1441

1442
    return named_ranges;
88✔
1443
}
×
1444

1445
format workbook::create_format(bool default_format)
10,556✔
1446
{
1447
    register_workbook_part(relationship_type::stylesheet);
10,556✔
1448
    return d_->stylesheet_.get().create_format(default_format);
10,556✔
1449
}
1450

1451
bool workbook::has_style(const std::string &name) const
256✔
1452
{
1453
    return d_->stylesheet_.get().has_style(name);
256✔
1454
}
1455

1456
void workbook::clear_styles()
1✔
1457
{
1458
    apply_to_cells([](cell c) { c.clear_style(); });
2✔
1459
}
1✔
1460

1461
void workbook::default_slicer_style(const std::string &value)
2✔
1462
{
1463
    d_->stylesheet_.get().default_slicer_style = value;
2✔
1464
}
2✔
1465

1466
std::string workbook::default_slicer_style() const
×
1467
{
1468
    return d_->stylesheet_.get().default_slicer_style.get();
×
1469
}
1470

1471
void workbook::enable_known_fonts()
51✔
1472
{
1473
    d_->stylesheet_.get().known_fonts_enabled = true;
51✔
1474
}
51✔
1475

1476
void workbook::disable_known_fonts()
×
1477
{
1478
    d_->stylesheet_.get().known_fonts_enabled = false;
×
1479
}
×
1480

1481
bool workbook::known_fonts_enabled() const
×
1482
{
1483
    return d_->stylesheet_.get().known_fonts_enabled;
×
1484
}
1485

1486
void workbook::clear_formats()
1✔
1487
{
1488
    apply_to_cells([](cell c) { c.clear_format(); });
2✔
1489
}
1✔
1490

1491
void workbook::apply_to_cells(std::function<void(cell)> f)
2✔
1492
{
1493
    for (auto ws : *this)
6✔
1494
    {
1495
        for (auto row = ws.lowest_row(); row <= ws.highest_row(); ++row)
4✔
1496
        {
1497
            for (auto column = ws.lowest_column(); column <= ws.highest_column(); ++column)
6✔
1498
            {
1499
                if (ws.has_cell(cell_reference(column, row)))
2✔
1500
                {
1501
                    f.operator()(ws.cell(cell_reference(column, row)));
2✔
1502
                }
1503
            }
1504
        }
1505
    }
1506
}
2✔
1507

1508
format workbook::format(std::size_t format_index)
10,875✔
1509
{
1510
    return d_->stylesheet_.get().format(format_index);
10,875✔
1511
}
1512

1513
const format workbook::format(std::size_t format_index) const
×
1514
{
1515
    return d_->stylesheet_.get().format(format_index);
×
1516
}
1517

1518
manifest &workbook::manifest()
139,178✔
1519
{
1520
    return d_->manifest_;
139,178✔
1521
}
1522

1523
const manifest &workbook::manifest() const
1,680✔
1524
{
1525
    return d_->manifest_;
1,680✔
1526
}
1527

1528
const rich_text &workbook::shared_strings(std::size_t index) const
10,201✔
1529
{
1530
    if (index < d_->shared_strings_values_.size())
10,201✔
1531
    {
1532
        return d_->shared_strings_values_.at(index);
10,201✔
1533
    }
1534

1535
    static rich_text empty;
×
1536
    return empty;
×
1537
}
1538

1539
std::vector<rich_text> &workbook::shared_strings()
×
1540
{
1541
    return d_->shared_strings_values_;
×
1542
}
1543

1544
const std::vector<rich_text> &workbook::shared_strings() const
108✔
1545
{
1546
    return d_->shared_strings_values_;
108✔
1547
}
1548

1549
std::size_t workbook::add_shared_string(const rich_text &shared, bool allow_duplicates)
31,013✔
1550
{
1551
    register_workbook_part(relationship_type::shared_string_table);
31,013✔
1552

1553
    if (!allow_duplicates)
31,013✔
1554
    {
1555
        auto it = d_->shared_strings_ids_.find(shared);
20,293✔
1556

1557
        if (it != d_->shared_strings_ids_.end())
20,293✔
1558
        {
1559
            return it->second;
9,904✔
1560
        }
1561
    }
1562

1563
    auto sz = d_->shared_strings_ids_.size();
21,109✔
1564
    d_->shared_strings_ids_[shared] = sz;
21,109✔
1565
    d_->shared_strings_values_.push_back(shared);
21,109✔
1566

1567
    return sz;
21,109✔
1568
}
1569

1570
bool workbook::contains(const std::string &sheet_title) const
388✔
1571
{
1572
    for (auto ws : *this)
548✔
1573
    {
1574
        if (ws.title() == sheet_title) return true;
145✔
1575
    }
1576

1577
    return false;
323✔
1578
}
1579

1580
void workbook::thumbnail(const std::vector<std::uint8_t> &thumbnail,
266✔
1581
    const std::string &extension, const std::string &content_type)
1582
{
1583
    if (!d_->manifest_.has_relationship(path("/"), relationship_type::thumbnail))
798✔
1584
    {
1585
        d_->manifest_.register_default_type(extension, content_type);
266✔
1586
        d_->manifest_.register_relationship(uri("/"), relationship_type::thumbnail,
532✔
1587
            uri("docProps/thumbnail.jpeg"), target_mode::internal);
798✔
1588
    }
1589

1590
    auto thumbnail_rel = d_->manifest_.relationship(path("/"), relationship_type::thumbnail);
532✔
1591
    d_->images_[thumbnail_rel.target().to_string()] = thumbnail;
266✔
1592
}
266✔
1593

1594
const std::vector<std::uint8_t> &workbook::thumbnail() const
×
1595
{
1596
    auto thumbnail_rel = d_->manifest_.relationship(path("/"), relationship_type::thumbnail);
×
1597
    return d_->images_.at(thumbnail_rel.target().to_string());
×
1598
}
×
1599

1600
const std::unordered_map<std::string, std::vector<std::uint8_t>> &workbook::binaries() const
×
1601
{
1602
    return d_->binaries_;
×
1603
}
1604

1605
style workbook::create_style(const std::string &name)
17✔
1606
{
1607
    return d_->stylesheet_.get().create_style(name);
17✔
1608
}
1609

1610
style workbook::create_builtin_style(const std::size_t builtin_id)
267✔
1611
{
1612
    return d_->stylesheet_.get().create_builtin_style(builtin_id);
267✔
1613
}
1614

1615
style workbook::style(const std::string &name)
259✔
1616
{
1617
    return d_->stylesheet_.get().style(name);
259✔
1618
}
1619

1620
const style workbook::style(const std::string &name) const
1✔
1621
{
1622
    return d_->stylesheet_.get().style(name);
1✔
1623
}
1624

1625
calendar workbook::base_date() const
102✔
1626
{
1627
    return d_->base_date_;
102✔
1628
}
1629

1630
void workbook::base_date(calendar base_date)
111✔
1631
{
1632
    d_->base_date_ = base_date;
111✔
1633
}
111✔
1634

1635
bool workbook::has_title() const
×
1636
{
1637
    return d_->title_.is_set();
×
1638
}
1639

1640
std::string workbook::title() const
×
1641
{
1642
    return d_->title_.get();
×
1643
}
1644

1645
void workbook::title(const std::string &title)
×
1646
{
1647
    d_->title_ = title;
×
1648
}
×
1649

1650
detail::workbook_impl &workbook::impl()
208✔
1651
{
1652
    return *d_;
208✔
1653
}
1654

1655
const detail::workbook_impl &workbook::impl() const
338✔
1656
{
1657
    return *d_;
338✔
1658
}
1659

1660
bool workbook::has_view() const
192✔
1661
{
1662
    return d_->view_.is_set();
192✔
1663
}
1664

1665
workbook_view workbook::view() const
188✔
1666
{
1667
    if (!d_->view_.is_set())
188✔
1668
    {
1669
        throw invalid_attribute();
×
1670
    }
1671

1672
    return d_->view_.get();
188✔
1673
}
1674

1675
void workbook::view(const workbook_view &view)
365✔
1676
{
1677
    d_->view_ = view;
365✔
1678
}
365✔
1679

1680
bool workbook::has_code_name() const
88✔
1681
{
1682
    return d_->code_name_.is_set();
88✔
1683
}
1684

1685
std::string workbook::code_name() const
×
1686
{
1687
    if (has_code_name())
×
1688
    {
1689
        throw invalid_attribute();
×
1690
    }
1691

1692
    return d_->code_name_.get();
×
1693
}
1694

1695
void workbook::code_name(const std::string &code_name)
×
1696
{
1697
    d_->code_name_ = code_name;
×
1698
}
×
1699

1700
bool workbook::has_file_version() const
88✔
1701
{
1702
    return d_->file_version_.is_set();
88✔
1703
}
1704

1705
std::string workbook::app_name() const
84✔
1706
{
1707
    return d_->file_version_.get().app_name;
84✔
1708
}
1709

1710
std::size_t workbook::last_edited() const
84✔
1711
{
1712
    return d_->file_version_.get().last_edited;
84✔
1713
}
1714

1715
std::size_t workbook::lowest_edited() const
84✔
1716
{
1717
    return d_->file_version_.get().lowest_edited;
84✔
1718
}
1719

1720
std::size_t workbook::rup_build() const
84✔
1721
{
1722
    return d_->file_version_.get().rup_build;
84✔
1723
}
1724

1725
bool workbook::has_calculation_properties() const
88✔
1726
{
1727
    return d_->calculation_properties_.is_set();
88✔
1728
}
1729

1730
class calculation_properties workbook::calculation_properties() const
170✔
1731
{
1732
    return d_->calculation_properties_.get();
170✔
1733
}
1734

1735
void workbook::calculation_properties(const class calculation_properties &props)
363✔
1736
{
1737
    d_->calculation_properties_ = props;
363✔
1738
}
363✔
1739

1740
void workbook::garbage_collect_formulae()
18✔
1741
{
1742
    auto any_with_formula = false;
18✔
1743

1744
    for (auto ws : *this)
36✔
1745
    {
1746
        for (auto row : ws.rows(true))
104✔
1747
        {
1748
            for (auto cell : row)
512✔
1749
            {
1750
                if (cell.has_formula())
426✔
1751
                {
1752
                    any_with_formula = true;
136✔
1753
                }
1754
            }
1755
        }
18✔
1756
    }
1757

1758
    if (any_with_formula) return;
18✔
1759

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

1762
    if (manifest().has_relationship(wb_rel.target().path(), relationship_type::calculation_chain))
2✔
1763
    {
1764
        auto calc_chain_rel = manifest().relationship(wb_rel.target().path(), relationship_type::calculation_chain);
2✔
1765
        auto calc_chain_part = manifest().canonicalize({wb_rel, calc_chain_rel});
10✔
1766
        manifest().unregister_override_type(calc_chain_part);
2✔
1767
        manifest().unregister_relationship(wb_rel.target(), calc_chain_rel.id());
2✔
1768
    }
2✔
1769
}
4✔
1770

1771
void workbook::update_sheet_properties()
342✔
1772
{
1773
    if (has_extended_property(xlnt::extended_property::titles_of_parts))
342✔
1774
    {
1775
        extended_property(xlnt::extended_property::titles_of_parts, sheet_titles());
342✔
1776
    }
1777

1778
    if (has_extended_property(xlnt::extended_property::heading_pairs))
342✔
1779
    {
1780
        extended_property(xlnt::extended_property::heading_pairs,
342✔
1781
            std::vector<variant>{variant("Worksheets"), variant(static_cast<int>(sheet_count()))});
1,710✔
1782
    }
1783
}
684✔
1784

1785
namespace {
1786
// true if a sheet index is != worksheet relationship index
1787
bool needs_reorder(const std::unordered_map<std::string, std::string> &title_to_rels,
322✔
1788
    const std::vector<std::string> &titles,
1789
    std::vector<std::string> &relation_ids)
1790
{
1791
    bool all_match = true;
322✔
1792
    for (std::size_t title_index = 0; title_index < titles.size(); ++title_index)
712✔
1793
    {
1794
        const auto &rel = title_to_rels.at(titles[title_index]);
390✔
1795
        relation_ids.push_back(rel);
390✔
1796
        const auto expected_rel_id = "rId" + std::to_string(title_index + 1);
390✔
1797
        if (rel != expected_rel_id)
390✔
1798
        {
1799
            all_match = false;
57✔
1800
        }
1801
    }
390✔
1802
    return !all_match; // if all are as expected, reorder not required
322✔
1803
};
1804

1805
struct rel_id_sorter
1806
{
1807
    // true if lhs < rhs
1808
    bool operator()(const xlnt::relationship &lhs, const xlnt::relationship &rhs)
325✔
1809
    {
1810
        // format is rTd<decimal number 1..n>
1811
        if (lhs.id().size() != rhs.id().size()) // a number with more digits will be larger
325✔
1812
        {
1813
            return lhs.id().size() < rhs.id().size();
×
1814
        }
1815
        return lhs.id() < rhs.id();
325✔
1816
    }
1817
};
1818
} // namespace
1819

1820
void workbook::reorder_relationships()
322✔
1821
{
1822
    const auto titles = sheet_titles();
322✔
1823
    // the relation ID corresponding to the title at the same index is copied into here
1824
    std::vector<std::string> worksheet_rel_ids;
322✔
1825
    worksheet_rel_ids.reserve(titles.size());
322✔
1826
    if (!needs_reorder(d_->sheet_title_rel_id_map_, titles, worksheet_rel_ids))
322✔
1827
    {
1828
        return;
266✔
1829
    }
1830
    // copy of existing relations
1831
    const auto wb_rel_target = manifest().relationship(path("/"), relationship_type::office_document).target();
112✔
1832
    auto rel_copy = manifest().relationships(wb_rel_target.path());
56✔
1833
    std::sort(rel_copy.begin(), rel_copy.end(), rel_id_sorter{});
56✔
1834
    // clear existing relations
1835
    for (const auto &rel : rel_copy)
295✔
1836
    {
1837
        manifest().unregister_relationship(wb_rel_target, rel.id());
239✔
1838
    }
1839
    // create new relations
1840
    std::size_t index = 0;
56✔
1841
    auto new_id = [&index]() { return "rId" + std::to_string(++index); }; // ids start from 1
239✔
1842
    // worksheets first
1843
    while (index < worksheet_rel_ids.size())
180✔
1844
    {
1845
        auto rel_it = std::find_if(rel_copy.begin(), rel_copy.end(),
124✔
1846
            [&](const relationship &rel) { return rel.id() == worksheet_rel_ids[index]; });
323✔
1847

1848
        std::string rel_id = new_id();
124✔
1849
        d_->sheet_title_rel_id_map_.at(titles[index - 1]) = rel_id; // update title -> relation mapping
124✔
1850
        manifest().register_relationship(relationship(rel_id, rel_it->type(),
248✔
1851
            rel_it->source(), rel_it->target(), rel_it->target_mode()));
124✔
1852
    }
124✔
1853
    // then all the other relations in the same order they started (just new indices)
1854
    for (const auto &old_rel : rel_copy)
295✔
1855
    {
1856
        if (old_rel.type() == relationship_type::worksheet)
239✔
1857
        {
1858
            continue;
124✔
1859
        }
1860
        manifest().register_relationship(relationship(new_id(), old_rel.type(),
230✔
1861
            old_rel.source(), old_rel.target(), old_rel.target_mode()));
115✔
1862
    }
1863
}
588✔
1864

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