Coverage Report

Created: 2026-09-26 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibPDF/ObjectDerivatives.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2021-2022, Matthew Olsson <mattco@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <AK/Hex.h>
8
#include <LibPDF/CommonNames.h>
9
#include <LibPDF/Document.h>
10
#include <LibPDF/ObjectDerivatives.h>
11
12
namespace PDF {
13
14
PDFErrorOr<NonnullRefPtr<Object>> ArrayObject::get_object_at(Document* document, size_t index) const
15
0
{
16
0
    return document->resolve_to<Object>(at(index));
17
0
}
18
19
PDFErrorOr<NonnullRefPtr<Object>> DictObject::get_object(Document* document, DeprecatedFlyString const& key) const
20
0
{
21
0
    return document->resolve_to<Object>(get_value(key));
22
0
}
23
24
#define DEFINE_ACCESSORS(class_name, snake_name)                                                                                 \
25
    PDFErrorOr<NonnullRefPtr<class_name>> ArrayObject::get_##snake_name##_at(Document* document, size_t index) const             \
26
0
    {                                                                                                                            \
27
0
        if (index >= m_elements.size())                                                                                          \
28
0
            return Error { Error::Type::Internal, "Out of bounds array access" };                                                \
29
0
        return document->resolve_to<class_name>(m_elements[index]);                                                              \
30
0
    }                                                                                                                            \
Unexecuted instantiation: PDF::ArrayObject::get_string_at(PDF::Document*, unsigned long) const
Unexecuted instantiation: PDF::ArrayObject::get_name_at(PDF::Document*, unsigned long) const
Unexecuted instantiation: PDF::ArrayObject::get_array_at(PDF::Document*, unsigned long) const
Unexecuted instantiation: PDF::ArrayObject::get_dict_at(PDF::Document*, unsigned long) const
Unexecuted instantiation: PDF::ArrayObject::get_stream_at(PDF::Document*, unsigned long) const
Unexecuted instantiation: PDF::ArrayObject::get_indirect_value_at(PDF::Document*, unsigned long) const
31
                                                                                                                                 \
32
    NonnullRefPtr<class_name> ArrayObject::get_##snake_name##_at(size_t index) const                                             \
33
0
    {                                                                                                                            \
34
0
        VERIFY(index < m_elements.size());                                                                                       \
35
0
        return cast_to<class_name>(m_elements[index]);                                                                           \
36
0
    }                                                                                                                            \
Unexecuted instantiation: PDF::ArrayObject::get_string_at(unsigned long) const
Unexecuted instantiation: PDF::ArrayObject::get_name_at(unsigned long) const
Unexecuted instantiation: PDF::ArrayObject::get_array_at(unsigned long) const
Unexecuted instantiation: PDF::ArrayObject::get_dict_at(unsigned long) const
Unexecuted instantiation: PDF::ArrayObject::get_stream_at(unsigned long) const
Unexecuted instantiation: PDF::ArrayObject::get_indirect_value_at(unsigned long) const
37
                                                                                                                                 \
38
    PDFErrorOr<NonnullRefPtr<class_name>> DictObject::get_##snake_name(Document* document, DeprecatedFlyString const& key) const \
39
0
    {                                                                                                                            \
40
0
        return document->resolve_to<class_name>(get_value(key));                                                                 \
41
0
    }                                                                                                                            \
Unexecuted instantiation: PDF::DictObject::get_string(PDF::Document*, AK::DeprecatedFlyString const&) const
Unexecuted instantiation: PDF::DictObject::get_name(PDF::Document*, AK::DeprecatedFlyString const&) const
Unexecuted instantiation: PDF::DictObject::get_array(PDF::Document*, AK::DeprecatedFlyString const&) const
Unexecuted instantiation: PDF::DictObject::get_dict(PDF::Document*, AK::DeprecatedFlyString const&) const
Unexecuted instantiation: PDF::DictObject::get_stream(PDF::Document*, AK::DeprecatedFlyString const&) const
Unexecuted instantiation: PDF::DictObject::get_indirect_value(PDF::Document*, AK::DeprecatedFlyString const&) const
42
                                                                                                                                 \
43
    NonnullRefPtr<class_name> DictObject::get_##snake_name(DeprecatedFlyString const& key) const                                 \
44
0
    {                                                                                                                            \
45
0
        return cast_to<class_name>(get_value(key));                                                                              \
46
0
    }
Unexecuted instantiation: PDF::DictObject::get_string(AK::DeprecatedFlyString const&) const
Unexecuted instantiation: PDF::DictObject::get_name(AK::DeprecatedFlyString const&) const
Unexecuted instantiation: PDF::DictObject::get_array(AK::DeprecatedFlyString const&) const
Unexecuted instantiation: PDF::DictObject::get_dict(AK::DeprecatedFlyString const&) const
Unexecuted instantiation: PDF::DictObject::get_stream(AK::DeprecatedFlyString const&) const
Unexecuted instantiation: PDF::DictObject::get_indirect_value(AK::DeprecatedFlyString const&) const
47
48
ENUMERATE_OBJECT_TYPES(DEFINE_ACCESSORS)
49
#undef DEFINE_INDEXER
50
51
static void append_indent(StringBuilder& builder, int indent)
52
0
{
53
0
    for (int i = 0; i < indent; i++)
54
0
        builder.append("  "sv);
55
0
}
56
57
ByteString StringObject::to_byte_string(int) const
58
0
{
59
0
    if (is_binary())
60
0
        return ByteString::formatted("<{}>", encode_hex(string().bytes()).to_uppercase());
61
0
    return ByteString::formatted("({})", string());
62
0
}
63
64
ByteString NameObject::to_byte_string(int) const
65
0
{
66
0
    StringBuilder builder;
67
0
    builder.appendff("/{}", this->name());
68
0
    return builder.to_byte_string();
69
0
}
70
71
Vector<float> ArrayObject::float_elements() const
72
0
{
73
0
    Vector<float> values;
74
0
    values.ensure_capacity(m_elements.size());
75
0
    for (auto const& value : m_elements) {
76
0
        values.append(value.to_float());
77
0
    }
78
0
    return values;
79
0
}
80
81
ByteString ArrayObject::to_byte_string(int indent) const
82
0
{
83
0
    StringBuilder builder;
84
0
    builder.append("[\n"sv);
85
0
    bool first = true;
86
87
0
    for (auto& element : elements()) {
88
0
        if (!first)
89
0
            builder.append("\n"sv);
90
0
        first = false;
91
0
        append_indent(builder, indent + 1);
92
0
        builder.appendff("{}", element.to_byte_string(indent));
93
0
    }
94
95
0
    builder.append('\n');
96
0
    append_indent(builder, indent);
97
0
    builder.append(']');
98
0
    return builder.to_byte_string();
99
0
}
100
101
ByteString DictObject::to_byte_string(int indent) const
102
0
{
103
0
    StringBuilder builder;
104
0
    append_indent(builder, indent);
105
0
    builder.append("<<\n"sv);
106
0
    bool first = true;
107
108
0
    for (auto& [key, value] : map()) {
109
0
        if (!first)
110
0
            builder.append("\n"sv);
111
0
        first = false;
112
0
        append_indent(builder, indent + 1);
113
0
        builder.appendff("/{} ", key);
114
0
        builder.appendff("{}", value.to_byte_string(indent + 1));
115
0
    }
116
117
0
    builder.append('\n');
118
0
    append_indent(builder, indent);
119
0
    builder.append(">>"sv);
120
0
    return builder.to_byte_string();
121
0
}
122
123
ByteString StreamObject::to_byte_string(int indent) const
124
0
{
125
0
    StringBuilder builder;
126
0
    builder.appendff("{}\n", dict()->to_byte_string(indent));
127
0
    builder.append("stream\n"sv);
128
129
0
    size_t ascii_count = 0;
130
0
    for (auto c : bytes()) {
131
0
        if (c < 128)
132
0
            ++ascii_count;
133
0
    }
134
135
0
    size_t percentage_ascii = 100;
136
0
    if (bytes().size())
137
0
        percentage_ascii = ascii_count * 100 / bytes().size();
138
0
    bool is_mostly_text = percentage_ascii > 95;
139
140
0
    if (dict()->contains(CommonNames::Subtype) && dict()->get_name(CommonNames::Subtype)->name() == "Image")
141
0
        is_mostly_text = false;
142
143
0
    if (is_mostly_text) {
144
0
        for (size_t i = 0; i < bytes().size(); ++i) {
145
0
            auto c = bytes()[i];
146
0
            if (c < 128) {
147
0
                bool next_is_newline = i + 1 < bytes().size() && bytes()[i + 1] == '\n';
148
0
                if (c == '\r' && !next_is_newline)
149
0
                    builder.append('\n');
150
0
                else
151
0
                    builder.append(c);
152
0
            } else {
153
0
                builder.appendff("\\{:03o}", c);
154
0
            }
155
0
        }
156
0
    } else {
157
0
        int const chars_per_line = 60;
158
0
        int const bytes_per_line = chars_per_line / 2;
159
0
        int const max_lines_to_print = 10;
160
0
        int const max_bytes_to_print = max_lines_to_print * bytes_per_line;
161
0
        auto string = encode_hex(bytes().trim(max_bytes_to_print));
162
0
        StringView view { string };
163
0
        while (view.length() > 60) {
164
0
            builder.appendff("{}\n", view.substring_view(0, chars_per_line));
165
0
            append_indent(builder, indent);
166
0
            view = view.substring_view(60);
167
0
        }
168
0
        builder.appendff("{}\n", view);
169
170
0
        if (bytes().size() > max_bytes_to_print)
171
0
            builder.appendff("... (and {} more bytes)\n", bytes().size() - max_bytes_to_print);
172
0
    }
173
174
0
    builder.append("endstream"sv);
175
0
    return builder.to_byte_string();
176
0
}
177
178
ByteString IndirectValue::to_byte_string(int indent) const
179
0
{
180
0
    StringBuilder builder;
181
0
    builder.appendff("{} {} obj\n", index(), generation_index());
182
0
    append_indent(builder, indent + 1);
183
0
    builder.append(value().to_byte_string(indent + 1));
184
0
    builder.append('\n');
185
0
    append_indent(builder, indent);
186
0
    builder.append("endobj"sv);
187
0
    return builder.to_byte_string();
188
0
}
189
190
}