mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 11:07:35 +00:00
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:
parent
f4941f5940
commit
534a2e95d2
3 changed files with 177 additions and 24 deletions
|
@ -59,18 +59,18 @@
|
||||||
V(TextShowStringArray, text_show_string_array, TJ) \
|
V(TextShowStringArray, text_show_string_array, TJ) \
|
||||||
V(Type3FontSetGlyphWidth, type3_font_set_glyph_width, d0) \
|
V(Type3FontSetGlyphWidth, type3_font_set_glyph_width, d0) \
|
||||||
V(Type3FontSetGlyphWidthAndBBox, type3_font_set_glyph_width_and_bbox, d1) \
|
V(Type3FontSetGlyphWidthAndBBox, type3_font_set_glyph_width_and_bbox, d1) \
|
||||||
V(ColorSetStrokingSpace, color_set_stroking_space, CS) \
|
V(SetStrokingSpace, set_stroking_space, CS) \
|
||||||
V(ColorSetPaintingSpace, color_set_painting_space, cs) \
|
V(SetPaintingSpace, set_painting_space, cs) \
|
||||||
V(ColorSetStroking, color_set_stroking, SC) \
|
V(SetStrokingColor, set_stroking_color, SC) \
|
||||||
V(ColorSetStrokingExtended, color_set_stroking_extended, SCN) \
|
V(SetStrokingColorExtended, set_stroking_color_extended, SCN) \
|
||||||
V(ColorSetPainting, color_set_painting, sc) \
|
V(SetPaintingColor, set_painting_color, sc) \
|
||||||
V(ColorSetPaintingExtended, color_set_painting_extended, scn) \
|
V(SetPaintingColorExtended, set_painting_color_extended, scn) \
|
||||||
V(ColorSetStrokingSpaceToGray, color_set_stroking_space_to_gray, G) \
|
V(SetStrokingColorAndSpaceToGray, set_stroking_color_and_space_to_gray, G) \
|
||||||
V(ColorSetPaintingSpaceToGray, color_set_painting_space_to_gray, g) \
|
V(SetPaintingColorAndSpaceToGray, set_painting_color_and_space_to_gray, g) \
|
||||||
V(ColorSetStrokingSpaceToRGB, color_set_stroking_space_to_rgb, RG) \
|
V(SetStrokingColorAndSpaceToRGB, set_stroking_color_and_space_to_rgb, RG) \
|
||||||
V(ColorSetPaintingSpaceToRGB, color_set_painting_space_to_rgb, rg) \
|
V(SetPaintingColorAndSpaceToRGB, set_painting_color_and_space_to_rgb, rg) \
|
||||||
V(ColorSetStrokingSpaceToCMYK, color_set_stroking_space_to_cmyk, K) \
|
V(SetStrokingColorAndSpaceToCMYK, set_stroking_color_and_space_to_cmyk, K) \
|
||||||
V(ColorSetPaintingSpaceToCMYK, color_set_painting_space_to_cmyk, k) \
|
V(SetPaintingColorAndSpaceToCMYK, set_painting_color_and_space_to_cmyk, k) \
|
||||||
V(Shade, shade, sh) \
|
V(Shade, shade, sh) \
|
||||||
V(InlineImageBegin, inline_image_begin, BI) \
|
V(InlineImageBegin, inline_image_begin, BI) \
|
||||||
V(InlineImageBeginData, inline_image_begin_data, ID) \
|
V(InlineImageBeginData, inline_image_begin_data, ID) \
|
||||||
|
|
|
@ -21,6 +21,58 @@
|
||||||
|
|
||||||
namespace PDF {
|
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)
|
void Renderer::render(Document& document, const Page& page, RefPtr<Gfx::Bitmap> bitmap)
|
||||||
{
|
{
|
||||||
Renderer(document, page, bitmap).render();
|
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(text_show_string_array);
|
||||||
RENDERER_TODO(type3_font_set_glyph_width);
|
RENDERER_TODO(type3_font_set_glyph_width);
|
||||||
RENDERER_TODO(type3_font_set_glyph_width_and_bbox);
|
RENDERER_TODO(type3_font_set_glyph_width_and_bbox);
|
||||||
RENDERER_TODO(color_set_stroking_space);
|
|
||||||
RENDERER_TODO(color_set_painting_space);
|
RENDERER_HANDLER(set_stroking_space)
|
||||||
RENDERER_TODO(color_set_stroking);
|
{
|
||||||
RENDERER_TODO(color_set_stroking_extended);
|
state().stroke_color_space = get_color_space(args[0]);
|
||||||
RENDERER_TODO(color_set_painting);
|
state().stroke_color = ColorSpace::default_color_for_color_space(state().stroke_color_space);
|
||||||
RENDERER_TODO(color_set_painting_extended);
|
}
|
||||||
RENDERER_TODO(color_set_stroking_space_to_gray);
|
|
||||||
RENDERER_TODO(color_set_painting_space_to_gray);
|
RENDERER_HANDLER(set_painting_space)
|
||||||
RENDERER_TODO(color_set_stroking_space_to_rgb);
|
{
|
||||||
RENDERER_TODO(color_set_painting_space_to_rgb);
|
state().paint_color_space = get_color_space(args[0]);
|
||||||
RENDERER_TODO(color_set_stroking_space_to_cmyk);
|
state().paint_color = ColorSpace::default_color_for_color_space(state().paint_color_space);
|
||||||
RENDERER_TODO(color_set_painting_space_to_cmyk);
|
}
|
||||||
|
|
||||||
|
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(shade);
|
||||||
RENDERER_TODO(inline_image_begin);
|
RENDERER_TODO(inline_image_begin);
|
||||||
RENDERER_TODO(inline_image_begin_data);
|
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);
|
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)
|
void Renderer::show_text(const String& string, int shift)
|
||||||
{
|
{
|
||||||
auto utf = Utf8View(string);
|
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()
|
const Gfx::AffineTransform& Renderer::calculate_text_rendering_matrix()
|
||||||
{
|
{
|
||||||
if (m_text_rendering_matrix_is_dirty) {
|
if (m_text_rendering_matrix_is_dirty) {
|
||||||
|
|
|
@ -19,6 +19,19 @@
|
||||||
#include <LibPDF/Document.h>
|
#include <LibPDF/Document.h>
|
||||||
#include <LibPDF/Object.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 {
|
namespace PDF {
|
||||||
|
|
||||||
enum class LineCapStyle : u8 {
|
enum class LineCapStyle : u8 {
|
||||||
|
@ -60,8 +73,23 @@ struct TextState {
|
||||||
bool knockout { true };
|
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 {
|
struct GraphicsState {
|
||||||
Gfx::AffineTransform ctm;
|
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 stroke_color { Gfx::Color::NamedColor::Black };
|
||||||
Gfx::Color paint_color { Gfx::Color::NamedColor::Black };
|
Gfx::Color paint_color { Gfx::Color::NamedColor::Black };
|
||||||
float line_width { 1.0f };
|
float line_width { 1.0f };
|
||||||
|
@ -91,6 +119,7 @@ private:
|
||||||
|
|
||||||
// shift is the manual advance given in the TJ command array
|
// shift is the manual advance given in the TJ command array
|
||||||
void show_text(const String&, int shift = 0);
|
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 const GraphicsState& state() const { return m_graphics_state_stack.last(); }
|
||||||
ALWAYS_INLINE GraphicsState& state() { return m_graphics_state_stack.last(); }
|
ALWAYS_INLINE GraphicsState& state() { return m_graphics_state_stack.last(); }
|
||||||
|
@ -103,6 +132,9 @@ private:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ALWAYS_INLINE Gfx::Size<T> map(Gfx::Size<T>) const;
|
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();
|
const Gfx::AffineTransform& calculate_text_rendering_matrix();
|
||||||
|
|
||||||
RefPtr<Document> m_document;
|
RefPtr<Document> m_document;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue