diff options
-rw-r--r-- | Userland/Libraries/LibPDF/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibPDF/ColorSpace.cpp | 56 | ||||
-rw-r--r-- | Userland/Libraries/LibPDF/ColorSpace.h | 72 | ||||
-rw-r--r-- | Userland/Libraries/LibPDF/CommonNames.h | 10 | ||||
-rw-r--r-- | Userland/Libraries/LibPDF/Document.cpp | 5 | ||||
-rw-r--r-- | Userland/Libraries/LibPDF/Renderer.cpp | 103 | ||||
-rw-r--r-- | Userland/Libraries/LibPDF/Renderer.h | 33 |
7 files changed, 174 insertions, 106 deletions
diff --git a/Userland/Libraries/LibPDF/CMakeLists.txt b/Userland/Libraries/LibPDF/CMakeLists.txt index ea1cbce233..c3a959e4b2 100644 --- a/Userland/Libraries/LibPDF/CMakeLists.txt +++ b/Userland/Libraries/LibPDF/CMakeLists.txt @@ -1,4 +1,5 @@ set(SOURCES + ColorSpace.cpp CommonNames.cpp Document.cpp Filter.cpp diff --git a/Userland/Libraries/LibPDF/ColorSpace.cpp b/Userland/Libraries/LibPDF/ColorSpace.cpp new file mode 100644 index 0000000000..1651c3fff8 --- /dev/null +++ b/Userland/Libraries/LibPDF/ColorSpace.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibPDF/ColorSpace.h> +#include <LibPDF/CommonNames.h> + +namespace PDF { + +RefPtr<DeviceGrayColorSpace> DeviceGrayColorSpace::the() +{ + static auto instance = adopt_ref(*new DeviceGrayColorSpace()); + return instance; +} + +Color DeviceGrayColorSpace::color(const Vector<Value>& arguments) const +{ + VERIFY(arguments.size() == 1); + auto gray = static_cast<u8>(arguments[0].to_float() * 255.0f); + return Color(gray, gray, gray); +} + +RefPtr<DeviceRGBColorSpace> DeviceRGBColorSpace::the() +{ + static auto instance = adopt_ref(*new DeviceRGBColorSpace()); + return instance; +} + +Color DeviceRGBColorSpace::color(const Vector<Value>& arguments) const +{ + VERIFY(arguments.size() == 3); + auto r = static_cast<u8>(arguments[0].to_float() * 255.0f); + auto g = static_cast<u8>(arguments[1].to_float() * 255.0f); + auto b = static_cast<u8>(arguments[2].to_float() * 255.0f); + return Color(r, g, b); +} + +RefPtr<DeviceCMYKColorSpace> DeviceCMYKColorSpace::the() +{ + static auto instance = adopt_ref(*new DeviceCMYKColorSpace()); + return instance; +} + +Color DeviceCMYKColorSpace::color(const Vector<Value>& arguments) const +{ + VERIFY(arguments.size() == 4); + auto c = arguments[0].to_float(); + auto m = arguments[1].to_float(); + auto y = arguments[2].to_float(); + auto k = arguments[3].to_float(); + return Color::from_cmyk(c, m, y, k); +} + +} diff --git a/Userland/Libraries/LibPDF/ColorSpace.h b/Userland/Libraries/LibPDF/ColorSpace.h new file mode 100644 index 0000000000..cea624a6ac --- /dev/null +++ b/Userland/Libraries/LibPDF/ColorSpace.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/FlyString.h> +#include <LibGfx/Color.h> +#include <LibPDF/Object.h> +#include <LibPDF/Value.h> + +#define ENUMERATE_COLOR_SPACES(V) \ + V(DeviceGray) \ + V(DeviceRGB) \ + V(DeviceCMYK) \ + V(CalGray) \ + V(CalRGB) \ + V(Lab) \ + V(ICCBased) \ + V(Indexed) \ + V(Pattern) \ + V(Separation) \ + V(DeviceN) + +namespace PDF { + +class ColorSpace : public RefCounted<ColorSpace> { +public: + virtual ~ColorSpace() = default; + + virtual Color color(const Vector<Value>& arguments) const = 0; +}; + +class DeviceGrayColorSpace final : public ColorSpace { +public: + static RefPtr<DeviceGrayColorSpace> the(); + + virtual ~DeviceGrayColorSpace() override = default; + + virtual Color color(const Vector<Value>& arguments) const override; + +private: + DeviceGrayColorSpace() = default; +}; + +class DeviceRGBColorSpace final : public ColorSpace { +public: + static RefPtr<DeviceRGBColorSpace> the(); + + virtual ~DeviceRGBColorSpace() override = default; + + virtual Color color(const Vector<Value>& arguments) const override; + +private: + DeviceRGBColorSpace() = default; +}; + +class DeviceCMYKColorSpace final : public ColorSpace { +public: + static RefPtr<DeviceCMYKColorSpace> the(); + + virtual ~DeviceCMYKColorSpace() override = default; + + virtual Color color(const Vector<Value>& arguments) const override; + +private: + DeviceCMYKColorSpace() = default; +}; + +} diff --git a/Userland/Libraries/LibPDF/CommonNames.h b/Userland/Libraries/LibPDF/CommonNames.h index fe4010dc7e..b300c81396 100644 --- a/Userland/Libraries/LibPDF/CommonNames.h +++ b/Userland/Libraries/LibPDF/CommonNames.h @@ -12,14 +12,20 @@ V(ASCII85Decode) \ V(ASCIIHexDecode) \ V(BaseFont) \ + V(BlackPoint) \ V(C) \ + V(CalRGB) \ V(CCITTFaxDecode) \ + V(ColorSpace) \ V(Contents) \ V(Count) \ V(CropBox) \ V(Crypt) \ V(DCTDecode) \ V(Dest) \ + V(DeviceCMYK) \ + V(DeviceGray) \ + V(DeviceRGB) \ V(E) \ V(F) \ V(Filter) \ @@ -33,6 +39,7 @@ V(FitV) \ V(FlateDecode) \ V(Font) \ + V(Gamma) \ V(H) \ V(JBIG2Decode) \ V(JPXDecode) \ @@ -42,6 +49,7 @@ V(Last) \ V(Length) \ V(Linearized) \ + V(Matrix) \ V(MediaBox) \ V(N) \ V(Next) \ @@ -50,6 +58,7 @@ V(P) \ V(Pages) \ V(Parent) \ + V(Pattern) \ V(Prev) \ V(Resources) \ V(Root) \ @@ -59,6 +68,7 @@ V(Title) \ V(Type) \ V(UserUnit) \ + V(WhitePoint) \ V(XYZ) namespace PDF { diff --git a/Userland/Libraries/LibPDF/Document.cpp b/Userland/Libraries/LibPDF/Document.cpp index a127a4359f..c545e781dd 100644 --- a/Userland/Libraries/LibPDF/Document.cpp +++ b/Userland/Libraries/LibPDF/Document.cpp @@ -89,6 +89,11 @@ Page Document::get_page(u32 index) auto page_object_index = m_page_object_indices[index]; auto raw_page_object = resolve_to<DictObject>(get_or_load_value(page_object_index)); + if (!raw_page_object->contains(CommonNames::Resources)) { + // This page inherits its resource dictionary + TODO(); + } + auto resources = raw_page_object->get_dict(this, CommonNames::Resources); auto contents = raw_page_object->get_object(this, CommonNames::Contents); diff --git a/Userland/Libraries/LibPDF/Renderer.cpp b/Userland/Libraries/LibPDF/Renderer.cpp index a0de5c37f9..009b660d06 100644 --- a/Userland/Libraries/LibPDF/Renderer.cpp +++ b/Userland/Libraries/LibPDF/Renderer.cpp @@ -7,8 +7,6 @@ #include <AK/Utf8View.h> #include <LibPDF/CommonNames.h> #include <LibPDF/Renderer.h> -#include <ctype.h> -#include <math.h> #define RENDERER_HANDLER(name) \ void Renderer::handle_##name([[maybe_unused]] const Vector<Value>& args) @@ -22,58 +20,6 @@ namespace PDF { -Optional<ColorSpace::Type> ColorSpace::color_space_from_string(const FlyString& str) -{ -#define ENUM(name) \ - if (str == #name) \ - return ColorSpace::Type::name; - ENUMERATE_COLOR_SPACES(ENUM) -#undef ENUM - - return {}; -} - -Color ColorSpace::default_color_for_color_space(ColorSpace::Type color_space) -{ - switch (color_space) { - case Type::DeviceGray: - case Type::DeviceRGB: - return Color::NamedColor::Black; - case Type::DeviceCMYK: - return Color::from_cmyk(1.0f, 1.0f, 1.0f, 0.0f); - default: - TODO(); - } -} - -Color ColorSpace::color_from_parameters(ColorSpace::Type color_space, const Vector<Value>& args) -{ - switch (color_space) { - case Type::DeviceGray: { - VERIFY(args.size() == 1); - auto gray = static_cast<u8>(args[0].to_float() * 255.0f); - return Color(gray, gray, gray); - } - case Type::DeviceRGB: { - VERIFY(args.size() == 3); - auto r = static_cast<u8>(args[0].to_float() * 255.0f); - auto g = static_cast<u8>(args[1].to_float() * 255.0f); - auto b = static_cast<u8>(args[2].to_float() * 255.0f); - return Color(r, g, b); - } - case Type::DeviceCMYK: { - VERIFY(args.size() == 4); - auto c = args[0].to_float(); - auto m = args[1].to_float(); - auto y = args[2].to_float(); - auto k = args[3].to_float(); - return Color::from_cmyk(c, m, y, k); - } - default: - TODO(); - } -} - void Renderer::render(Document& document, const Page& page, RefPtr<Gfx::Bitmap> bitmap) { Renderer(document, page, bitmap).render(); @@ -438,63 +384,63 @@ RENDERER_TODO(type3_font_set_glyph_width_and_bbox); RENDERER_HANDLER(set_stroking_space) { state().stroke_color_space = get_color_space(args[0]); - state().stroke_color = ColorSpace::default_color_for_color_space(state().stroke_color_space); + VERIFY(state().stroke_color_space); } RENDERER_HANDLER(set_painting_space) { state().paint_color_space = get_color_space(args[0]); - state().paint_color = ColorSpace::default_color_for_color_space(state().paint_color_space); + VERIFY(state().paint_color_space); } RENDERER_HANDLER(set_stroking_color) { - state().stroke_color = ColorSpace::color_from_parameters(state().stroke_color_space, args); + state().stroke_color = state().stroke_color_space->color(args); } RENDERER_TODO(set_stroking_color_extended); RENDERER_HANDLER(set_painting_color) { - state().paint_color = ColorSpace::color_from_parameters(state().paint_color_space, args); + state().paint_color = state().paint_color_space->color(args); } RENDERER_TODO(set_painting_color_extended); RENDERER_HANDLER(set_stroking_color_and_space_to_gray) { - state().stroke_color_space = ColorSpace::Type::DeviceGray; - state().stroke_color = ColorSpace::color_from_parameters(ColorSpace::Type::DeviceGray, args); + state().stroke_color_space = DeviceGrayColorSpace::the(); + state().stroke_color = state().stroke_color_space->color(args); } RENDERER_HANDLER(set_painting_color_and_space_to_gray) { - state().paint_color_space = ColorSpace::Type::DeviceGray; - state().paint_color = ColorSpace::color_from_parameters(ColorSpace::Type::DeviceGray, args); + state().paint_color_space = DeviceGrayColorSpace::the(); + state().paint_color = state().paint_color_space->color(args); } RENDERER_HANDLER(set_stroking_color_and_space_to_rgb) { - state().stroke_color_space = ColorSpace::Type::DeviceRGB; - state().stroke_color = ColorSpace::color_from_parameters(ColorSpace::Type::DeviceRGB, args); + state().stroke_color_space = DeviceRGBColorSpace::the(); + state().stroke_color = state().stroke_color_space->color(args); } RENDERER_HANDLER(set_painting_color_and_space_to_rgb) { - state().paint_color_space = ColorSpace::Type::DeviceRGB; - state().paint_color = ColorSpace::color_from_parameters(ColorSpace::Type::DeviceRGB, args); + state().paint_color_space = DeviceRGBColorSpace::the(); + state().paint_color = state().paint_color_space->color(args); } RENDERER_HANDLER(set_stroking_color_and_space_to_cmyk) { - state().stroke_color_space = ColorSpace::Type::DeviceCMYK; - state().stroke_color = ColorSpace::color_from_parameters(ColorSpace::Type::DeviceCMYK, args); + state().stroke_color_space = DeviceCMYKColorSpace::the(); + state().stroke_color = state().stroke_color_space->color(args); } RENDERER_HANDLER(set_painting_color_and_space_to_cmyk) { - state().paint_color_space = ColorSpace::Type::DeviceCMYK; - state().paint_color = ColorSpace::color_from_parameters(ColorSpace::Type::DeviceCMYK, args); + state().paint_color_space = DeviceCMYKColorSpace::the(); + state().paint_color = state().paint_color_space->color(args); } RENDERER_TODO(shade); @@ -560,16 +506,19 @@ void Renderer::show_text(const String& string, int shift) } } -ColorSpace::Type Renderer::get_color_space(const Value& value) +RefPtr<ColorSpace> Renderer::get_color_space(const Value& value) { auto name = object_cast<NameObject>(value.as_object())->name(); - auto color_space_opt = ColorSpace::color_space_from_string(name); - if (!color_space_opt.has_value()) { - // The name is probably a key into the resource dictionary - TODO(); - } - return color_space_opt.value(); + // Simple color spaces with no parameters, which can be specified directly + if (name == CommonNames::DeviceGray) + return DeviceGrayColorSpace::the(); + if (name == CommonNames::DeviceRGB) + return DeviceRGBColorSpace::the(); + if (name == CommonNames::DeviceCMYK) + return DeviceCMYKColorSpace::the(); + + TODO(); } const Gfx::AffineTransform& Renderer::calculate_text_rendering_matrix() diff --git a/Userland/Libraries/LibPDF/Renderer.h b/Userland/Libraries/LibPDF/Renderer.h index 5709d1c564..36bd103d7a 100644 --- a/Userland/Libraries/LibPDF/Renderer.h +++ b/Userland/Libraries/LibPDF/Renderer.h @@ -16,22 +16,10 @@ #include <LibGfx/Point.h> #include <LibGfx/Rect.h> #include <LibGfx/Size.h> +#include <LibPDF/ColorSpace.h> #include <LibPDF/Document.h> #include <LibPDF/Object.h> -#define ENUMERATE_COLOR_SPACES(V) \ - V(DeviceGray) \ - V(DeviceRGB) \ - V(DeviceCMYK) \ - V(CalGray) \ - V(CalRGB) \ - V(Lab) \ - V(ICCBased) \ - V(Indexed) \ - V(Pattern) \ - V(Separation) \ - V(DeviceN) - namespace PDF { enum class LineCapStyle : u8 { @@ -73,23 +61,10 @@ struct TextState { bool knockout { true }; }; -class ColorSpace { -public: - enum class Type { -#define ENUM(name) name, - ENUMERATE_COLOR_SPACES(ENUM) -#undef ENUM - }; - - static Optional<ColorSpace::Type> color_space_from_string(const FlyString&); - static Color default_color_for_color_space(ColorSpace::Type); - static Color color_from_parameters(ColorSpace::Type color_space, const Vector<Value>& args); -}; - struct GraphicsState { Gfx::AffineTransform ctm; - ColorSpace::Type stroke_color_space { ColorSpace::Type::DeviceGray }; - ColorSpace::Type paint_color_space { ColorSpace::Type::DeviceGray }; + RefPtr<ColorSpace> stroke_color_space { DeviceGrayColorSpace::the() }; + RefPtr<ColorSpace> paint_color_space { DeviceGrayColorSpace::the() }; Gfx::Color stroke_color { Gfx::Color::NamedColor::Black }; Gfx::Color paint_color { Gfx::Color::NamedColor::Black }; float line_width { 1.0f }; @@ -119,7 +94,7 @@ private: // shift is the manual advance given in the TJ command array void show_text(const String&, int shift = 0); - ColorSpace::Type get_color_space(const Value&); + RefPtr<ColorSpace> get_color_space(const Value&); ALWAYS_INLINE const GraphicsState& state() const { return m_graphics_state_stack.last(); } ALWAYS_INLINE GraphicsState& state() { return m_graphics_state_stack.last(); } |