From ba163107394a0f4797e8f7b11aa0271383aa8ed7 Mon Sep 17 00:00:00 2001 From: Rodrigo Tobar Date: Thu, 24 Nov 2022 12:40:24 +0800 Subject: [PATCH] LibPDF: Refactor parsing of ColorSpaces ColorSpaces can be specified in two ways: with a stream as operands of the color space operations (CS/cs), or as a separate PDF object, which is then referred to by other means (e.g., from Image XObjects and other entities). These two modes of addressing a ColorSpace are slightly different and need to be addressed separately. However, the current implementation embedded the full logic of the first case in the routine that created ColorSpace objects. This commit refactors the creation of ColorSpace to support both cases. First, a new ColorSpaceFamily class encapsulates the static aspects of a family, like its name or whether color space construction never requires parameters. Then we define the supported ColorSpaceFamily objects. On top of this also sit a breakage on how ColorSpaces are created. Two methods are now offered: one only providing construction of no-argument color spaces (and thus taking a simple name), and another taking an ArrayObject, hence used to create ColorSpaces requiring arguments. Finally, on top of *that* two ways to get a color space in the Renderer are made available: the first creates a ColorSpace with a name and a Resources dictionary, and another takes an Object. These model the two addressing modes described above. --- Userland/Libraries/LibPDF/ColorSpace.cpp | 44 +++++++++++++------ Userland/Libraries/LibPDF/ColorSpace.h | 56 +++++++++++++++++------- Userland/Libraries/LibPDF/Renderer.cpp | 28 +++++++++--- Userland/Libraries/LibPDF/Renderer.h | 3 +- 4 files changed, 97 insertions(+), 34 deletions(-) diff --git a/Userland/Libraries/LibPDF/ColorSpace.cpp b/Userland/Libraries/LibPDF/ColorSpace.cpp index d9b87a0dc9..943f9b96fb 100644 --- a/Userland/Libraries/LibPDF/ColorSpace.cpp +++ b/Userland/Libraries/LibPDF/ColorSpace.cpp @@ -11,7 +11,23 @@ namespace PDF { -PDFErrorOr> ColorSpace::create(Document* document, FlyString const& name, NonnullRefPtr resources) +#define ENUMERATE(name, ever_needs_parameters) \ + ColorSpaceFamily ColorSpaceFamily::name { #name, ever_needs_parameters }; +ENUMERATE_COLOR_SPACE_FAMILIES(ENUMERATE); +#undef ENUMERATE + +PDFErrorOr ColorSpaceFamily::get(FlyString const& family_name) +{ +#define ENUMERATE(f_name, ever_needs_parameters) \ + if (family_name == f_name.name()) { \ + return ColorSpaceFamily::f_name; \ + } + ENUMERATE_COLOR_SPACE_FAMILIES(ENUMERATE) +#undef ENUMERATE + return Error(Error::Type::MalformedPDF, DeprecatedString::formatted("Unknown ColorSpace family {}", family_name)); +} + +PDFErrorOr> ColorSpace::create(FlyString const& name) { // Simple color spaces with no parameters, which can be specified directly if (name == CommonNames::DeviceGray) @@ -22,14 +38,11 @@ PDFErrorOr> ColorSpace::create(Document* document, Fly return DeviceCMYKColorSpace::the(); if (name == CommonNames::Pattern) TODO(); + VERIFY_NOT_REACHED(); +} - // The color space is a complex color space with parameters that resides in - // the resource dictionary - auto color_space_resource_dict = TRY(resources->get_dict(document, CommonNames::ColorSpace)); - if (!color_space_resource_dict->contains(name)) - TODO(); - - auto color_space_array = TRY(color_space_resource_dict->get_array(document, name)); +PDFErrorOr> ColorSpace::create(Document* document, NonnullRefPtr color_space_array) +{ auto color_space_name = TRY(color_space_array->get_name_at(document, 0))->name(); Vector parameters; @@ -41,7 +54,7 @@ PDFErrorOr> ColorSpace::create(Document* document, Fly return TRY(CalRGBColorSpace::create(document, move(parameters))); if (color_space_name == CommonNames::ICCBased) - return TRY(ICCBasedColorSpace::create(document, resources, move(parameters))); + return TRY(ICCBasedColorSpace::create(document, move(parameters))); dbgln("Unknown color space: {}", color_space_name); TODO(); @@ -261,7 +274,7 @@ Color CalRGBColorSpace::color(Vector const& arguments) const return Color(red, green, blue); } -PDFErrorOr> ICCBasedColorSpace::create(Document* document, NonnullRefPtr resources, Vector&& parameters) +PDFErrorOr> ICCBasedColorSpace::create(Document* document, Vector&& parameters) { if (parameters.is_empty()) return Error { Error::Type::MalformedPDF, "ICCBased color space expected one parameter" }; @@ -283,11 +296,16 @@ PDFErrorOr> ICCBasedColorSpace::create(Document* docum name = CommonNames::DeviceCMYK; else VERIFY_NOT_REACHED(); - } else { - name = TRY(dict->get_name(document, CommonNames::Alternate))->name(); + return ColorSpace::create(name); } - return TRY(ColorSpace::create(document, name, resources)); + auto alternate_color_space_object = MUST(dict->get_object(document, CommonNames::Alternate)); + if (alternate_color_space_object->is()) { + return ColorSpace::create(alternate_color_space_object->cast()->name()); + } + + dbgln("Alternate color spaces in array format not supported yet "); + TODO(); } Color ICCBasedColorSpace::color(Vector const&) const diff --git a/Userland/Libraries/LibPDF/ColorSpace.h b/Userland/Libraries/LibPDF/ColorSpace.h index 60eaf79ff8..ab1e8c4da5 100644 --- a/Userland/Libraries/LibPDF/ColorSpace.h +++ b/Userland/Libraries/LibPDF/ColorSpace.h @@ -10,30 +10,51 @@ #include #include -#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) +#define ENUMERATE_COLOR_SPACE_FAMILIES(V) \ + V(DeviceGray, true) \ + V(DeviceRGB, true) \ + V(DeviceCMYK, true) \ + V(CalGray, false) \ + V(CalRGB, false) \ + V(Lab, false) \ + V(ICCBased, false) \ + V(Indexed, false) \ + V(Pattern, false) \ + V(Separation, false) \ + V(DeviceN, false) namespace PDF { -struct Page; +class ColorSpaceFamily { +public: + ColorSpaceFamily(FlyString name, bool never_needs_paramaters_p) + : m_name(move(name)) + , m_never_needs_parameters(never_needs_paramaters_p) + { + } + + FlyString name() const { return m_name; }; + bool never_needs_parameters() const { return m_never_needs_parameters; }; + static PDFErrorOr get(FlyString const&); + +#define ENUMERATE(name, ever_needs_parameters) static ColorSpaceFamily name; + ENUMERATE_COLOR_SPACE_FAMILIES(ENUMERATE) +#undef ENUMERATE + +private: + FlyString m_name; + bool m_never_needs_parameters; +}; class ColorSpace : public RefCounted { public: - static PDFErrorOr> create(Document*, FlyString const& name, NonnullRefPtr resources); + static PDFErrorOr> create(FlyString const&); + static PDFErrorOr> create(Document*, NonnullRefPtr); virtual ~ColorSpace() = default; virtual Color color(Vector const& arguments) const = 0; + virtual ColorSpaceFamily const& family() const = 0; }; class DeviceGrayColorSpace final : public ColorSpace { @@ -43,6 +64,7 @@ public: ~DeviceGrayColorSpace() override = default; Color color(Vector const& arguments) const override; + ColorSpaceFamily const& family() const override { return ColorSpaceFamily::DeviceGray; } private: DeviceGrayColorSpace() = default; @@ -55,6 +77,7 @@ public: ~DeviceRGBColorSpace() override = default; Color color(Vector const& arguments) const override; + ColorSpaceFamily const& family() const override { return ColorSpaceFamily::DeviceRGB; } private: DeviceRGBColorSpace() = default; @@ -67,6 +90,7 @@ public: ~DeviceCMYKColorSpace() override = default; Color color(Vector const& arguments) const override; + ColorSpaceFamily const& family() const override { return ColorSpaceFamily::DeviceCMYK; } private: DeviceCMYKColorSpace() = default; @@ -79,6 +103,7 @@ public: ~CalRGBColorSpace() override = default; Color color(Vector const& arguments) const override; + ColorSpaceFamily const& family() const override { return ColorSpaceFamily::CalRGB; } private: CalRGBColorSpace() = default; @@ -91,11 +116,12 @@ private: class ICCBasedColorSpace final : public ColorSpace { public: - static PDFErrorOr> create(Document*, NonnullRefPtr resources, Vector&& parameters); + static PDFErrorOr> create(Document*, Vector&& parameters); ~ICCBasedColorSpace() override = default; Color color(Vector const& arguments) const override; + ColorSpaceFamily const& family() const override { return ColorSpaceFamily::ICCBased; } private: ICCBasedColorSpace() = delete; diff --git a/Userland/Libraries/LibPDF/Renderer.cpp b/Userland/Libraries/LibPDF/Renderer.cpp index e80d6213d1..0fde4418ed 100644 --- a/Userland/Libraries/LibPDF/Renderer.cpp +++ b/Userland/Libraries/LibPDF/Renderer.cpp @@ -523,14 +523,14 @@ RENDERER_TODO(type3_font_set_glyph_width_and_bbox) RENDERER_HANDLER(set_stroking_space) { - state().stroke_color_space = TRY(get_color_space(args[0], extra_resources.value_or(m_page.resources))); + state().stroke_color_space = TRY(get_color_space_from_resources(args[0], extra_resources.value_or(m_page.resources))); VERIFY(state().stroke_color_space); return {}; } RENDERER_HANDLER(set_painting_space) { - state().paint_color_space = TRY(get_color_space(args[0], extra_resources.value_or(m_page.resources))); + state().paint_color_space = TRY(get_color_space_from_resources(args[0], extra_resources.value_or(m_page.resources))); VERIFY(state().paint_color_space); return {}; } @@ -759,10 +759,28 @@ void Renderer::show_text(DeprecatedString const& string) m_text_matrix.translate(delta_x / text_rendering_matrix.x_scale(), 0.0f); } -PDFErrorOr> Renderer::get_color_space(Value const& value, NonnullRefPtr resources) +PDFErrorOr> Renderer::get_color_space_from_resources(Value const& value, NonnullRefPtr resources) { - auto name = value.get>()->cast()->name(); - return TRY(ColorSpace::create(m_document, name, resources)); + auto color_space_name = value.get>()->cast()->name(); + auto maybe_color_space_family = ColorSpaceFamily::get(color_space_name); + if (!maybe_color_space_family.is_error()) { + auto color_space_family = maybe_color_space_family.release_value(); + if (color_space_family.never_needs_parameters()) { + return ColorSpace::create(color_space_name); + } + } + auto color_space_resource_dict = TRY(resources->get_dict(m_document, CommonNames::ColorSpace)); + auto color_space_array = TRY(color_space_resource_dict->get_array(m_document, color_space_name)); + return ColorSpace::create(m_document, color_space_array); +} + +PDFErrorOr> Renderer::get_color_space_from_document(NonnullRefPtr color_space_object) +{ + // Pattern cannot be a name in these cases + if (color_space_object->is()) { + return ColorSpace::create(color_space_object->cast()->name()); + } + return ColorSpace::create(m_document, color_space_object->cast()); } Gfx::AffineTransform const& Renderer::calculate_text_rendering_matrix() diff --git a/Userland/Libraries/LibPDF/Renderer.h b/Userland/Libraries/LibPDF/Renderer.h index 9ca5cab815..762c2207c8 100644 --- a/Userland/Libraries/LibPDF/Renderer.h +++ b/Userland/Libraries/LibPDF/Renderer.h @@ -109,7 +109,8 @@ private: void end_path_paint(); PDFErrorOr set_graphics_state_from_dict(NonnullRefPtr); void show_text(DeprecatedString const&); - PDFErrorOr> get_color_space(Value const&, NonnullRefPtr); + PDFErrorOr> get_color_space_from_resources(Value const&, NonnullRefPtr); + PDFErrorOr> get_color_space_from_document(NonnullRefPtr); ALWAYS_INLINE GraphicsState const& state() const { return m_graphics_state_stack.last(); } ALWAYS_INLINE GraphicsState& state() { return m_graphics_state_stack.last(); }