1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 07:37:46 +00:00

LibPDF: Implement name tree lookups

Name Trees are hierarchical, string-keyed, sorted-by-key dictionary
structures in PDF where each node (except the root) specifies the bounds
of the values it holds, and either its kids (more nodes) or the
key/value pairs it contains.

This commit implements a series of lookup calls for finding a key in
such name trees. This implementation follows the tree as needed on each
lookup, but if that becomes inefficient in the long run we can switch to
creating a HashMap with all the contents, which as a drawback will
require more memory.
This commit is contained in:
Rodrigo Tobar 2023-01-06 00:33:24 +08:00 committed by Andreas Kling
parent 8c79f0e0cf
commit 5420261347
3 changed files with 46 additions and 0 deletions

View file

@ -199,6 +199,46 @@ PDFErrorOr<void> Document::add_page_tree_node_to_page_tree(NonnullRefPtr<DictObj
return {};
}
PDFErrorOr<NonnullRefPtr<Object>> Document::find_in_name_tree(NonnullRefPtr<DictObject> tree, FlyString name)
{
if (tree->contains(CommonNames::Kids)) {
return find_in_name_tree_nodes(tree->get_array(CommonNames::Kids), name);
}
if (!tree->contains(CommonNames::Names))
return Error { Error::Type::MalformedPDF, "name tree has neither Kids nor Names" };
auto key_value_names_array = TRY(tree->get_array(this, CommonNames::Names));
return find_in_key_value_array(key_value_names_array, name);
}
PDFErrorOr<NonnullRefPtr<Object>> Document::find_in_name_tree_nodes(NonnullRefPtr<ArrayObject> siblings, FlyString name)
{
for (size_t i = 0; i < siblings->size(); i++) {
auto sibling = TRY(resolve_to<DictObject>(siblings->at(i)));
auto limits = sibling->get_array(CommonNames::Limits);
if (limits->size() != 2)
return Error { Error::Type::MalformedPDF, "Expected 2-element Limits array" };
auto start = limits->get_string_at(0);
auto end = limits->get_string_at(1);
if (start->string() <= name && end->string() >= name) {
return find_in_name_tree(sibling, name);
}
}
return Error { Error::Type::MalformedPDF, DeprecatedString::formatted("Didn't find node in name tree containing name {}", name) };
}
PDFErrorOr<NonnullRefPtr<Object>> Document::find_in_key_value_array(NonnullRefPtr<ArrayObject> key_value_array, FlyString name)
{
if (key_value_array->size() % 2 == 1)
return Error { Error::Type::MalformedPDF, "key/value array has dangling key" };
for (size_t i = 0; i < key_value_array->size() / 2; i++) {
auto key = key_value_array->get_string_at(2 * i);
if (key->string() == name) {
return key_value_array->get_object_at(this, 2 * i + 1);
}
}
return Error { Error::Type::MalformedPDF, DeprecatedString::formatted("Didn't find expected name {} in key/value array", name) };
}
PDFErrorOr<void> Document::build_outline()
{
if (!m_catalog->contains(CommonNames::Outlines))