LibPDF: Add basic color space support to the renderer

This commit only supports the three most basic color spaces:
DeviceGray, DeviceRGB, and DeviceCMYK
This commit is contained in:
Matthew Olsson 2021-05-23 12:53:38 -07:00 committed by Ali Mohammad Pur
parent f4941f5940
commit 534a2e95d2
Notes: sideshowbarker 2024-07-18 17:26:25 +09:00
3 changed files with 177 additions and 24 deletions

View file

@ -59,18 +59,18 @@
V(TextShowStringArray, text_show_string_array, TJ) \
V(Type3FontSetGlyphWidth, type3_font_set_glyph_width, d0) \
V(Type3FontSetGlyphWidthAndBBox, type3_font_set_glyph_width_and_bbox, d1) \
V(ColorSetStrokingSpace, color_set_stroking_space, CS) \
V(ColorSetPaintingSpace, color_set_painting_space, cs) \
V(ColorSetStroking, color_set_stroking, SC) \
V(ColorSetStrokingExtended, color_set_stroking_extended, SCN) \
V(ColorSetPainting, color_set_painting, sc) \
V(ColorSetPaintingExtended, color_set_painting_extended, scn) \
V(ColorSetStrokingSpaceToGray, color_set_stroking_space_to_gray, G) \
V(ColorSetPaintingSpaceToGray, color_set_painting_space_to_gray, g) \
V(ColorSetStrokingSpaceToRGB, color_set_stroking_space_to_rgb, RG) \
V(ColorSetPaintingSpaceToRGB, color_set_painting_space_to_rgb, rg) \
V(ColorSetStrokingSpaceToCMYK, color_set_stroking_space_to_cmyk, K) \
V(ColorSetPaintingSpaceToCMYK, color_set_painting_space_to_cmyk, k) \
V(SetStrokingSpace, set_stroking_space, CS) \
V(SetPaintingSpace, set_painting_space, cs) \
V(SetStrokingColor, set_stroking_color, SC) \
V(SetStrokingColorExtended, set_stroking_color_extended, SCN) \
V(SetPaintingColor, set_painting_color, sc) \
V(SetPaintingColorExtended, set_painting_color_extended, scn) \
V(SetStrokingColorAndSpaceToGray, set_stroking_color_and_space_to_gray, G) \
V(SetPaintingColorAndSpaceToGray, set_painting_color_and_space_to_gray, g) \
V(SetStrokingColorAndSpaceToRGB, set_stroking_color_and_space_to_rgb, RG) \
V(SetPaintingColorAndSpaceToRGB, set_painting_color_and_space_to_rgb, rg) \
V(SetStrokingColorAndSpaceToCMYK, set_stroking_color_and_space_to_cmyk, K) \
V(SetPaintingColorAndSpaceToCMYK, set_painting_color_and_space_to_cmyk, k) \
V(Shade, shade, sh) \
V(InlineImageBegin, inline_image_begin, BI) \
V(InlineImageBeginData, inline_image_begin_data, ID) \

View file

@ -21,6 +21,58 @@
namespace PDF {
Optional<ColorSpace::Type> ColorSpace::color_space_from_string(const StringView& 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();
@ -381,18 +433,69 @@ RENDERER_TODO(text_next_line_show_string_set_spacing);
RENDERER_TODO(text_show_string_array);
RENDERER_TODO(type3_font_set_glyph_width);
RENDERER_TODO(type3_font_set_glyph_width_and_bbox);
RENDERER_TODO(color_set_stroking_space);
RENDERER_TODO(color_set_painting_space);
RENDERER_TODO(color_set_stroking);
RENDERER_TODO(color_set_stroking_extended);
RENDERER_TODO(color_set_painting);
RENDERER_TODO(color_set_painting_extended);
RENDERER_TODO(color_set_stroking_space_to_gray);
RENDERER_TODO(color_set_painting_space_to_gray);
RENDERER_TODO(color_set_stroking_space_to_rgb);
RENDERER_TODO(color_set_painting_space_to_rgb);
RENDERER_TODO(color_set_stroking_space_to_cmyk);
RENDERER_TODO(color_set_painting_space_to_cmyk);
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);
}
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);
}
RENDERER_HANDLER(set_stroking_color)
{
state().stroke_color = ColorSpace::color_from_parameters(state().stroke_color_space, args);
}
RENDERER_TODO(set_stroking_color_extended);
RENDERER_HANDLER(set_painting_color)
{
state().paint_color = ColorSpace::color_from_parameters(state().paint_color_space, 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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
RENDERER_TODO(shade);
RENDERER_TODO(inline_image_begin);
RENDERER_TODO(inline_image_begin_data);
@ -419,6 +522,12 @@ Gfx::Size<T> Renderer::map(Gfx::Size<T> size) const
return state().ctm.map(size);
}
template<typename T>
Gfx::Rect<T> Renderer::map(Gfx::Rect<T> rect) const
{
return state().ctm.map(rect);
}
void Renderer::show_text(const String& string, int shift)
{
auto utf = Utf8View(string);
@ -449,6 +558,18 @@ void Renderer::show_text(const String& string, int shift)
}
}
ColorSpace::Type 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();
}
const Gfx::AffineTransform& Renderer::calculate_text_rendering_matrix()
{
if (m_text_rendering_matrix_is_dirty) {

View file

@ -19,6 +19,19 @@
#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 {
@ -60,8 +73,23 @@ 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 StringView&);
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 };
Gfx::Color stroke_color { Gfx::Color::NamedColor::Black };
Gfx::Color paint_color { Gfx::Color::NamedColor::Black };
float line_width { 1.0f };
@ -91,6 +119,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&);
ALWAYS_INLINE const GraphicsState& state() const { return m_graphics_state_stack.last(); }
ALWAYS_INLINE GraphicsState& state() { return m_graphics_state_stack.last(); }
@ -103,6 +132,9 @@ private:
template<typename T>
ALWAYS_INLINE Gfx::Size<T> map(Gfx::Size<T>) const;
template<typename T>
ALWAYS_INLINE Gfx::Rect<T> map(Gfx::Rect<T>) const;
const Gfx::AffineTransform& calculate_text_rendering_matrix();
RefPtr<Document> m_document;