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(); }