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

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

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

push

circleci

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

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

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

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

11395 of 13900 relevant lines covered (81.98%)

1202684.1 hits per line

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

91.7
./source/cell/cell.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 <cmath>
28
#include <sstream>
29

30
#include <xlnt/cell/cell.hpp>
31
#include <xlnt/cell/cell_reference.hpp>
32
#include <xlnt/cell/comment.hpp>
33
#include <xlnt/cell/hyperlink.hpp>
34
#include <xlnt/cell/rich_text.hpp>
35
#include <xlnt/packaging/manifest.hpp>
36
#include <xlnt/packaging/relationship.hpp>
37
#include <xlnt/styles/alignment.hpp>
38
#include <xlnt/styles/border.hpp>
39
#include <xlnt/styles/color.hpp>
40
#include <xlnt/styles/fill.hpp>
41
#include <xlnt/styles/font.hpp>
42
#include <xlnt/styles/format.hpp>
43
#include <xlnt/styles/number_format.hpp>
44
#include <xlnt/styles/protection.hpp>
45
#include <xlnt/styles/style.hpp>
46
#include <xlnt/utils/date.hpp>
47
#include <xlnt/utils/datetime.hpp>
48
#include <xlnt/utils/exceptions.hpp>
49
#include <xlnt/utils/time.hpp>
50
#include <xlnt/utils/timedelta.hpp>
51
#include <xlnt/workbook/workbook.hpp>
52
#include <xlnt/worksheet/column_properties.hpp>
53
#include <xlnt/worksheet/phonetic_pr.hpp>
54
#include <xlnt/worksheet/row_properties.hpp>
55
#include <xlnt/worksheet/worksheet.hpp>
56
#include <detail/implementations/cell_impl.hpp>
57
#include <detail/implementations/format_impl.hpp>
58
#include <detail/implementations/hyperlink_impl.hpp>
59
#include <detail/implementations/stylesheet.hpp>
60
#include <detail/implementations/worksheet_impl.hpp>
61
#include <detail/serialization/serialisation_helpers.hpp>
62

63
namespace {
64

65
std::pair<bool, double> cast_numeric(const std::string &s)
11✔
66
{
67
    size_t len_convert = 0;
11✔
68
    double result = xlnt::detail::deserialise(s, &len_convert);
11✔
69
    return (len_convert != s.size())
11✔
70
        ? std::make_pair(false, 0.0)
11✔
71
        : std::make_pair(true, result);
11✔
72
}
73

74
std::pair<bool, double> cast_percentage(const std::string &s)
14✔
75
{
76
    if (s.back() == '%')
14✔
77
    {
78
        auto number = cast_numeric(s.substr(0, s.size() - 1));
1✔
79

80
        if (number.first)
1✔
81
        {
82
            return {true, number.second / 100};
1✔
83
        }
84
    }
85

86
    return {false, 0.0};
13✔
87
}
88

89
std::pair<bool, xlnt::time> cast_time(const std::string &s)
13✔
90
{
91
    xlnt::time result;
13✔
92

93
    std::vector<std::string> time_components;
13✔
94
    std::size_t prev = 0;
13✔
95
    auto colon_index = s.find(':');
13✔
96

97
    while (colon_index != std::string::npos)
18✔
98
    {
99
        time_components.push_back(s.substr(prev, colon_index - prev));
5✔
100
        prev = colon_index + 1;
5✔
101
        colon_index = s.find(':', colon_index + 1);
5✔
102
    }
103

104
    time_components.push_back(s.substr(prev, colon_index - prev));
13✔
105

106
    if (time_components.size() < 2 || time_components.size() > 3)
13✔
107
    {
108
        return {false, result};
9✔
109
    }
110

111
    std::vector<double> numeric_components;
4✔
112

113
    for (const auto & component : time_components)
12✔
114
    {
115
        if (component.empty() || (component.substr(0, component.find('.')).size() > 2))
9✔
116
        {
117
            return {false, result};
1✔
118
        }
119

120
        for (auto d : component)
34✔
121
        {
122
            if (!(d >= '0' && d <= '9') && d != '.')
26✔
123
            {
124
                return {false, result};
×
125
            }
126
        }
127
        auto numeric = xlnt::detail::deserialise(component);
8✔
128

129
        numeric_components.push_back(numeric);
8✔
130
    }
131

132
    result.hour = static_cast<int>(numeric_components[0]);
3✔
133
    result.minute = static_cast<int>(numeric_components[1]);
3✔
134

135
    if (std::fabs(static_cast<double>(result.minute) - numeric_components[1]) > std::numeric_limits<double>::epsilon())
3✔
136
    {
137
        result.minute = result.hour;
1✔
138
        result.hour = 0;
1✔
139
        result.second = static_cast<int>(numeric_components[1]);
1✔
140
        result.microsecond = static_cast<int>((numeric_components[1] - result.second) * 1E6);
1✔
141
    }
142
    else if (numeric_components.size() > 2)
2✔
143
    {
144
        result.second = static_cast<int>(numeric_components[2]);
1✔
145
        result.microsecond = static_cast<int>((numeric_components[2] - result.second) * 1E6);
1✔
146
    }
147

148
    return {true, result};
3✔
149
}
13✔
150

151
} // namespace
152

