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

xlnt-community / xlnt / 796b5d63-faf9-48e9-946d-5f649cf3d172

05 Mar 2025 05:11AM UTC coverage: 82.27% (+0.4%) from 81.87%
796b5d63-faf9-48e9-946d-5f649cf3d172

push

circleci

web-flow
Fix workbook comparisons, cleanup of included headers (#59)

This PR does the following:
1. Fixes issue https://github.com/xlnt-community/xlnt/issues/58. Please
read the issue for detailed infos.
2. Changes / removes a few definitions of `XLNT_API(_INTERNAL)` which
were defined in the wrong places (e.g. `.cpp` file, or in the header
although the function was implemented in the header too).
3. Cleans up many unnecessary or missing headers which I found along the
way.
4. Added `operator!=` to many classes to ease such comparisons and
improve programming experience.

103 of 143 new or added lines in 20 files covered. (72.03%)

3 existing lines in 2 files now uncovered.

11554 of 14044 relevant lines covered (82.27%)

1176465.94 hits per line

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

90.79
./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 <cassert>
28
#include <cmath>
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,778✔
195
    : d_(d)
14,453,778✔
196
{
197
}
14,453,778✔
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::compare(const cell &other, bool compare_by_reference) const
17✔
355
{
356
    if (compare_by_reference)
17✔
357
    {
358
        return d_ == other.d_;
17✔
359
    }
360
    else
361
    {
NEW
362
        return *d_ == *other.d_;
×
363
    }
364
}
365

366
bool cell::operator==(const cell &comparand) const
17✔
367
{
368
    return compare(comparand, true);
17✔
369
}
370

371
bool cell::operator!=(const cell &comparand) const
2✔
372
{
373
    return !(*this == comparand);
2✔
374
}
375

376
cell &cell::operator=(const cell &rhs) = default;
1✔
377

378
hyperlink cell::hyperlink() const
62✔
379
{
380
    return xlnt::hyperlink(&d_->hyperlink_.get());
62✔
381
}
382

383
void cell::hyperlink(const std::string &url, const std::string &display)
38✔
384
{
385
    if (url.empty())
38✔
386
    {
387
        throw invalid_parameter();
1✔
388
    }
389

390
    auto ws = worksheet();
37✔
391
    auto &manifest = ws.workbook().manifest();
37✔
392

393
    d_->hyperlink_ = detail::hyperlink_impl();
37✔
394

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

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

430
    d_->hyperlink_ = detail::hyperlink_impl();
4✔
431
    d_->hyperlink_.get().relationship = xlnt::relationship("", relationship_type::hyperlink,
8✔
432
        uri(""), uri(cell_address), target_mode::internal);
28✔
433
    // if a value is already present, the display string is ignored
434
    if (has_value())
4✔
435
    {
436
        d_->hyperlink_.get().display.set(to_string());
1✔
437
    }
438
    else
439
    {
440
        d_->hyperlink_.get().display.set(display.empty() ? cell_address : display);
3✔
441
        value(hyperlink().display());
3✔
442
    }
443
}
4✔
444

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

450
    d_->hyperlink_ = detail::hyperlink_impl();
3✔
451
    d_->hyperlink_.get().relationship = xlnt::relationship("", relationship_type::hyperlink,
6✔
452
        uri(""), uri(range_address), target_mode::internal);
21✔
453

454
    // if a value is already present, the display string is ignored
455
    if (has_value())
3✔
456
    {
457
        d_->hyperlink_.get().display.set(to_string());
1✔
458
    }
459
    else
460
    {
461
        d_->hyperlink_.get().display.set(display.empty() ? range_address : display);
2✔
462
        value(hyperlink().display());
2✔
463
    }
464
}
3✔
465

466
void cell::formula(const std::string &formula)
18✔
467
{
468
    if (formula.empty())
18✔
469
    {
470
        return clear_formula();
1✔
471
    }
472

473
    if (formula[0] == '=')
17✔
474
    {
475
        d_->formula_ = formula.substr(1);
8✔
476
    }
477
    else
478
    {
479
        d_->formula_ = formula;
9✔
480
    }
481

482
    worksheet().register_calc_chain_in_manifest();
17✔
483
}
484

485
bool cell::has_formula() const
14,222,467✔
486
{
487
    return d_->formula_.is_set();
14,222,467✔
488
}
489

490
std::string cell::formula() const
19✔
491
{
492
    return d_->formula_.get();
19✔
493
}
494

495
void cell::clear_formula()
795✔
496
{
497
    if (has_formula())
795✔
498
    {
499
        d_->formula_.clear();
18✔
500
        worksheet().garbage_collect_formulae();
18✔
501
    }
502
}
795✔
503

