/src/serenity/Userland/Libraries/LibPDF/XRefTable.h
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 | | #pragma once |
8 | | |
9 | | #include <AK/ByteString.h> |
10 | | #include <AK/Format.h> |
11 | | #include <AK/RefCounted.h> |
12 | | #include <AK/Vector.h> |
13 | | #include <LibPDF/Error.h> |
14 | | |
15 | | namespace PDF { |
16 | | |
17 | | constexpr u64 invalid_byte_offset = NumericLimits<u64>::max(); |
18 | | |
19 | | struct XRefEntry { |
20 | | u64 byte_offset { invalid_byte_offset }; |
21 | | u16 generation_number { 0 }; |
22 | | bool in_use { false }; |
23 | | bool compressed { false }; |
24 | | }; |
25 | | |
26 | | struct XRefSection { |
27 | | int starting_index; |
28 | | int count; |
29 | | Vector<XRefEntry> entries; |
30 | | }; |
31 | | |
32 | | class XRefTable final : public RefCounted<XRefTable> { |
33 | | public: |
34 | | PDFErrorOr<void> merge(XRefTable&& other) |
35 | 0 | { |
36 | 0 | auto this_size = m_entries.size(); |
37 | 0 | auto other_size = other.m_entries.size(); |
38 | 0 | TRY(m_entries.try_ensure_capacity(other_size)); |
39 | |
|
40 | 0 | for (size_t i = 0; i < other_size; i++) { |
41 | 0 | auto other_entry = other.m_entries[i]; |
42 | 0 | if (i >= this_size) { |
43 | 0 | m_entries.unchecked_append(other_entry); |
44 | 0 | continue; |
45 | 0 | } |
46 | | |
47 | 0 | auto this_entry = m_entries[i]; |
48 | | |
49 | | // Only add values that we don't already have. |
50 | 0 | if (this_entry.byte_offset == invalid_byte_offset) |
51 | 0 | m_entries[i] = other_entry; |
52 | 0 | } |
53 | |
|
54 | 0 | return {}; |
55 | 0 | } |
56 | | |
57 | | void add_section(XRefSection const& section) |
58 | 0 | { |
59 | 0 | m_entries.ensure_capacity(section.starting_index + section.count); |
60 | |
|
61 | 0 | for (int i = static_cast<int>(m_entries.size()); i < section.starting_index; i++) |
62 | 0 | m_entries.append(XRefEntry {}); |
63 | |
|
64 | 0 | for (auto& entry : section.entries) |
65 | 0 | m_entries.append(entry); |
66 | 0 | } |
67 | | |
68 | 0 | void set_trailer(RefPtr<DictObject> trailer) { m_trailer = trailer; } |
69 | | |
70 | 0 | ALWAYS_INLINE Vector<XRefEntry>& entries() { return m_entries; } |
71 | | |
72 | 0 | ALWAYS_INLINE RefPtr<DictObject> const& trailer() const { return m_trailer; } |
73 | | |
74 | | [[nodiscard]] ALWAYS_INLINE bool has_object(size_t index) const |
75 | 0 | { |
76 | 0 | return index < m_entries.size() && m_entries[index].byte_offset != invalid_byte_offset; |
77 | 0 | } |
78 | | |
79 | | [[nodiscard]] ALWAYS_INLINE u64 byte_offset_for_object(size_t index) const |
80 | 0 | { |
81 | 0 | VERIFY(has_object(index)); |
82 | 0 | return m_entries[index].byte_offset; |
83 | 0 | } |
84 | | |
85 | | [[nodiscard]] ALWAYS_INLINE u64 object_stream_for_object(size_t index) const |
86 | 0 | { |
87 | 0 | return byte_offset_for_object(index); |
88 | 0 | } |
89 | | |
90 | | [[nodiscard]] ALWAYS_INLINE u16 generation_number_for_object(size_t index) const |
91 | 0 | { |
92 | 0 | VERIFY(has_object(index)); |
93 | 0 | return m_entries[index].generation_number; |
94 | 0 | } |
95 | | |
96 | | [[nodiscard]] ALWAYS_INLINE u16 object_stream_index_for_object(size_t index) const |
97 | 0 | { |
98 | 0 | return generation_number_for_object(index); |
99 | 0 | } |
100 | | |
101 | | [[nodiscard]] ALWAYS_INLINE bool is_object_in_use(size_t index) const |
102 | 0 | { |
103 | 0 | VERIFY(has_object(index)); |
104 | 0 | return m_entries[index].in_use; |
105 | 0 | } |
106 | | |
107 | | [[nodiscard]] ALWAYS_INLINE bool is_object_compressed(size_t index) const |
108 | 0 | { |
109 | 0 | VERIFY(has_object(index)); |
110 | 0 | return m_entries[index].compressed; |
111 | 0 | } |
112 | | |
113 | | private: |
114 | | friend struct AK::Formatter<PDF::XRefTable>; |
115 | | |
116 | | Vector<XRefEntry> m_entries; |
117 | | RefPtr<DictObject> m_trailer; |
118 | | }; |
119 | | |
120 | | } |
121 | | |
122 | | namespace AK { |
123 | | |
124 | | template<> |
125 | | struct Formatter<PDF::XRefEntry> : Formatter<StringView> { |
126 | | ErrorOr<void> format(FormatBuilder& builder, PDF::XRefEntry const& entry) |
127 | 0 | { |
128 | 0 | return Formatter<StringView>::format(builder, |
129 | 0 | ByteString::formatted("XRefEntry {{ offset={} generation={} used={} }}", |
130 | 0 | entry.byte_offset, |
131 | 0 | entry.generation_number, |
132 | 0 | entry.in_use)); |
133 | 0 | } |
134 | | }; |
135 | | |
136 | | template<> |
137 | | struct Formatter<PDF::XRefTable> : Formatter<StringView> { |
138 | | ErrorOr<void> format(FormatBuilder& format_builder, PDF::XRefTable const& table) |
139 | 0 | { |
140 | 0 | StringBuilder builder; |
141 | 0 | builder.append("XRefTable {"sv); |
142 | 0 | for (auto& entry : table.m_entries) |
143 | 0 | builder.appendff("\n {}", entry); |
144 | 0 | builder.append("\n}"sv); |
145 | 0 | return Formatter<StringView>::format(format_builder, builder.to_byte_string()); |
146 | 0 | } |
147 | | }; |
148 | | |
149 | | } |