aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf/QPDF_Name.cc
blob: 046147696ebe86451ba3f57c3fe24a2ef5720ac3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include <qpdf/QPDF_Name.hh>

#include <qpdf/JSON_writer.hh>
#include <qpdf/QUtil.hh>

#include <string_view>

QPDF_Name::QPDF_Name(std::string const& name) :
    QPDFValue(::ot_name, "name"),
    name(name)
{
}

std::shared_ptr<QPDFObject>
QPDF_Name::create(std::string const& name)
{
    return do_create(new QPDF_Name(name));
}

std::shared_ptr<QPDFObject>
QPDF_Name::copy(bool shallow)
{
    return create(name);
}

std::string
QPDF_Name::normalizeName(std::string const& name)
{
    if (name.empty()) {
        return name;
    }
    std::string result;
    result += name.at(0);
    for (size_t i = 1; i < name.length(); ++i) {
        char ch = name.at(i);
        // Don't use locale/ctype here; follow PDF spec guidelines.
        if (ch == '\0') {
            // QPDFTokenizer embeds a null character to encode an invalid #.
            result += "#";
        } else if (
            ch < 33 || ch == '#' || ch == '/' || ch == '(' || ch == ')' || ch == '{' || ch == '}' ||
            ch == '<' || ch == '>' || ch == '[' || ch == ']' || ch == '%' || ch > 126) {
            result += QUtil::hex_encode_char(ch);
        } else {
            result += ch;
        }
    }
    return result;
}

std::string
QPDF_Name::unparse()
{
    return normalizeName(this->name);
}

std::pair<bool, bool>
QPDF_Name::analyzeJSONEncoding(const std::string& name)
{
    std::basic_string_view<unsigned char> view{
        reinterpret_cast<const unsigned char*>(name.data()), name.size()};

    int tail = 0; // Number of continuation characters expected.
    bool tail2 = false; // Potential overlong 3 octet utf-8.
    bool tail3 = false; // potential overlong 4 octet
    bool needs_escaping = false;
    for (auto const& c: view) {
        if (tail) {
            if ((c & 0xc0) != 0x80) {
                return {false, false};
            }
            if (tail2) {
                if ((c & 0xe0) == 0x80) {
                    return {false, false};
                }
                tail2 = false;
            } else if (tail3) {
                if ((c & 0xf0) == 0x80) {
                    return {false, false};
                }
                tail3 = false;
            }
            tail--;
        } else if (c < 0x80) {
            if (!needs_escaping) {
                needs_escaping = !((c > 34 && c != '\\') || c == ' ' || c == 33);
            }
        } else if ((c & 0xe0) == 0xc0) {
            if ((c & 0xfe) == 0xc0) {
                return {false, false};
            }
            tail = 1;
        } else if ((c & 0xf0) == 0xe0) {
            tail2 = (c == 0xe0);
            tail = 2;
        } else if ((c & 0xf8) == 0xf0) {
            tail3 = (c == 0xf0);
            tail = 3;
        } else {
            return {false, false};
        }
    }
    return {tail == 0, !needs_escaping};
}

JSON
QPDF_Name::getJSON(int json_version)
{
    if (json_version == 1) {
        return JSON::makeString(normalizeName(this->name));
    } else {
        if (auto res = analyzeJSONEncoding(name); res.first) {
            return JSON::makeString(name);
        } else {
            return JSON::makeString("n:" + normalizeName(name));
        }
    }
}

void
QPDF_Name::writeJSON(int json_version, JSON::Writer& p)
{
    if (json_version == 1) {
        p << "\"" << JSON::Writer::encode_string(normalizeName(name)) << "\"";
    } else {
        if (auto res = analyzeJSONEncoding(name); res.first) {
            if (res.second) {
                p << "\"" << name << "\"";
            } else {
                p << "\"" << JSON::Writer::encode_string(name) << "\"";
            }
        } else {
            p << "\"n:" << JSON::Writer::encode_string(normalizeName(name)) << "\"";
        }
    }
}