504
std::string cell::error() const
21✔
505
{
506
    if (d_->type_ != type::error)
21✔
507
    {
508
        throw xlnt::exception("called error() when cell type is not error");
21✔
509
    }
510
    return value<std::string>();
14✔
511
}
512

513
void cell::error(const std::string &error)
17✔
514
{
515
    if (error.length() == 0 || error[0] != '#')
17✔
516
    {
517
        throw invalid_data_type();
2✔
518
    }
519

520
    d_->value_text_.plain_text(error, false);
15✔
521
    d_->type_ = type::error;
15✔
522
}
15✔
523

524
cell cell::offset(int column, int row)
1✔
525
{
526
    return worksheet().cell(reference().make_offset(column, row));
1✔
527
}
528

529
worksheet cell::worksheet()
20,966✔
530
{
531
    return xlnt::worksheet(d_->parent_);
20,966✔
532
}
533

534
const worksheet cell::worksheet() const
10,265✔
535
{
536
    return xlnt::worksheet(d_->parent_);
10,265✔
537
}
538

539
workbook cell::workbook()
20,855✔
540
{
541
    return worksheet().workbook();
20,855✔
542
}
543

544
const workbook cell::workbook() const
10,215✔
545
{
546
    return worksheet().workbook();
10,215✔
547
}
548

549
std::pair<int, int> cell::anchor() const
35✔
550
{
551
    double left = 0;
35✔
552

553
    for (column_t column_index = 1; column_index <= d_->column_ - 1; column_index++)
35✔
554
    {
555
        left += worksheet().column_width(column_index);
×
556
    }
557

558
    double top = 0;
35✔
559

560
    for (row_t row_index = 1; row_index <= d_->row_ - 1; row_index++)
51✔
561
    {
562
        top += worksheet().row_height(row_index);
16✔
563
    }
564

565
    return {static_cast<int>(left), static_cast<int>(top)};
35✔
566
}
567

568
cell::type cell::data_type() const
28,453,504✔
569
{
570
    return d_->type_;
28,453,504✔
571
}
572

573
void cell::data_type(type t)
151✔
574
{
575
    d_->type_ = t;
151✔
576
}
151✔
577

578
number_format cell::computed_number_format() const
60✔
579
{
580
    return xlnt::number_format();
60✔
581
}
582

583
font cell::computed_font() const
×
584
{
585
    return xlnt::font();
×
586
}
587

588
fill cell::computed_fill() const
×
589
{
590
    return xlnt::fill();
×
591
}
592

593
border cell::computed_border() const
×
594
{
595
    return xlnt::border();
×
596
}
597

598
alignment cell::computed_alignment() const
×
599
{
600
    return xlnt::alignment();
×
601
}
602

603
protection cell::computed_protection() const
×
604
{
605
    return xlnt::protection();
×
606
}
607

608
void cell::clear_value()
768✔
609
{
610
    d_->value_numeric_ = 0;
768✔
611
    d_->value_text_.clear();
768✔
612
    d_->type_ = cell::type::empty;
768✔
613
    clear_formula();
768✔
614
}
768✔
615

616
template <>
617
bool cell::value() const
7✔
618
{
619
    return d_->value_numeric_ != 0.0;
7✔
620
}
621

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

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

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

640
template <>
641
unsigned long long cell::value() const
1✔
642
{
643
    return static_cast<unsigned long long>(d_->value_numeric_);
1✔
644
}
645

646
template <>
647
float cell::value() const
1✔
648
{
649
    return static_cast<float>(d_->value_numeric_);
1✔
650
}
651

652
template <>
653
double cell::value() const
14,200,069✔
654
{
655
    return static_cast<double>(d_->value_numeric_);
14,200,069✔
656
}
657

658
template <>
659
time cell::value() const
3✔
660
{
661
    return time::from_number(d_->value_numeric_);
3✔
662
}
663

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

670
template <>
NEW
671
date cell::value() const
×
672
{
673
    return date::from_number(static_cast<int>(d_->value_numeric_), base_date());
×
674
}
675

676
template <>
NEW
677
timedelta cell::value() const
×
678
{
679
    return timedelta::from_number(d_->value_numeric_);
×
680
}
681

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

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

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

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

706
void cell::number_format(const class number_format &number_format_)
22✔
707
{
708
    auto new_format = has_format() ? modifiable_format() : workbook().create_format();
22✔
709
    format(new_format.number_format(number_format_, optional<bool>(true)));
22✔
710
}
22✔
711

712
void cell::protection(const class protection &protection_)
1✔
713
{
714
    auto new_format = has_format() ? modifiable_format() : workbook().create_format();
1✔
715
    format(new_format.protection(protection_, optional<bool>(true)));
1✔
716
}
1✔
717