153
namespace xlnt {
154

155
const std::unordered_map<std::string, int> &cell::error_codes()
1✔
156
{
157
    static const auto codes = std::unordered_map<std::string, int>{
158
        {"#NULL!", 0},
×
159
        {"#DIV/0!", 1},
×
160
        {"#VALUE!", 2},
×
161
        {"#REF!", 3},
×
162
        {"#NAME?", 4},
×
163
        {"#NUM!", 5},
×
164
        {"#N/A!", 6}};
10✔
165

166
    return codes;
1✔
167
}
1✔
168

169
std::string cell::check_string(const std::string &to_check)
40,615✔
170
{
171
    // so we can modify it
172
    std::string s = to_check;
40,615✔
173

174
    if (s.size() == 0)
40,615✔
175
    {
176
        return s;
2✔
177
    }
178
    else if (s.size() > 32767)
40,613✔
179
    {
180
        s = s.substr(0, 32767); // max string length in Excel
1✔
181
    }
182

183
    for (char c : s)
426,637✔
184
    {
185
        if (c >= 0 && (c <= 8 || c == 11 || c == 12 || (c >= 14 && c <= 31)))
386,053✔
186
        {
187
            throw illegal_character(c);
29✔
188
        }
189
    }
190

191
    return s;
40,584✔
192
}
29✔
193

194
cell::cell(detail::cell_impl *d)
14,453,782✔
195
    : d_(d)
14,453,782✔
196
{
197
}
14,453,782✔
198

199
bool cell::garbage_collectible() const
14,221,216✔
200
{
201
    return d_->is_garbage_collectible();
14,221,216✔
202
}
203

204
void cell::value(std::nullptr_t)
1✔
205
{
206
    clear_value();
1✔
207
}
1✔
208

209
void cell::value(bool boolean_value)
10✔
210
{
211
    d_->type_ = type::boolean;
10✔
212
    d_->value_numeric_ = boolean_value ? 1.0 : 0.0;
10✔
213
}
10✔
214

215
void cell::value(int int_value)
200,015✔
216
{
217
    d_->value_numeric_ = static_cast<double>(int_value);
200,015✔
218
    d_->type_ = type::number;
200,015✔
219
}
200,015✔
220

221
void cell::value(unsigned int int_value)
2✔
222
{
223
    d_->value_numeric_ = static_cast<double>(int_value);
2✔
224
    d_->type_ = type::number;
2✔
225
}
2✔
226

227
void cell::value(long long int int_value)
2✔
228
{
229
    d_->value_numeric_ = static_cast<double>(int_value);
2✔
230
    d_->type_ = type::number;
2✔
231
}
2✔
232

233
void cell::value(unsigned long long int int_value)
2✔
234
{
235
    d_->value_numeric_ = static_cast<double>(int_value);
2✔
236
    d_->type_ = type::number;
2✔
237
}
2✔
238

239
void cell::value(float float_value)
2✔
240
{
241
    d_->value_numeric_ = static_cast<double>(float_value);
2✔
242
    d_->type_ = type::number;
2✔
243
}
2✔
244

245
void cell::value(double float_value)
7✔
246
{
247
    d_->value_numeric_ = static_cast<double>(float_value);
7✔
248
    d_->type_ = type::number;
7✔
249
}
7✔
250

251
void cell::value(const std::string &s)
20,322✔
252
{
253
    value(rich_text(check_string(s)));
20,322✔
254
}
20,293✔
255

256
void cell::value(const rich_text &text)
20,293✔
257
{
258
    check_string(text.plain_text());
20,293✔
259

260
    d_->type_ = type::shared_string;
20,293✔
261
    d_->value_numeric_ = static_cast<double>(workbook().add_shared_string(text));
20,293✔
262
}
20,293✔
263

264
void cell::value(const char *c)
121✔
265
{
266
    value(std::string(c));
121✔
267
}
121✔
268

269
void cell::value(const cell c)
1✔
270
{
271
    d_->type_ = c.d_->type_;
1✔
272
    d_->value_numeric_ = c.d_->value_numeric_;
1✔
273
    d_->value_text_ = c.d_->value_text_;
1✔
274
    d_->hyperlink_ = c.d_->hyperlink_;
1✔
275
    d_->formula_ = c.d_->formula_;
1✔
276
    d_->format_ = c.d_->format_;
1✔
277
}
1✔
278

279
void cell::value(const date &d)
3✔
280
{
281
    d_->type_ = type::number;
3✔
282
    d_->value_numeric_ = d.to_number(base_date());
3✔
283
    number_format(number_format::date_yyyymmdd2());
3✔
284
}
3✔
285

286
void cell::value(const datetime &d)
5✔
287
{
288
    d_->type_ = type::number;
5✔
289
    d_->value_numeric_ = d.to_number(base_date());
5✔
290
    number_format(number_format::date_datetime());
5✔
291
}
5✔
292

293
void cell::value(const time &t)
2✔
294
{
295
    d_->type_ = type::number;
2✔
296
    d_->value_numeric_ = t.to_number();
2✔
297
    number_format(number_format::date_time6());
2✔
298
}
2✔
299

300
void cell::value(const timedelta &t)
2✔
301
{
302
    d_->type_ = type::number;
2✔
303
    d_->value_numeric_ = t.to_number();
2✔
304
    number_format(xlnt::number_format("[hh]:mm:ss"));
2✔
305
}
2✔
306

307
row_t cell::row() const
7✔
308
{
309
    return d_->row_;
7✔
310
}
311

312
column_t cell::column() const
39✔
313
{
314
    return d_->column_;
39✔
315
}
316

317
column_t::index_t cell::column_index() const
1✔
318
{
319
    return d_->column_.index;
1✔
320
}
321

322
void cell::merged(bool merged)
776✔
323
{
324
    d_->is_merged_ = merged;
776✔
325
}
776✔
326

327
bool cell::is_merged() const
×
328
{
329
    return d_->is_merged_;
×
330
}
331

332
bool cell::phonetics_visible() const
14,221,223✔
333
{
334
    return d_->phonetics_visible_;
14,221,223✔
335
}
336

337
void cell::show_phonetics(bool phonetics)
2✔
338
{
339
    d_->phonetics_visible_ = phonetics;
2✔
340
}
2✔
341

342
bool cell::is_date() const
7✔
343
{
344
    return data_type() == type::number
7✔
345
        && has_format()
4✔
346
        && number_format().is_date_format();
11✔
347
}
348

349
cell_reference cell::reference() const
14,221,383✔
350
{
351
    return {d_->column_, d_->row_};
14,221,383✔
352
}
353

354
bool cell::operator==(const cell &comparand) const
15✔
355
{
356
    return d_ == comparand.d_;
15✔
357
}
358

359
bool cell::operator!=(const cell &comparand) const
2✔
360
{
361
    return d_ != comparand.d_;
2✔
362
}
363

364
cell &cell::operator=(const cell &rhs) = default;
1✔
365

366
hyperlink cell::hyperlink() const
62✔
367
{
368
    return xlnt::hyperlink(&d_->hyperlink_.get());
62✔
369
}
370

371
void cell::hyperlink(const std::string &url, const std::string &display)
38✔
372
{
373
    if (url.empty())
38✔
374
    {
375
        throw invalid_parameter();
1✔
376
    }
377

378
    auto ws = worksheet();
37✔
379
    auto &manifest = ws.workbook().manifest();
37✔
380

381
    d_->hyperlink_ = detail::hyperlink_impl();
37✔
382

383
    // check for existing relationships
384
    auto relationships = manifest.relationships(ws.path(), relationship_type::hyperlink);
37✔
385
    auto relation = std::find_if(relationships.cbegin(), relationships.cend(),
37✔
386
        [&url](xlnt::relationship rel) { return rel.target().path().string() == url; });
57✔
387
    if (relation != relationships.end())
37✔
388
    {
389
        d_->hyperlink_.get().relationship = *relation;
30✔
390
    }
391
    else
392
    { // register a new relationship
393
        auto rel_id = manifest.register_relationship(
394
            uri(ws.path().string()),
14✔
395
            relationship_type::hyperlink,
396
            uri(url),
14✔
397
            target_mode::external);
7✔
398
        // TODO: make manifest::register_relationship return the created relationship instead of rel id
399
        d_->hyperlink_.get().relationship = manifest.relationship(ws.path(), rel_id);
7✔
400
    }
7✔
401
    // if a value is already present, the display string is ignored
402
    if (has_value())
37✔
403
    {
404
        d_->hyperlink_.get().display.set(to_string());
31✔
405
    }
406
    else
407
    {
408
        d_->hyperlink_.get().display.set(display.empty() ? url : display);
6✔
409
        value(hyperlink().display());
6✔
410
    }
411
}
37✔
412

413
void cell::hyperlink(xlnt::cell target, const std::string &display)
4✔
414
{
415
    // TODO: should this computed value be a method on a cell?
416
    const auto cell_address = target.worksheet().title() + "!" + target.reference().to_string();
4✔
417

418
    d_->hyperlink_ = detail::hyperlink_impl();
4✔
419
    d_->hyperlink_.get().relationship = xlnt::relationship("", relationship_type::hyperlink,
8✔
420
        uri(""), uri(cell_address), target_mode::internal);
28✔
421
    // if a value is already present, the display string is ignored
422
    if (has_value())
4✔
423
    {
424
        d_->hyperlink_.get().display.set(to_string());
1✔
425
    }
426
    else
427
    {
428
        d_->hyperlink_.get().display.set(display.empty() ? cell_address : display);
3✔
429
        value(hyperlink().display());
3✔
430
    }
431
}
4✔
432

433
void cell::hyperlink(xlnt::range target, const std::string &display)
3✔
434
{
435
    // TODO: should this computed value be a method on a cell?
436
    const auto range_address = target.target_worksheet().title() + "!" + target.reference().to_string();
3✔
437

438
    d_->hyperlink_ = detail::hyperlink_impl();
3✔
439
    d_->hyperlink_.get().relationship = xlnt::relationship("", relationship_type::hyperlink,
6✔
440
        uri(""), uri(range_address), target_mode::internal);
21✔
441

442
    // if a value is already present, the display string is ignored
443
    if (has_value())
3✔
444
    {
445
        d_->hyperlink_.get().display.set(to_string());
1✔
446
    }
447
    else
448
    {
449
        d_->hyperlink_.get().display.set(display.empty() ? range_address : display);
2✔
450
        value(hyperlink().display());
2✔
451
    }
452
}
3✔
453

454
void cell::formula(const std::string &formula)
18✔
455
{
456
    if (formula.empty())
18✔
457
    {
458
        return clear_formula();
1✔
459
    }
460

461
    if (formula[0] == '=')
17✔
462
    {
463
        d_->formula_ = formula.substr(1);
8✔
464
    }
465
    else
466
    {
467
        d_->formula_ = formula;
9✔
468
    }
469

470
    worksheet().register_calc_chain_in_manifest();
17✔
471
}
472

473
bool cell::has_formula() const
14,222,467✔
474
{
475
    return d_->formula_.is_set();
14,222,467✔
476
}
477

478
std::string cell::formula() const
19✔
479
{
480
    return d_->formula_.get();
19✔
481
}
482

483
void cell::clear_formula()
795✔
484
{
485
    if (has_formula())
795✔
486
    {
487
        d_->formula_.clear();
18✔
488
        worksheet().garbage_collect_formulae();
18✔
489
    }
490
}
795✔
491

492
std::string cell::error() const
21✔
493
{
494
    if (d_->type_ != type::error)
21✔
495
    {
496
        throw xlnt::exception("called error() when cell type is not error");
21✔
497
    }
498
    return value<std::string>();
14✔
499
}
500

501
void cell::error(const std::string &error)
17✔
502
{
503
    if (error.length() == 0 || error[0] != '#')
17✔
504
    {
505
        throw invalid_data_type();
2✔
506
    }
507

508
    d_->value_text_.plain_text(error, false);
15✔
509
    d_->type_ = type::error;
15✔
510
}
15✔
511

512
cell cell::offset(int column, int row)
1✔
513
{
514
    return worksheet().cell(reference().make_offset(column, row));
1✔
515
}
516

517
worksheet cell::worksheet()
20,966✔
518
{
519
    return xlnt::worksheet(d_->parent_);
20,966✔
520
}
521

522
const worksheet cell::worksheet() const
10,265✔
523
{
524
    return xlnt::worksheet(d_->parent_);
10,265✔
525
}
526

527
workbook &cell::workbook()
20,855✔
528
{
529
    return worksheet().workbook();
20,855✔
530
}
531

532
const workbook &cell::workbook() const
10,215✔
533
{
534
    return worksheet().workbook();
10,215✔
535
}
536

537
std::pair<int, int> cell::anchor() const
35✔
538
{
539
    double left = 0;
35✔
540

541
    for (column_t column_index = 1; column_index <= d_->column_ - 1; column_index++)
35✔
542
    {
543
        left += worksheet().column_width(column_index);
×
544
    }
545

546
    double top = 0;
35✔
547

548
    for (row_t row_index = 1; row_index <= d_->row_ - 1; row_index++)
51✔
549
    {
550
        top += worksheet().row_height(row_index);
16✔
551
    }
552

553
    return {static_cast<int>(left), static_cast<int>(top)};
35✔
554
}
555

556
cell::type cell::data_type() const
28,453,510✔
557
{
558
    return d_->type_;
28,453,510✔
559
}
560

561
void cell::data_type(type t)
151✔
562
{
563
    d_->type_ = t;
151✔
564
}
151✔
565

566
number_format cell::computed_number_format() const
60✔
567
{
568
    return xlnt::number_format();
60✔
569
}
570

571
font cell::computed_font() const
×
572
{
573
    return xlnt::font();
×
574
}
575

576
fill cell::computed_fill() const
×
577
{
578
    return xlnt::fill();
×
579
}
580

581
border cell::computed_border() const
×
582
{
583
    return xlnt::border();
×
584
}
585

586
alignment cell::computed_alignment() const
×
587
{
588
    return xlnt::alignment();
×
589
}
590

591
protection cell::computed_protection() const
×
592
{
593
    return xlnt::protection();
×
594
}
595

596
void cell::clear_value()
768✔
597
{
598
    d_->value_numeric_ = 0;
768✔
599
    d_->value_text_.clear();
768✔
600
    d_->type_ = cell::type::empty;
768✔
601
    clear_formula();
768✔
602
}
768✔
603

604
template <>
605
XLNT_API bool cell::value() const
7✔
606
{
607
    return d_->value_numeric_ != 0.0;
7✔
608
}
609

610
template <>
611
XLNT_API int cell::value() const
24✔
612
{
613
    return static_cast<int>(d_->value_numeric_);
24✔
614
}
615

616
template <>
617
XLNT_API long long int cell::value() const
1✔
618
{
619
    return static_cast<long long int>(d_->value_numeric_);
1✔
620
}
621

622
template <>
623
XLNT_API unsigned int cell::value() const
1✔
624
{
625
    return static_cast<unsigned int>(d_->value_numeric_);
1✔
626
}
627

628
template <>
629
XLNT_API unsigned long long cell::value() const
1✔
630
{
631
    return static_cast<unsigned long long>(d_->value_numeric_);
1✔
632
}
633

634
template <>
635
XLNT_API float cell::value() const
1✔
636
{
637
    return static_cast<float>(d_->value_numeric_);
1✔
638
}
639

640
template <>
641
XLNT_API double cell::value() const
14,200,073✔
642
{
643
    return static_cast<double>(d_->value_numeric_);
14,200,073✔
644
}
645

646
template <>
647
XLNT_API time cell::value() const
3✔
648
{
649
    return time::from_number(d_->value_numeric_);
3✔
650
}
651

652
template <>
653
XLNT_API datetime cell::value() const
×
654
{
655
    return datetime::from_number(d_->value_numeric_, base_date());
×
656
}
657

658
template <>
659
XLNT_API date cell::value() const
×
660
{
661
    return date::from_number(static_cast<int>(d_->value_numeric_), base_date());
×
662
}
663

664
template <>
665
XLNT_API timedelta cell::value() const
×
666
{
667
    return timedelta::from_number(d_->value_numeric_);
×
668
}
669

670
void cell::alignment(const class alignment &alignment_)
1✔
671
{
672
    auto new_format = has_format() ? modifiable_format() : workbook().create_format();
1✔
673
    format(new_format.alignment(alignment_, optional<bool>(true)));
1✔
674
}
1✔
675

676
void cell::border(const class border &border_)
1✔
677
{
678
    auto new_format = has_format() ? modifiable_format() : workbook().create_format();
1✔
679
    format(new_format.border(border_, optional<bool>(true)));
1✔
680
}
1✔
681

682
void cell::fill(const class fill &fill_)
3✔
683
{
684
    auto new_format = has_format() ? modifiable_format() : workbook().create_format();
3✔
685
    format(new_format.fill(fill_, optional<bool>(true)));
3✔
686
}
3✔
687

688
void cell::font(const class font &font_)
23✔
689
{
690
    auto new_format = has_format() ? modifiable_format() : workbook().create_format();
23✔
691
    format(new_format.font(font_, optional<bool>(true)));
23✔
692
}
23✔
693

694
void cell::number_format(const class number_format &number_format_)
22✔
695
{
696
    auto new_format = has_format() ? modifiable_format() : workbook().create_format();
22✔
697
    format(new_format.number_format(number_format_, optional<bool>(true)));
22✔
698
}
22✔
699

700
void cell::protection(const class protection &protection_)
1✔
701
{
702
    auto new_format = has_format() ? modifiable_format() : workbook().create_format();
1✔
703
    format(new_format.protection(protection_, optional<bool>(true)));
1✔
704
}
1✔
705

706
template <>
707
XLNT_API std::string cell::value() const
10,223✔
708
{
709
    return value<rich_text>().plain_text();
10,223✔
710
}
711

712
template <>
713
XLNT_API rich_text cell::value() const
10,226✔
714
{
715
    if (data_type() == cell::type::shared_string)
10,226✔
716
    {
717
        return workbook().shared_strings(static_cast<std::size_t>(d_->value_numeric_));
10,201✔
718
    }
719

720
    return d_->value_text_;
25✔
721
}
722

723
bool cell::has_value() const
112✔
724
{
725
    return d_->type_ != cell::type::empty;
112✔
726
}
727

728
std::string cell::to_string() const
60✔
729
{
730
    auto nf = computed_number_format();
60✔
731

732
    switch (data_type())
60✔
733
    {
734
    case cell::type::empty:
2✔
735
        return "";
4✔
736
    case cell::type::date:
6✔
737
    case cell::type::number:
738
        return nf.format(value<double>(), base_date());
6✔
739
    case cell::type::inline_string:
48✔
740
    case cell::type::shared_string:
741
    case cell::type::formula_string:
742
    case cell::type::error:
743
        return nf.format(value<std::string>());
48✔
744
    case cell::type::boolean:
4✔
745
        return value<double>() == 0.0 ? "FALSE" : "TRUE";
8✔
746
    }
747

748
    return "";
×
749
}
60✔
750

751
bool cell::has_format() const
14,231,904✔
752
{
753
    return d_->format_.is_set();
14,231,904✔
754
}
755

756
void cell::format(const class format new_format)
10,349✔
757
{
758
    if (has_format())
10,349✔
759
    {
760
        format().d_->references -= format().d_->references > 0 ? 1 : 0;
8✔
761
    }
762

763
    ++new_format.d_->references;
10,349✔
764
    d_->format_ = new_format.d_;
10,349✔
765
}
10,349✔
766

767
calendar cell::base_date() const
14✔
768
{
769
    return workbook().base_date();
14✔
770
}
771

772
bool operator==(std::nullptr_t, const cell &cell)
4✔
773
{
774
    return cell.data_type() == cell::type::empty;
4✔
775
}
776

777
bool operator==(const cell &cell, std::nullptr_t)
2✔
778
{
779
    return nullptr == cell;
2✔
780
}
781

782
XLNT_API std::ostream &operator<<(std::ostream &stream, const xlnt::cell &cell)
6✔
783
{
784
    return stream << cell.to_string();
6✔
785
}
786

787
void cell::value(const std::string &value_string, bool infer_type)
23✔
788
{
789
    value(value_string);
23✔
790

791
    if (!infer_type || value_string.empty())
23✔
792
    {
793
        return;
9✔
794
    }
795

796
    if (value_string.front() == '=' && value_string.size() > 1)
23✔
797
    {
798
        formula(value_string);
2✔
799
        return;
2✔
800
    }
801

802
    if (value_string.front() == '#' && value_string.size() > 1)
21✔
803
    {
804
        error(value_string);
7✔
805
        return;
7✔
806
    }
807

808
    auto percentage = cast_percentage(value_string);
14✔
809

810
    if (percentage.first)
14✔
811
    {
812
        d_->value_numeric_ = percentage.second;
1✔
813
        d_->type_ = cell::type::number;
1✔
814
        number_format(xlnt::number_format::percentage());
1✔
815
    }
816
    else
817
    {
818
        auto time = cast_time(value_string);
13✔
819

820
        if (time.first)
13✔
821
        {
822
            d_->type_ = cell::type::number;
3✔
823
            number_format(number_format::date_time6());
3✔
824
            d_->value_numeric_ = time.second.to_number();
3✔
825
        }
826
        else
827
        {
828
            auto numeric = cast_numeric(value_string);
10✔
829

830
            if (numeric.first)
10✔
831
            {
832
                d_->value_numeric_ = numeric.second;
8✔
833
                d_->type_ = cell::type::number;
8✔
834
            }
835
        }
836
    }
837
}
838

839
void cell::clear_format()
2✔
840
{
841
    if (d_->format_.is_set())
2✔
842
    {
843
        format().d_->references -= format().d_->references > 0 ? 1 : 0;
2✔
844
        d_->format_.clear();
2✔
845
    }
846
}
2✔
847

848
void cell::clear_style()
2✔
849
{
850
    if (has_format())
2✔
851
    {
852
        modifiable_format().clear_style();
2✔
853
    }
854
}
2✔
855

856
void cell::style(const class style &new_style)
260✔
857
{
858
    auto new_format = has_format() ? format() : workbook().create_format();
260✔
859

860
    new_format.border(new_style.border());
260✔
861
    new_format.fill(new_style.fill());
260✔
862
    new_format.font(new_style.font());
260✔
863
    new_format.number_format(new_style.number_format());
260✔
864

865
    format(new_format.style(new_style));
260✔
866
}
260✔
867

868
void cell::style(const std::string &style_name)
258✔
869
{
870
    style(workbook().style(style_name));
258✔
871
}
257✔
872

873
style cell::style()
7✔
874
{
875
    if (!has_format() || !format().has_style())
7✔
876
    {
877
        throw invalid_attribute();
1✔
878
    }
879

880
    auto f = format();
6✔
881

882
    return f.style();
12✔
883
}
884

885
const style cell::style() const
×
886
{
887
    if (!has_format() || !format().has_style())
×
888
    {
889
        throw invalid_attribute();
×
890
    }
891

892
    return format().style();
×
893
}
894

895
bool cell::has_style() const
5✔
896
{
897
    return has_format() && format().has_style();
5✔
898
}
899

900
format cell::modifiable_format()
7✔
901
{
902
    if (!d_->format_.is_set())
7✔
903
    {
904
        throw invalid_attribute();
×
905
    }
906

907
    return xlnt::format(d_->format_.get());
7✔
908
}
909

910
const format cell::format() const
20,658✔
911
{
912
    if (!d_->format_.is_set())
20,658✔
913
    {
914
        throw invalid_attribute();
×
915
    }
916

917
    return xlnt::format(d_->format_.get());
20,658✔
918
}
919

920
alignment cell::alignment() const
1✔
921
{
922
    return format().alignment();
1✔
923
}
924

925
border cell::border() const
1✔
926
{
927
    return format().border();
1✔
928
}
929

930
fill cell::fill() const
3✔
931
{
932
    return format().fill();
3✔
933
}
934

935
font cell::font() const
7✔
936
{
937
    return format().font();
7✔
938
}
939

940
number_format cell::number_format() const
17✔
941
{
942
    return format().number_format();
17✔
943
}
944

945
protection cell::protection() const
1✔
946
{
947
    return format().protection();
1✔
948
}
949

950
bool cell::has_hyperlink() const
14,221,226✔
951
{
952
    return d_->hyperlink_.is_set();
14,221,226✔
953
}
954

955
// comment
956

957
bool cell::has_comment()
14,221,308✔
958
{
959
    return d_->comment_.is_set();
14,221,308✔
960
}
961

962
void cell::clear_comment()
1✔
963
{
964
    if (has_comment())
1✔
965
    {
966
        d_->parent_->comments_.erase(reference().to_string());
1✔
967
        d_->comment_.clear();
1✔
968
    }
969
}
1✔
970

971
class comment cell::comment()
56✔
972
{
973
    if (!has_comment())
56✔
974
    {
975
        throw xlnt::exception("cell has no comment");
6✔
976
    }
977

978
    return *d_->comment_.get();
54✔
979
}
980

981
void cell::comment(const std::string &text, const std::string &author)
×
982
{
983
    comment(xlnt::comment(text, author));
×
984
}
×
985

986
void cell::comment(const std::string &text, const class font &comment_font, const std::string &author)
4✔
987
{
988
    comment(xlnt::comment(xlnt::rich_text(text, comment_font), author));
4✔
989
}
4✔
990

991
void cell::comment(const class comment &new_comment)
34✔
992
{
993
    if (has_comment())
34✔
994
    {
995
        *d_->comment_.get() = new_comment;
×
996
    }
997
    else
998
    {
999
        d_->parent_->comments_[reference().to_string()] = new_comment;
34✔
1000
        d_->comment_.set(&d_->parent_->comments_[reference().to_string()]);
34✔
1001
    }
1002

1003
    // offset comment 5 pixels down and 5 pixels right of the top right corner of the cell
1004
    auto cell_position = anchor();
34✔
1005
    cell_position.first += static_cast<int>(width()) + 5;
34✔
1006
    cell_position.second += 5;
34✔
1007

1008
    d_->comment_.get()->position(cell_position.first, cell_position.second);
34✔
1009

1010
    worksheet().register_comments_in_manifest();
34✔
1011
}
34✔
1012

1013
double cell::width() const
34✔
1014
{
1015
    return worksheet().column_width(column());
34✔
1016
}
1017

1018
double cell::height() const
×
1019
{
1020
    return worksheet().row_height(row());
×
1021
}
1022

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