1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 05:27:45 +00:00

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.
This commit is contained in:
Rodrigo Tobar 2022-11-24 12:40:24 +08:00 committed by Andreas Kling
parent 287bb0feac
commit ba16310739
4 changed files with 97 additions and 34 deletions

View file

@ -11,7 +11,23 @@
namespace PDF {
PDFErrorOr<NonnullRefPtr<ColorSpace>> ColorSpace::create(Document* document, FlyString const& name, NonnullRefPtr<DictObject> resources)
#define ENUMERATE(name, ever_needs_parameters) \
ColorSpaceFamily ColorSpaceFamily::name { #name, ever_needs_parameters };
ENUMERATE_COLOR_SPACE_FAMILIES(ENUMERATE);
#undef ENUMERATE
PDFErrorOr<ColorSpaceFamily> 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<NonnullRefPtr<ColorSpace>> 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<NonnullRefPtr<ColorSpace>> 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<NonnullRefPtr<ColorSpace>> ColorSpace::create(Document* document, NonnullRefPtr<ArrayObject> color_space_array)
{
auto color_space_name = TRY(color_space_array->get_name_at(document, 0))->name();
Vector<Value> parameters;
@ -41,7 +54,7 @@ PDFErrorOr<NonnullRefPtr<ColorSpace>> 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<Value> const& arguments) const
return Color(red, green, blue);
}
PDFErrorOr<NonnullRefPtr<ColorSpace>> ICCBasedColorSpace::create(Document* document, NonnullRefPtr<DictObject> resources, Vector<Value>&& parameters)
PDFErrorOr<NonnullRefPtr<ColorSpace>> ICCBasedColorSpace::create(Document* document, Vector<Value>&& parameters)
{
if (parameters.is_empty())
return Error { Error::Type::MalformedPDF, "ICCBased color space expected one parameter" };
@ -283,11 +296,16 @@ PDFErrorOr<NonnullRefPtr<ColorSpace>> 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<NameObject>()) {
return ColorSpace::create(alternate_color_space_object->cast<NameObject>()->name());
}
dbgln("Alternate color spaces in array format not supported yet ");
TODO();
}
Color ICCBasedColorSpace::color(Vector<Value> const&) const

View file

@ -10,30 +10,51 @@
#include <LibGfx/Color.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)
#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<ColorSpaceFamily> 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<ColorSpace> {
public:
static PDFErrorOr<NonnullRefPtr<ColorSpace>> create(Document*, FlyString const& name, NonnullRefPtr<DictObject> resources);
static PDFErrorOr<NonnullRefPtr<ColorSpace>> create(FlyString const&);
static PDFErrorOr<NonnullRefPtr<ColorSpace>> create(Document*, NonnullRefPtr<ArrayObject>);
virtual ~ColorSpace() = default;
virtual Color color(Vector<Value> 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<Value> 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<Value> 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<Value> 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<Value> 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<NonnullRefPtr<ColorSpace>> create(Document*, NonnullRefPtr<DictObject> resources, Vector<Value>&& parameters);
static PDFErrorOr<NonnullRefPtr<ColorSpace>> create(Document*, Vector<Value>&& parameters);
~ICCBasedColorSpace() override = default;
Color color(Vector<Value> const& arguments) const override;
ColorSpaceFamily const& family() const override { return ColorSpaceFamily::ICCBased; }
private:
ICCBasedColorSpace() = delete;

View file

@ -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<NonnullRefPtr<ColorSpace>> Renderer::get_color_space(Value const& value, NonnullRefPtr<DictObject> resources)
PDFErrorOr<NonnullRefPtr<ColorSpace>> Renderer::get_color_space_from_resources(Value const& value, NonnullRefPtr<DictObject> resources)
{
auto name = value.get<NonnullRefPtr<Object>>()->cast<NameObject>()->name();
return TRY(ColorSpace::create(m_document, name, resources));
auto color_space_name = value.get<NonnullRefPtr<Object>>()->cast<NameObject>()->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<NonnullRefPtr<ColorSpace>> Renderer::get_color_space_from_document(NonnullRefPtr<Object> color_space_object)
{
// Pattern cannot be a name in these cases
if (color_space_object->is<NameObject>()) {
return ColorSpace::create(color_space_object->cast<NameObject>()->name());
}
return ColorSpace::create(m_document, color_space_object->cast<ArrayObject>());
}
Gfx::AffineTransform const& Renderer::calculate_text_rendering_matrix()

View file

@ -109,7 +109,8 @@ private:
void end_path_paint();
PDFErrorOr<void> set_graphics_state_from_dict(NonnullRefPtr<DictObject>);
void show_text(DeprecatedString const&);
PDFErrorOr<NonnullRefPtr<ColorSpace>> get_color_space(Value const&, NonnullRefPtr<DictObject>);
PDFErrorOr<NonnullRefPtr<ColorSpace>> get_color_space_from_resources(Value const&, NonnullRefPtr<DictObject>);
PDFErrorOr<NonnullRefPtr<ColorSpace>> get_color_space_from_document(NonnullRefPtr<Object>);
ALWAYS_INLINE GraphicsState const& state() const { return m_graphics_state_stack.last(); }
ALWAYS_INLINE GraphicsState& state() { return m_graphics_state_stack.last(); }