From a8f5b6aaa3405e4b4eda49888ee9da8572d5473d Mon Sep 17 00:00:00 2001 From: Matthew Olsson Date: Fri, 30 Apr 2021 18:23:17 -0700 Subject: [PATCH] LibPDF: Create basic object structure This commit is the start of LibPDF, and introduces some basic structure objects. This emulates LibJS's Value structure, where Value is a simple class that can contain a pointer to a more complex Object class with more data. All of the basic PDF objects have a representation. --- Base/res/pdf/complex.pdf | 93 +++++++++++ Base/res/pdf/linearized.pdf | Bin 0 -> 3908 bytes Base/res/pdf/non-linearized.pdf | Bin 0 -> 13264 bytes Userland/Libraries/CMakeLists.txt | 1 + Userland/Libraries/LibPDF/CMakeLists.txt | 7 + Userland/Libraries/LibPDF/Forward.h | 33 ++++ Userland/Libraries/LibPDF/Object.cpp | 116 ++++++++++++++ Userland/Libraries/LibPDF/Object.h | 193 +++++++++++++++++++++++ Userland/Libraries/LibPDF/Value.cpp | 60 +++++++ Userland/Libraries/LibPDF/Value.h | 134 ++++++++++++++++ 10 files changed, 637 insertions(+) create mode 100644 Base/res/pdf/complex.pdf create mode 100644 Base/res/pdf/linearized.pdf create mode 100644 Base/res/pdf/non-linearized.pdf create mode 100644 Userland/Libraries/LibPDF/CMakeLists.txt create mode 100644 Userland/Libraries/LibPDF/Forward.h create mode 100644 Userland/Libraries/LibPDF/Object.cpp create mode 100644 Userland/Libraries/LibPDF/Object.h create mode 100644 Userland/Libraries/LibPDF/Value.cpp create mode 100644 Userland/Libraries/LibPDF/Value.h diff --git a/Base/res/pdf/complex.pdf b/Base/res/pdf/complex.pdf new file mode 100644 index 0000000000..4d7a58dcb6 --- /dev/null +++ b/Base/res/pdf/complex.pdf @@ -0,0 +1,93 @@ +%PDF-1.1 +1 0 obj +<< /Kids [2 0 R 3 0 R] /Type /Pages /Count 3 >> +endobj +4 0 obj +<< >> +stream +1. 0.000000 0.000000 1. 50. 770. cm BT /F0 36. Tf (Page One) Tj ET +endstream +endobj +2 0 obj +<< + /Rotate 0 + /Parent 1 0 R + /Resources + << /Font << /F0 << /BaseFont /Times-Italic /Subtype /Type1 /Type /Font >> >> >> + /MediaBox [0.000000 0.000000 595.275590551 841.88976378] + /Type /Page + /Contents [4 0 R] +>> +endobj +5 0 obj +<< /PageLayout /TwoColumnLeft /Pages 1 0 R /Type /Catalog >> +endobj +6 0 obj +<< + /Rotate 0 + /Parent 3 0 R + /Resources + << /Font << /F0 << /BaseFont /Times-Italic /Subtype /Type1 /Type /Font >> >> >> + /MediaBox [0.000000 0.000000 595.275590551 841.88976378] + /Type /Page + /Contents [7 0 R] +>> +endobj +3 0 obj +<< /Parent 1 0 R /Kids [8 0 R 6 0 R] /Count 2 /Type /Pages >> +endobj +8 0 obj +<< + /Rotate 270 + /Parent 3 0 R + /Resources + << /Font << /F0 << /BaseFont /Times-Italic /Subtype /Type1 /Type /Font >> >> >> + /MediaBox [0.000000 0.000000 595.275590551 841.88976378] + /Type /Page + /Contents [9 0 R] +>> +endobj +9 0 obj +<< >> +stream +q 1. 0.000000 0.000000 1. 50. 770. cm BT /F0 36. Tf (Page Two) Tj ET Q +1. 0.000000 0.000000 1. 50. 750 cm BT /F0 16 Tf ((Rotated by 270 degrees)) Tj ET +endstream +endobj +7 0 obj +<< >> +stream +1. 0.000000 0.000000 1. 50. 770. cm BT /F0 36. Tf (Page Three) Tj ET +endstream +endobj +10 0 obj +<< + /Title (PDF Explained Example) + /Author (John Whitington) + /Producer (Manually Created) + /ModDate (D:20110313002346Z) + /CreationDate (D:2011) +>> +endobj xref +0 11 +0000000000 65536 f +0000000009 00000 n +0000000177 00000 n +0000000731 00000 n +0000000072 00000 n +0000000416 00000 n +0000000492 00000 n +0000001239 00000 n +0000000808 00000 n +0000001049 00000 n +0000001346 00000 n +trailer +<< + /Info 10 0 R + /Root 5 0 R + /Size 11 + /ID [<75ff22189ceac848dfa2afec93deee03> <75ff22189ceac848dfa2afec93deee03>] +>> +startxref +1516 +%%EOF diff --git a/Base/res/pdf/linearized.pdf b/Base/res/pdf/linearized.pdf new file mode 100644 index 0000000000000000000000000000000000000000..34f8324f24f0b92140148fcf6ac8ef40e8e74581 GIT binary patch literal 3908 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 aQ0Y)yl2}v%j!Q!$a|?4WRaIAiH!c7+lV(H! literal 0 HcmV?d00001 diff --git a/Userland/Libraries/CMakeLists.txt b/Userland/Libraries/CMakeLists.txt index ae9734ef40..548498ba85 100644 --- a/Userland/Libraries/CMakeLists.txt +++ b/Userland/Libraries/CMakeLists.txt @@ -26,6 +26,7 @@ add_subdirectory(LibLine) add_subdirectory(LibM) add_subdirectory(LibMarkdown) add_subdirectory(LibPCIDB) +add_subdirectory(LibPDF) add_subdirectory(LibProtocol) add_subdirectory(LibPthread) add_subdirectory(LibRegex) diff --git a/Userland/Libraries/LibPDF/CMakeLists.txt b/Userland/Libraries/LibPDF/CMakeLists.txt new file mode 100644 index 0000000000..15e1562f76 --- /dev/null +++ b/Userland/Libraries/LibPDF/CMakeLists.txt @@ -0,0 +1,7 @@ +set(SOURCES + Object.cpp + Value.cpp + ) + +serenity_lib(LibPDF pdf) +target_link_libraries(LibPDF LibC LibCore) diff --git a/Userland/Libraries/LibPDF/Forward.h b/Userland/Libraries/LibPDF/Forward.h new file mode 100644 index 0000000000..dd9825cff8 --- /dev/null +++ b/Userland/Libraries/LibPDF/Forward.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021, Matthew Olsson + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +namespace PDF { + +class Document; +class Object; + +#define ENUMERATE_DIRECT_OBJECT_TYPES(V) \ + V(StringObject, string) \ + V(NameObject, name) \ + V(ArrayObject, array) \ + V(DictObject, dict) \ + V(StreamObject, stream) \ + V(IndirectValue, indirect_value) + +#define ENUMERATE_OBJECT_TYPES(V) \ + ENUMERATE_DIRECT_OBJECT_TYPES(V) \ + V(IndirectValueRef, indirect_value_ref) + +#define FORWARD_DECL(class_name, _) class class_name; +ENUMERATE_OBJECT_TYPES(FORWARD_DECL) +#undef FORWARD_DECL + +template +concept IsObject = IsBaseOf; + +} diff --git a/Userland/Libraries/LibPDF/Object.cpp b/Userland/Libraries/LibPDF/Object.cpp new file mode 100644 index 0000000000..4fca01a497 --- /dev/null +++ b/Userland/Libraries/LibPDF/Object.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021, Matthew Olsson + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace PDF { + +static void append_indent(StringBuilder& builder, int indent) +{ + for (int i = 0; i < indent; i++) + builder.append(" "); +} + +String StringObject::to_string(int) const +{ + if (is_binary()) + return String::formatted("<{}>", encode_hex(string().bytes()).to_uppercase()); + return String::formatted("({})", string()); +} + +String NameObject::to_string(int) const +{ + StringBuilder builder; + builder.appendff("/{}", this->name()); + return builder.to_string(); +} + +String ArrayObject::to_string(int indent) const +{ + StringBuilder builder; + builder.append("[\n"); + bool first = true; + + for (auto& element : elements()) { + if (!first) + builder.append(",\n"); + first = false; + append_indent(builder, indent + 1); + builder.appendff("{}", element.to_string(indent)); + } + + builder.append('\n'); + append_indent(builder, indent); + builder.append(']'); + return builder.to_string(); +} + +String DictObject::to_string(int indent) const +{ + StringBuilder builder; + builder.append("<<\n"); + bool first = true; + + for (auto& [key, value] : map()) { + if (!first) + builder.append(",\n"); + first = false; + append_indent(builder, indent + 1); + builder.appendff("/{} ", key); + builder.appendff("{}", value.to_string(indent + 1)); + } + + builder.append('\n'); + append_indent(builder, indent); + builder.append(">>"); + return builder.to_string(); +} + +String StreamObject::to_string(int indent) const +{ + StringBuilder builder; + builder.append("stream\n"); + append_indent(builder, indent); + builder.appendff("{}\n", dict()->to_string(indent + 1)); + append_indent(builder, indent + 1); + + auto string = encode_hex(bytes()); + while (true) { + if (string.length() > 60) { + builder.appendff("{}\n", string.substring(0, 60)); + append_indent(builder, indent); + string = string.substring(60); + continue; + } + + builder.appendff("{}\n", string); + break; + } + + append_indent(builder, indent); + builder.append("endstream"); + return builder.to_string(); +} + +String IndirectValue::to_string(int indent) const +{ + StringBuilder builder; + builder.appendff("{} {} obj\n", index(), generation_index()); + append_indent(builder, indent + 1); + builder.append(value().to_string(indent + 1)); + builder.append('\n'); + append_indent(builder, indent); + builder.append("endobj"); + return builder.to_string(); +} + +String IndirectValueRef::to_string(int) const +{ + return String::formatted("{} {} R", index(), generation_index()); +} + +} diff --git a/Userland/Libraries/LibPDF/Object.h b/Userland/Libraries/LibPDF/Object.h new file mode 100644 index 0000000000..088204e397 --- /dev/null +++ b/Userland/Libraries/LibPDF/Object.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2021, Matthew Olsson + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace PDF { + +class Object : public RefCounted { +public: + virtual ~Object() = default; + + [[nodiscard]] ALWAYS_INLINE u32 generation_index() const { return m_generation_index; } + ALWAYS_INLINE void set_generation_index(u32 generation_index) { m_generation_index = generation_index; } + +#define DEFINE_ID(_, name) \ + virtual bool is_##name() const { return false; } + ENUMERATE_OBJECT_TYPES(DEFINE_ID) +#undef DEFINE_ID + + virtual String to_string(int indent) const = 0; + +private: + u32 m_generation_index { 0 }; +}; + +class StringObject final : public Object { +public: + StringObject(String string, bool is_binary) + : m_string(move(string)) + , m_is_binary(is_binary) + { + } + + ~StringObject() override = default; + + [[nodiscard]] ALWAYS_INLINE const String& string() const { return m_string; } + [[nodiscard]] ALWAYS_INLINE bool is_binary() const { return m_is_binary; } + + ALWAYS_INLINE bool is_string() const override { return true; } + String to_string(int indent) const override; + +private: + String m_string; + bool m_is_binary; +}; + +class NameObject final : public Object { +public: + explicit NameObject(FlyString name) + : m_name(move(name)) + { + } + + ~NameObject() override = default; + + [[nodiscard]] ALWAYS_INLINE FlyString name() const { return m_name; } + + ALWAYS_INLINE bool is_name() const override { return true; } + String to_string(int indent) const override; + +private: + FlyString m_name; +}; + +class ArrayObject final : public Object { +public: + explicit ArrayObject(Vector elements) + : m_elements(move(elements)) + { + } + + ~ArrayObject() override = default; + + [[nodiscard]] ALWAYS_INLINE Vector elements() const { return m_elements; } + + ALWAYS_INLINE bool is_array() const override { return true; } + String to_string(int indent) const override; + +private: + Vector m_elements; +}; + +class DictObject final : public Object { +public: + explicit DictObject(HashMap map) + : m_map(move(map)) + { + } + + ~DictObject() override = default; + + [[nodiscard]] ALWAYS_INLINE HashMap map() const { return m_map; } + + ALWAYS_INLINE bool is_dict() const override { return true; } + String to_string(int indent) const override; + +private: + HashMap m_map; +}; + +class StreamObject final : public Object { +public: + StreamObject(const NonnullRefPtr& dict, const ReadonlyBytes& bytes) + : m_dict(dict) + , m_bytes(bytes) + { + } + + ~StreamObject() override = default; + + [[nodiscard]] ALWAYS_INLINE NonnullRefPtr dict() const { return m_dict; } + [[nodiscard]] ALWAYS_INLINE const ReadonlyBytes& bytes() const { return m_bytes; } + + ALWAYS_INLINE bool is_stream() const override { return true; } + String to_string(int indent) const override; + +private: + NonnullRefPtr m_dict; + ReadonlyBytes m_bytes; +}; + +class IndirectValue final : public Object { +public: + IndirectValue(u32 index, u32 generation_index, const Value& value) + : m_index(index) + , m_value(value) + { + set_generation_index(generation_index); + } + + ~IndirectValue() override = default; + + [[nodiscard]] ALWAYS_INLINE u32 index() const { return m_index; } + [[nodiscard]] ALWAYS_INLINE const Value& value() const { return m_value; } + + ALWAYS_INLINE bool is_indirect_value() const override { return true; } + String to_string(int indent) const override; + +private: + u32 m_index; + Value m_value; +}; + +class IndirectValueRef final : public Object { +public: + IndirectValueRef(u32 index, u32 generation_index) + : m_index(index) + { + set_generation_index(generation_index); + } + + ~IndirectValueRef() override = default; + + [[nodiscard]] ALWAYS_INLINE u32 index() const { return m_index; } + + ALWAYS_INLINE bool is_indirect_value_ref() const override { return true; } + String to_string(int indent) const override; + +private: + u32 m_index; +}; + +} + +namespace AK { + +template +struct Formatter : Formatter { + void format(FormatBuilder& builder, const T& object) + { + Formatter::format(builder, object.to_string(0)); + } +}; + +template +struct Formatter> : Formatter { + void format(FormatBuilder& builder, const NonnullRefPtr& object) + { + Formatter::format(builder, *object); + } +}; + +} diff --git a/Userland/Libraries/LibPDF/Value.cpp b/Userland/Libraries/LibPDF/Value.cpp new file mode 100644 index 0000000000..8d79bb9743 --- /dev/null +++ b/Userland/Libraries/LibPDF/Value.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021, Matthew Olsson + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace PDF { + +Value::~Value() +{ + if (is_object()) + m_as_object->unref(); +} + +Value& Value::operator=(const Value& other) +{ + m_type = other.m_type; + switch (m_type) { + case Type::Null: + break; + case Type::Bool: + m_as_bool = other.m_as_bool; + break; + case Type::Int: + m_as_int = other.m_as_int; + break; + case Type::Float: + m_as_float = other.m_as_float; + break; + case Type::Object: + m_as_object = other.m_as_object; + if (m_as_object) + m_as_object->ref(); + break; + } + return *this; +} + +String Value::to_string(int indent) const +{ + switch (m_type) { + case Type::Null: + return "null"; + case Type::Bool: + return as_bool() ? "true" : "false"; + case Type::Int: + return String::number(as_int()); + case Type::Float: + return String::number(as_float()); + case Type::Object: + return as_object()->to_string(indent); + } + + VERIFY_NOT_REACHED(); +} + +} diff --git a/Userland/Libraries/LibPDF/Value.h b/Userland/Libraries/LibPDF/Value.h new file mode 100644 index 0000000000..6999902bfc --- /dev/null +++ b/Userland/Libraries/LibPDF/Value.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021, Matthew Olsson + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace PDF { + +class Object; + +class Value { +public: + Value() + : m_type(Type::Null) + { + } + + Value(bool b) + : m_type(Type::Bool) + { + m_as_bool = b; + } + + Value(int i) + : m_type(Type::Int) + { + m_as_int = i; + } + + Value(float f) + : m_type(Type::Float) + { + m_as_float = f; + } + + template + Value(NonnullRefPtr obj) + : m_type(Type::Object) + { + obj->ref(); + m_as_object = obj; + } + + Value(const Value& other) + { + *this = other; + } + + ~Value(); + + Value& operator=(const Value& other); + + [[nodiscard]] ALWAYS_INLINE bool is_null() const { return m_type == Type::Null; } + [[nodiscard]] ALWAYS_INLINE bool is_bool() const { return m_type == Type::Bool; } + [[nodiscard]] ALWAYS_INLINE bool is_int() const { return m_type == Type::Int; } + [[nodiscard]] ALWAYS_INLINE bool is_float() const { return m_type == Type::Float; } + [[nodiscard]] ALWAYS_INLINE bool is_number() const { return is_int() || is_float(); } + + [[nodiscard]] ALWAYS_INLINE bool is_object() const { return m_type == Type::Object; } + + [[nodiscard]] ALWAYS_INLINE bool as_bool() const + { + VERIFY(is_bool()); + return m_as_bool; + } + + [[nodiscard]] ALWAYS_INLINE int as_int() const + { + VERIFY(is_int()); + return m_as_int; + } + + [[nodiscard]] ALWAYS_INLINE int to_int() const + { + if (is_int()) + return as_int(); + return static_cast(as_float()); + } + + [[nodiscard]] ALWAYS_INLINE float as_float() const + { + VERIFY(is_float()); + return m_as_float; + } + + [[nodiscard]] ALWAYS_INLINE float to_float() const + { + if (is_float()) + return as_float(); + return static_cast(as_int()); + } + + [[nodiscard]] ALWAYS_INLINE NonnullRefPtr as_object() const { return *m_as_object; } + + [[nodiscard]] ALWAYS_INLINE explicit operator bool() const { return !is_null(); } + + [[nodiscard]] String to_string(int indent = 0) const; + +private: + enum class Type { + Null, + Bool, + Int, + Float, + Object, + }; + + union { + bool m_as_bool; + int m_as_int; + float m_as_float; + Object* m_as_object; + }; + + Type m_type; +}; + +} + +namespace AK { + +template<> +struct Formatter : Formatter { + void format(FormatBuilder& builder, const PDF::Value& value) + { + Formatter::format(builder, value.to_string()); + } +}; + +}