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