718
template <>
719
std::string cell::value() const
10,223✔
720
{
721
    return value<rich_text>().plain_text();
10,223✔
722
}
723

724
template <>
725
rich_text cell::value() const
10,226✔
726
{
727
    if (data_type() == cell::type::shared_string)
10,226✔
728
    {
729
        return workbook().shared_strings(static_cast<std::size_t>(d_->value_numeric_));
10,201✔
730
    }
731

732
    return d_->value_text_;
25✔
733
}
734

735
bool cell::has_value() const
112✔
736
{
737
    return d_->type_ != cell::type::empty;
112✔
738
}
739

740
std::string cell::to_string() const
60✔
741
{
742
    auto nf = computed_number_format();
60✔
743

744
    switch (data_type())
60✔
745
    {
746
    case cell::type::empty:
2✔
747
        return "";
4✔
748
    case cell::type::date:
6✔
749
    case cell::type::number:
750
        return nf.format(value<double>(), base_date());
6✔
751
    case cell::type::inline_string:
48✔
752
    case cell::type::shared_string:
753
    case cell::type::formula_string:
754
    case cell::type::error:
755
        return nf.format(value<std::string>());
48✔
756
    case cell::type::boolean:
4✔
757
        return value<double>() == 0.0 ? "FALSE" : "TRUE";
8✔
758
    }
759

760
    return "";
×
761
}
60✔
762

763
bool cell::has_format() const
14,231,904✔
764
{
765
    return d_->format_.is_set();
14,231,904✔
766
}
767

768
void cell::format(const class format new_format)
10,349✔
769
{
770
    if (has_format())
10,349✔
771
    {
772
        format().d_->references -= format().d_->references > 0 ? 1 : 0;
8✔
773
    }
774

775
    ++new_format.d_->references;
10,349✔
776
    d_->format_ = new_format.d_;
10,349✔
777
}
10,349✔
778

779
calendar cell::base_date() const
14✔
780
{
781
    return workbook().base_date();
14✔
782
}
783

784
bool operator==(std::nullptr_t, const cell &cell)
4✔
785
{
786
    return cell.data_type() == cell::type::empty;
4✔
787
}
788

789
bool operator==(const cell &cell, std::nullptr_t)
2✔
790
{
791
    return nullptr == cell;
2✔
792
}
793

NEW
794
bool operator!=(std::nullptr_t, const cell &cell)
×
795
{
NEW
796
    return !(nullptr == cell);
×
797
}
798

NEW
799
bool operator!=(const cell &cell, std::nullptr_t)
×
800
{
NEW
801
    return nullptr != cell;
×
802
}
803

804
std::ostream &operator<<(std::ostream &stream, const xlnt::cell &cell)
6✔
805
{
806
    return stream << cell.to_string();
6✔
807
}
808

809
void cell::value(const std::string &value_string, bool infer_type)
23✔
810
{
811
    value(value_string);
23✔
812

813
    if (!infer_type || value_string.empty())
23✔
814
    {
815
        return;
9✔
816
    }
817

818
    if (value_string.front() == '=' && value_string.size() > 1)
23✔
819
    {
820
        formula(value_string);
2✔
821
        return;
2✔
822
    }
823

824
    if (value_string.front() == '#' && value_string.size() > 1)
21✔
825
    {
826
        error(value_string);
7✔
827
        return;
7✔
828
    }
829

830
    auto percentage = cast_percentage(value_string);
14✔
831

832
    if (percentage.first)
14✔
833
    {
834
        d_->value_numeric_ = percentage.second;
1✔
835
        d_->type_ = cell::type::number;
1✔
836
        number_format(xlnt::number_format::percentage());
1✔
837
    }
838
    else
839
    {
840
        auto time = cast_time(value_string);
13✔
841

842
        if (time.first)
13✔
843
        {
844
            d_->type_ = cell::type::number;
3✔
845
            number_format(number_format::date_time6());
3✔
846
            d_->value_numeric_ = time.second.to_number();
3✔
847
        }
848
        else
849
        {
850
            auto numeric = cast_numeric(value_string);
10✔
851

852
            if (numeric.first)
10✔
853
            {
854
                d_->value_numeric_ = numeric.second;
8✔
855
                d_->type_ = cell::type::number;
8✔
856
            }
857
        }
858
    }
859
}
860

861
void cell::clear_format()
2✔
862
{
863
    if (d_->format_.is_set())
2✔
864
    {
865
        format().d_->references -= format().d_->references > 0 ? 1 : 0;
2✔
866
        d_->format_.clear();
2✔
867
    }
868
}
2✔
869

