summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/CMakeLists.txt1
-rw-r--r--Userland/Libraries/LibPDF/CMakeLists.txt7
-rw-r--r--Userland/Libraries/LibPDF/Forward.h33
-rw-r--r--Userland/Libraries/LibPDF/Object.cpp116
-rw-r--r--Userland/Libraries/LibPDF/Object.h193
-rw-r--r--Userland/Libraries/LibPDF/Value.cpp60
-rw-r--r--Userland/Libraries/LibPDF/Value.h134
7 files changed, 544 insertions, 0 deletions
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 <mattco@serenityos.org>
+ *
+ * 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<typename T>
+concept IsObject = IsBaseOf<Object, T>;
+
+}
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 <mattco@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/Hex.h>
+#include <LibPDF/Object.h>
+
+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 <mattco@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+#include <AK/Format.h>
+#include <AK/HashMap.h>
+#include <AK/RefCounted.h>
+#include <LibPDF/Forward.h>
+#include <LibPDF/Value.h>
+
+namespace PDF {
+
+class Object : public RefCounted<Object> {
+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<Value> elements)
+ : m_elements(move(elements))
+ {
+ }
+
+ ~ArrayObject() override = default;
+
+ [[nodiscard]] ALWAYS_INLINE Vector<Value> elements() const { return m_elements; }
+
+ ALWAYS_INLINE bool is_array() const override { return true; }
+ String to_string(int indent) const override;
+
+private:
+ Vector<Value> m_elements;
+};
+
+class DictObject final : public Object {
+public:
+ explicit DictObject(HashMap<FlyString, Value> map)
+ : m_map(move(map))
+ {
+ }
+
+ ~DictObject() override = default;
+
+ [[nodiscard]] ALWAYS_INLINE HashMap<FlyString, Value> map() const { return m_map; }
+
+ ALWAYS_INLINE bool is_dict() const override { return true; }
+ String to_string(int indent) const override;
+
+private:
+ HashMap<FlyString, Value> m_map;
+};
+
+class StreamObject final : public Object {
+public:
+ StreamObject(const NonnullRefPtr<DictObject>& dict, const ReadonlyBytes& bytes)
+ : m_dict(dict)
+ , m_bytes(bytes)
+ {
+ }
+
+ ~StreamObject() override = default;
+
+ [[nodiscard]] ALWAYS_INLINE NonnullRefPtr<DictObject> 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<DictObject> 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<PDF::IsObject T>
+struct Formatter<T> : Formatter<StringView> {
+ void format(FormatBuilder& builder, const T& object)
+ {
+ Formatter<StringView>::format(builder, object.to_string(0));
+ }
+};
+
+template<PDF::IsObject T>
+struct Formatter<NonnullRefPtr<T>> : Formatter<T> {
+ void format(FormatBuilder& builder, const NonnullRefPtr<T>& object)
+ {
+ Formatter<T>::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 <mattco@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibPDF/Object.h>
+#include <LibPDF/Value.h>
+
+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 <mattco@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Format.h>
+
+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<IsObject T>
+ Value(NonnullRefPtr<T> 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<int>(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<float>(as_int());
+ }
+
+ [[nodiscard]] ALWAYS_INLINE NonnullRefPtr<Object> 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<PDF::Value> : Formatter<StringView> {
+ void format(FormatBuilder& builder, const PDF::Value& value)
+ {
+ Formatter<StringView>::format(builder, value.to_string());
+ }
+};
+
+}