• 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

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

25
#include <detail/header_footer/header_footer_code.hpp>
26
//#include <detail/numeric_utils.hpp>
27

28
namespace xlnt {
29
namespace detail {
30

31
std::array<xlnt::optional<xlnt::rich_text>, 3> decode_header_footer(const std::string &hf_string)
18✔
32
{
33
    std::array<xlnt::optional<xlnt::rich_text>, 3> result;
18✔
34

35
    if (hf_string.empty())
18✔
36
    {
37
        return result;
×
38
    }
39

40
    enum class hf_code
41
    {
42
        left_section, // &L
43
        center_section, // &C
44
        right_section, // &R
45
        current_page_number, // &P
46
        total_page_number, // &N
47
        font_size, // &#
48
        text_font_color, // &KRRGGBB or &KTTSNN
49
        text_strikethrough, // &S
50
        text_superscript, // &X
51
        text_subscript, // &Y
52
        date, // &D
53
        time, // &T
54
        picture_as_background, // &G
55
        text_single_underline, // &U
56
        text_double_underline, // &E
57
        workbook_file_path, // &Z
58
        workbook_file_name, // &F
59
        sheet_tab_name, // &A
60
        add_to_page_number, // &+
61
        subtract_from_page_number, // &-
62
        text_font_name, // &"font name,font type"
63
        bold_font_style, // &B
64
        italic_font_style, // &I
65
        outline_style, // &O
66
        shadow_style, // &H
67
        text // everything else
68
    };
69

70
    struct hf_token
71
    {
72
        hf_code code = hf_code::text;
73
        std::string value;
74
    };
75

76
    std::vector<hf_token> tokens;
18✔
77
    std::size_t position = 0;
18✔
78

79
    while (position < hf_string.size())
156✔
80
    {
81
        hf_token token;
138✔
82

83
        auto next_ampersand = hf_string.find('&', position + 1);
138✔
84
        token.value = hf_string.substr(position, next_ampersand - position);
138✔
85
        auto next_position = next_ampersand;
138✔
86

87
        if (hf_string[position] == '&')
138✔
88
        {
89
            token.value.clear();
106✔
90
            next_position = position + 2;
106✔
91
            auto first_code_char = hf_string[position + 1];
106✔
92

93
            if (first_code_char == '"')
106✔
94
            {
95
                auto end_quote_index = hf_string.find('"', position + 2);
24✔
96
                next_position = end_quote_index + 1;
24✔
97

98
                token.value = hf_string.substr(position + 2, end_quote_index - position - 2); // remove quotes
24✔
99
                token.code = hf_code::text_font_name;
24✔
100
            }
101
            else if (first_code_char == '&')
82✔
102
            {
103
                token.value = "&&"; // escaped ampersand
4✔
104
            }
105
            else if (first_code_char == 'L')
78✔
106
            {
107
                token.code = hf_code::left_section;
8✔
108
            }
109
            else if (first_code_char == 'C')
70✔
110
            {
111
                token.code = hf_code::center_section;
18✔
112
            }
113
            else if (first_code_char == 'R')
52✔
114
            {
115
                token.code = hf_code::right_section;
8✔
116
            }
117
            else if (first_code_char == 'P')
44✔
118
            {
119
                token.code = hf_code::current_page_number;
6✔
120
            }
121
            else if (first_code_char == 'N')
38✔
122
            {
123
                token.code = hf_code::total_page_number;
×
124
            }
125
            else if (std::string("0123456789").find(hf_string[position + 1]) != std::string::npos)
76✔
126
            {
127
                token.code = hf_code::font_size;
12✔
128
                next_position = hf_string.find_first_not_of("0123456789", position + 1);
12✔
129
                token.value = hf_string.substr(position + 1, next_position - position - 1);
12✔
130
            }
131
            else if (first_code_char == 'K')
26✔
132
            {
133
                if (hf_string[position + 4] == '+' || hf_string[position + 4] == '-')
18✔
134
                {
135
                    token.value = hf_string.substr(position + 2, 5);
×
136
                    next_position = position + 7;
×
137
                }
138
                else
139
                {
140
                    token.value = hf_string.substr(position + 2, 6);
18✔
141
                    next_position = position + 8;
18✔
142
                }
143

144
                token.code = hf_code::text_font_color;
18✔
145
            }
146
            else if (first_code_char == 'S')
8✔
147
            {
148
                token.code = hf_code::text_strikethrough;
×
149
            }
150
            else if (first_code_char == 'X')
8✔
151
            {
152
                token.code = hf_code::text_superscript;
×
153
            }
154
            else if (first_code_char == 'Y')
8✔
155
            {
156
                token.code = hf_code::text_subscript;
×
157
            }
158
            else if (first_code_char == 'D')
8✔
159
            {
160
                token.code = hf_code::date;
×
161
            }
162
            else if (first_code_char == 'T')
8✔
163
            {
164
                token.code = hf_code::time;
×
165
            }
166
            else if (first_code_char == 'G')
8✔
167
            {
168
                token.code = hf_code::picture_as_background;
×
169
            }
170
            else if (first_code_char == 'U')
8✔
171
            {
172
                token.code = hf_code::text_single_underline;
4✔
173
            }
174
            else if (first_code_char == 'E')
4✔
175
            {
176
                token.code = hf_code::text_double_underline;
×
177
            }
178
            else if (first_code_char == 'Z')
4✔
179
            {
180
                token.code = hf_code::workbook_file_path;
×
181
            }
182
            else if (first_code_char == 'F')
4✔
183
            {
184
                token.code = hf_code::workbook_file_name;
×
185
            }
186
            else if (first_code_char == 'A')
4✔
187
            {
188
                token.code = hf_code::sheet_tab_name;
4✔
189
            }
190
            else if (first_code_char == '+')
×
191
            {
192
                token.code = hf_code::add_to_page_number;
×
193
            }
194
            else if (first_code_char == '-')
×
195
            {
196
                token.code = hf_code::subtract_from_page_number;
×
197
            }
198
            else if (first_code_char == 'B')
×
199
            {
200
                token.code = hf_code::bold_font_style;
×
201
            }
202
            else if (first_code_char == 'I')
×
203
            {
204
                token.code = hf_code::italic_font_style;
×
205
            }
206
            else if (first_code_char == 'O')
×
207
            {
208
                token.code = hf_code::outline_style;
×
209
            }
210
            else if (first_code_char == 'H')
×
211
            {
212
                token.code = hf_code::shadow_style;
×
213
            }
214
        }
215

216
        position = next_position;
138✔
217
        tokens.push_back(token);
138✔
218
    }
138✔
219

220
    const auto parse_section = [&tokens, &result](hf_code code) {
54✔
221
        std::vector<hf_code> end_codes{hf_code::left_section, hf_code::center_section, hf_code::right_section};
108✔
222
        end_codes.erase(std::find(end_codes.begin(), end_codes.end(), code));
54✔
223

224
        std::size_t start_index = 0;
54✔
225

226
        while (start_index < tokens.size() && tokens[start_index].code != code)
250✔
227
        {
228
            ++start_index;
196✔
229
        }
230

231
        if (start_index == tokens.size())
54✔
232
        {
233
            return;
20✔
234
        }
235

236
        ++start_index; // skip the section code
34✔
237
        std::size_t end_index = start_index;
34✔
238

239
        while (end_index < tokens.size()
138✔
240
            && std::find(end_codes.begin(), end_codes.end(), tokens[end_index].code) == end_codes.end())
138✔
241
        {
242
            ++end_index;
104✔
243
        }
244

245
        xlnt::rich_text current_text;
34✔
246
        xlnt::rich_text_run current_run;
34✔
247

248
        // todo: all this nice parsing and the codes are just being turned back into text representations
249
        // It would be nice to create an interface for the library to read and write these codes
250

251
        for (auto i = start_index; i < end_index; ++i)
138✔
252
        {
253
            const auto &current_token = tokens[i];
104✔
254

255
            if (current_token.code == hf_code::text)
104✔
256
            {
257
                current_run.first = current_run.first + current_token.value;
36✔
258
                continue;
36✔
259
            }
260

261
            if (!current_run.first.empty())
68✔
262
            {
263
                current_text.add_run(current_run);
4✔
264
                current_run = xlnt::rich_text_run();
4✔
265
            }
266

267
            switch (current_token.code)
68✔
268
            {
269
            case hf_code::text: {
×
270
                break; // already handled above
×
271
            }
272

273
            case hf_code::left_section: {
×
274
                break; // used below
×
275
            }
276

277
            case hf_code::center_section: {
×
278
                break; // used below
×
279
            }
280

281
            case hf_code::right_section: {
×
282
                break; // used below
×
283
            }
284

285
            case hf_code::current_page_number: {
6✔
286
                current_run.first = current_run.first + "&P";
6✔
287
                break;
6✔
288
            }
289

290
            case hf_code::total_page_number: {
×
291
                current_run.first = current_run.first + "&N";
×
292
                break;
×
293
            }
294

295
            case hf_code::font_size: {
12✔
296
                if (!current_run.second.is_set())
12✔
297
                {
298
                    current_run.second = xlnt::font();
×
299
                }
300

301
                current_run.second.get().size(xlnt::detail::deserialise(current_token.value));
12✔
302

303
                break;
12✔
304
            }
305

306
            case hf_code::text_font_color: {
18✔
307
                if (current_token.value.size() == 6)
18✔
308
                {
309
                    if (!current_run.second.is_set())
18✔
310
                    {
311
                        current_run.second = xlnt::font();
×
312
                    }
313

314
                    current_run.second.get().color(xlnt::rgb_color(current_token.value));
18✔
315
                }
316

317
                break;
18✔
318
            }
319

320
            case hf_code::text_strikethrough: {
×
321
                break;
×
322
            }
323

324
            case hf_code::text_superscript: {
×
325
                break;
×
326
            }
327

328
            case hf_code::text_subscript: {
×
329
                break;
×
330
            }
331

332
            case hf_code::date: {
×
333
                current_run.first = current_run.first + "&D";
×
334
                break;
×
335
            }
336

337
            case hf_code::time: {
×
338
                current_run.first = current_run.first + "&T";
×
339
                break;
×
340
            }
341

342
            case hf_code::picture_as_background: {
×
343
                current_run.first = current_run.first + "&G";
×
344
                break;
×
345
            }
346

347
            case hf_code::text_single_underline: {
4✔
348
                if (!current_run.second.is_set())
4✔
349
                {
350
                    current_run.second = xlnt::font();
×
351
                }
352
                current_run.second.get().underline(font::underline_style::single);
4✔
353
                break;
4✔
354
            }
355

356
            case hf_code::text_double_underline: {
×
357
                break;
×
358
            }
359

360
            case hf_code::workbook_file_path: {
×
361
                current_run.first = current_run.first + "&Z";
×
362
                break;
×
363
            }
364

365
            case hf_code::workbook_file_name: {
×
366
                current_run.first = current_run.first + "&F";
×
367
                break;
×
368
            }
369

370
            case hf_code::sheet_tab_name: {
4✔
371
                current_run.first = current_run.first + "&A";
4✔
372
                break;
4✔
373
            }
374

375
            case hf_code::add_to_page_number: {
×
376
                break;
×
377
            }
378

379
            case hf_code::subtract_from_page_number: {
×
380
                break;
×
381
            }
382

383
            case hf_code::text_font_name: {
24✔
384
                auto comma_index = current_token.value.find(',');
24✔
385
                auto font_name = current_token.value.substr(0, comma_index);
24✔
386

387
                if (!current_run.second.is_set())
24✔
388
                {
389
                    current_run.second = xlnt::font();
24✔
390
                }
391

392
                if (font_name != "-")
24✔
393
                {
394
                    current_run.second.get().name(font_name);
24✔
395
                }
396

397
                if (comma_index != std::string::npos)
24✔
398
                {
399
                    auto font_type = current_token.value.substr(comma_index + 1);
24✔
400

401
                    if (font_type == "Bold")
24✔
402
                    {
403
                        current_run.second.get().bold(true);
4✔
404
                    }
405
                    else if (font_type == "Italic")
20✔
406
                    {
407
                        // TODO
408
                    }
409
                    else if (font_type == "BoldItalic")
20✔
410
                    {
411
                        current_run.second.get().bold(true);
×
412
                    }
413
                }
24✔
414

415
                break;
24✔
416
            }
24✔
417

418
            case hf_code::bold_font_style: {
×
419
                if (!current_run.second.is_set())
×
420
                {
421
                    current_run.second = xlnt::font();
×
422
                }
423

424
                current_run.second.get().bold(true);
×
425

426
                break;
×
427
            }
428

429
            case hf_code::italic_font_style: {
×
430
                break;
×
431
            }
432

433
            case hf_code::outline_style: {
×
434
                break;
×
435
            }
436

437
            case hf_code::shadow_style: {
×
438
                break;
×
439
            }
440
            }
441
        }
442

443
        if (!current_run.first.empty())
34✔
444
        {
445
            current_text.add_run(current_run);
34✔
446
        }
447

448
        auto location_index =
34✔
449
            static_cast<std::size_t>(code == hf_code::left_section ? 0 : code == hf_code::center_section ? 1 : 2);
34✔
450

451
        if (!current_text.plain_text().empty())
34✔
452
        {
453
            result[location_index] = current_text;
34✔
454
        }
455
    };
54✔
456

457
    parse_section(hf_code::left_section);
18✔
458
    parse_section(hf_code::center_section);
18✔
459
    parse_section(hf_code::right_section);
18✔
460

461
    return result;
18✔
462
}
18✔
463

464
std::string encode_header_footer(const rich_text &t, header_footer::location where)
17✔
465
{
466
    const auto location_code_map =
467
        std::unordered_map<header_footer::location,
468
            std::string, scoped_enum_hash<header_footer::location>>{
469
            {header_footer::location::left, "&L"},
×
470
            {header_footer::location::center, "&C"},
×
471
            {header_footer::location::right, "&R"},
×
472
        };
85✔
473

474
    auto encoded = location_code_map.at(where);
17✔
475

476
    for (const auto &run : t.runs())
36✔
477
    {
478
        if (run.first.empty()) continue;
19✔
479

480
        if (run.second.is_set())
19✔
481
        {
482
            if (run.second.get().has_name())
11✔
483
            {
484
                encoded.push_back('&');
11✔
485
                encoded.push_back('"');
11✔
486
                encoded.append(run.second.get().name());
11✔
487
                encoded.push_back(',');
11✔
488

489
                if (run.second.get().bold())
11✔
490
                {
491
                    encoded.append("Bold");
2✔
492
                }
493
                else
494
                {
495
                    encoded.append("Regular");
9✔
496
                }
497
                // todo: BoldItalic?
498

499
                encoded.push_back('"');
11✔
500
            }
501
            else if (run.second.get().bold())
×
502
            {
503
                encoded.append("&B");
×
504
            }
505

506
            if (run.second.get().has_size())
11✔
507
            {
508
                encoded.push_back('&');
5✔
509
                encoded.append(xlnt::detail::serialise(run.second.get().size()));
5✔
510
            }
511
            if (run.second.get().underlined())
11✔
512
            {
513
                switch (run.second.get().underline())
2✔
514
                {
515
                case font::underline_style::single:
2✔
516
                case font::underline_style::single_accounting:
517
                    encoded.append("&U");
2✔
518
                    break;
2✔
519
                case font::underline_style::double_:
×
520
                case font::underline_style::double_accounting:
521
                    encoded.append("&E");
×
522
                    break;
×
523
                case font::underline_style::none:
×
524
                    break;
×
525
                }
526
            }
527
            if (run.second.get().has_color())
11✔
528
            {
529
                encoded.push_back('&');
9✔
530
                encoded.push_back('K');
9✔
531
                encoded.append(run.second.get().color().rgb().hex_string().substr(2));
9✔
532
            }
533
        }
534

535
        encoded.append(run.first);
19✔
536
    }
17✔
537

538
    return encoded;
17✔
539
};
34✔
540

541
} // namespace detail
542
} // 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