870
void cell::clear_style()
2✔
871
{
872
    if (has_format())
2✔
873
    {
874
        modifiable_format().clear_style();
2✔
875
    }
876
}
2✔
877

878
void cell::style(const class style &new_style)
260✔
879
{
880
    auto new_format = has_format() ? format() : workbook().create_format();
260✔
881

882
    new_format.border(new_style.border());
260✔
883
    new_format.fill(new_style.fill());
260✔
884
    new_format.font(new_style.font());
260✔
885
    new_format.number_format(new_style.number_format());
260✔
886

887
    format(new_format.style(new_style));
260✔
888
}
260✔
889

890
void cell::style(const std::string &style_name)
258✔
891
{
892
    style(workbook().style(style_name));
259✔
893
}
257✔
894

895
style cell::style()
7✔
896
{
897
    if (!has_format() || !format().has_style())
7✔
898
    {
899
        throw invalid_attribute();
1✔
900
    }
901

902
    auto f = format();
6✔
903

904
    return f.style();
12✔
905
}
906

907
const style cell::style() const
×
908
{
909
    if (!has_format() || !format().has_style())
×
910
    {
911
        throw invalid_attribute();
×
912
    }
913

914
    return format().style();
×
915
}
916

917
bool cell::has_style() const
5✔
918
{
919
    return has_format() && format().has_style();
5✔
920
}
921

922
format cell::modifiable_format()
7✔
923
{
924
    if (!d_->format_.is_set())
7✔
925
    {
926
        throw invalid_attribute();
×
927
    }
928

929
    return xlnt::format(d_->format_.get());
7✔
930
}
931

932
const format cell::format() const
20,658✔
933
{
934
    if (!d_->format_.is_set())
20,658✔
935
    {
936
        throw invalid_attribute();
×
937
    }
938

939
    return xlnt::format(d_->format_.get());
20,658✔
940
}
941

942
alignment cell::alignment() const
1✔
943
{
944
    return format().alignment();
1✔
945
}
946

947
border cell::border() const
1✔
948
{
949
    return format().border();
1✔
950
}
951

952
fill cell::fill() const
3✔
953
{
954
    return format().fill();
3✔
955
}
956

957
font cell::font() const
7✔
958
{
959
    return format().font();
7✔
960
}
961

962
number_format cell::number_format() const
17✔
963
{
964
    return format().number_format();
17✔
965
}
966

967
protection cell::protection() const
1✔
968
{
969
    return format().protection();
1✔
970
}
971

972
bool cell::has_hyperlink() const
14,221,226✔
973
{
974
    return d_->hyperlink_.is_set();
14,221,226✔
975
}
976

977
// comment
978

979
bool cell::has_comment()
14,221,308✔
980
{
981
    return d_->comment_.is_set();
14,221,308✔
982
}
983

984
void cell::clear_comment()
1✔
985
{
986
    if (has_comment())
1✔
987
    {
988
        d_->parent_->comments_.erase(reference().to_string());
1✔
989
        d_->comment_.clear();
1✔
990
    }
991
}
1✔
992

993
class comment cell::comment()
56✔
994
{
995
    if (!has_comment())
56✔
996
    {
997
        throw xlnt::exception("cell has no comment");
6✔
998
    }
999

1000
    return *d_->comment_.get();
54✔
1001
}
1002

1003
void cell::comment(const std::string &text, const std::string &author)
×
1004
{
1005
    comment(xlnt::comment(text, author));
×
1006
}
×
1007

1008
void cell::comment(const std::string &text, const class font &comment_font, const std::string &author)
4✔
1009
{
1010
    comment(xlnt::comment(xlnt::rich_text(text, comment_font), author));
4✔
1011
}
4✔
1012

1013
void cell::comment(const class comment &new_comment)
34✔
1014
{
1015
    if (has_comment())
34✔
1016
    {
1017
        *d_->comment_.get() = new_comment;
×
1018
    }
1019
    else
1020
    {
1021
        d_->parent_->comments_[reference().to_string()] = new_comment;
34✔
1022
        d_->comment_.set(&d_->parent_->comments_[reference().to_string()]);
34✔
1023
    }
1024

1025
    // offset comment 5 pixels down and 5 pixels right of the top right corner of the cell
1026
    auto cell_position = anchor();
34✔
1027
    cell_position.first += static_cast<int>(width()) + 5;
34✔
1028
    cell_position.second += 5;
34✔
1029

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

1032
    worksheet().register_comments_in_manifest();
34✔
1033
}
34✔
1034

1035
double cell::width() const
34✔
1036
{
1037
    return worksheet().column_width(column());
34✔
1038
}
1039

1040
double cell::height() const
×
1041
{
1042
    return worksheet().row_height(row());
×
1043
}
1044

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