Coverage Report

Created: 2024-09-08 06:06

/src/qpdf/libqpdf/QPDF_Name.cc
Line
Count
Source (jump to first uncovered line)
1
#include <qpdf/QPDF_Name.hh>
2
3
#include <qpdf/JSON_writer.hh>
4
#include <qpdf/QUtil.hh>
5
6
QPDF_Name::QPDF_Name(std::string const& name) :
7
    QPDFValue(::ot_name),
8
    name(name)
9
7.10M
{
10
7.10M
}
11
12
std::shared_ptr<QPDFObject>
13
QPDF_Name::create(std::string const& name)
14
7.10M
{
15
7.10M
    return do_create(new QPDF_Name(name));
16
7.10M
}
17
18
std::shared_ptr<QPDFObject>
19
QPDF_Name::copy(bool shallow)
20
8.99k
{
21
8.99k
    return create(name);
22
8.99k
}
23
24
std::string
25
QPDF_Name::normalizeName(std::string const& name)
26
10.7M
{
27
10.7M
    if (name.empty()) {
28
0
        return name;
29
0
    }
30
10.7M
    std::string result;
31
10.7M
    result += name.at(0);
32
80.5M
    for (size_t i = 1; i < name.length(); ++i) {
33
69.7M
        char ch = name.at(i);
34
        // Don't use locale/ctype here; follow PDF spec guidelines.
35
69.7M
        if (ch == '\0') {
36
            // QPDFTokenizer embeds a null character to encode an invalid #.
37
4.73k
            result += "#";
38
69.7M
        } else if (
39
69.7M
            ch < 33 || ch == '#' || ch == '/' || ch == '(' || ch == ')' || ch == '{' || ch == '}' ||
40
69.7M
            ch == '<' || ch == '>' || ch == '[' || ch == ']' || ch == '%' || ch > 126) {
41
36.5M
            result += QUtil::hex_encode_char(ch);
42
36.5M
        } else {
43
33.2M
            result += ch;
44
33.2M
        }
45
69.7M
    }
46
10.7M
    return result;
47
10.7M
}
48
49
std::string
50
QPDF_Name::unparse()
51
7.42M
{
52
7.42M
    return normalizeName(this->name);
53
7.42M
}
54
55
std::pair<bool, bool>
56
QPDF_Name::analyzeJSONEncoding(const std::string& name)
57
0
{
58
0
    int tail = 0;       // Number of continuation characters expected.
59
0
    bool tail2 = false; // Potential overlong 3 octet utf-8.
60
0
    bool tail3 = false; // potential overlong 4 octet
61
0
    bool needs_escaping = false;
62
0
    for (auto const& it: name) {
63
0
        auto c = static_cast<unsigned char>(it);
64
0
        if (tail) {
65
0
            if ((c & 0xc0) != 0x80) {
66
0
                return {false, false};
67
0
            }
68
0
            if (tail2) {
69
0
                if ((c & 0xe0) == 0x80) {
70
0
                    return {false, false};
71
0
                }
72
0
                tail2 = false;
73
0
            } else if (tail3) {
74
0
                if ((c & 0xf0) == 0x80) {
75
0
                    return {false, false};
76
0
                }
77
0
                tail3 = false;
78
0
            }
79
0
            tail--;
80
0
        } else if (c < 0x80) {
81
0
            if (!needs_escaping) {
82
0
                needs_escaping = !((c > 34 && c != '\\') || c == ' ' || c == 33);
83
0
            }
84
0
        } else if ((c & 0xe0) == 0xc0) {
85
0
            if ((c & 0xfe) == 0xc0) {
86
0
                return {false, false};
87
0
            }
88
0
            tail = 1;
89
0
        } else if ((c & 0xf0) == 0xe0) {
90
0
            tail2 = (c == 0xe0);
91
0
            tail = 2;
92
0
        } else if ((c & 0xf8) == 0xf0) {
93
0
            tail3 = (c == 0xf0);
94
0
            tail = 3;
95
0
        } else {
96
0
            return {false, false};
97
0
        }
98
0
    }
99
0
    return {tail == 0, !needs_escaping};
100
0
}
101
102
void
103
QPDF_Name::writeJSON(int json_version, JSON::Writer& p)
104
0
{
105
    // For performance reasons this code is duplicated in QPDF_Dictionary::writeJSON. When updating
106
    // this method make sure QPDF_Dictionary is also update.
107
0
    if (json_version == 1) {
108
0
        p << "\"" << JSON::Writer::encode_string(normalizeName(name)) << "\"";
109
0
    } else {
110
0
        if (auto res = analyzeJSONEncoding(name); res.first) {
111
0
            if (res.second) {
112
0
                p << "\"" << name << "\"";
113
0
            } else {
114
0
                p << "\"" << JSON::Writer::encode_string(name) << "\"";
115
0
            }
116
0
        } else {
117
0
            p << "\"n:" << JSON::Writer::encode_string(normalizeName(name)) << "\"";
118
0
        }
119
0
    }
120
0
}