@@ -45,7 +45,7 @@ PDFErrorOr<NonnullRefPtr<ColorSpace>> ColorSpace::create(Document* document, Non
4545 return Error { Error::Type::MalformedPDF, " Color space must be name or array" };
4646}
4747
48- PDFErrorOr<NonnullRefPtr<ColorSpace>> ColorSpace::create (DeprecatedFlyString const & name, Renderer&)
48+ PDFErrorOr<NonnullRefPtr<ColorSpace>> ColorSpace::create (DeprecatedFlyString const & name, Renderer& renderer )
4949{
5050 // Simple color spaces with no parameters, which can be specified directly
5151 if (name == CommonNames::DeviceGray)
@@ -55,7 +55,7 @@ PDFErrorOr<NonnullRefPtr<ColorSpace>> ColorSpace::create(DeprecatedFlyString con
5555 if (name == CommonNames::DeviceCMYK)
5656 return DeviceCMYKColorSpace::the ();
5757 if (name == CommonNames::Pattern)
58- return Error::rendering_unsupported_error ( " Pattern color spaces not yet implemented " );
58+ return PatternColorSpace::create (renderer );
5959 VERIFY_NOT_REACHED ();
6060}
6161
@@ -86,9 +86,6 @@ PDFErrorOr<NonnullRefPtr<ColorSpace>> ColorSpace::create(Document* document, Non
8686 if (color_space_name == CommonNames::Lab)
8787 return TRY (LabColorSpace::create (document, move (parameters)));
8888
89- if (color_space_name == CommonNames::Pattern)
90- return Error::rendering_unsupported_error (" Pattern color spaces not yet implemented" );
91-
9289 if (color_space_name == CommonNames::Separation)
9390 return TRY (SeparationColorSpace::create (document, move (parameters), renderer));
9491
@@ -773,4 +770,104 @@ Vector<float> SeparationColorSpace::default_decode() const
773770{
774771 return { 0 .0f , 1 .0f };
775772}
773+ NonnullRefPtr<PatternColorSpace> PatternColorSpace::create (Renderer& renderer)
774+ {
775+ return adopt_ref (*new PatternColorSpace (renderer));
776+ }
777+
778+ PDFErrorOr<ColorOrStyle> PatternColorSpace::style (ReadonlySpan<Value> arguments) const
779+ {
780+ VERIFY (arguments.size () >= 1 );
781+
782+ auto resources = m_renderer.m_page .resources ;
783+
784+ auto pattern_resource = resources->get_value (CommonNames::Pattern);
785+ auto doc_pattern_dict = pattern_resource.get <NonnullRefPtr<Object>>()->cast <DictObject>();
786+
787+ auto const & pattern_name = arguments.last ().get <NonnullRefPtr<Object>>()->cast <NameObject>()->name ();
788+ if (!doc_pattern_dict->contains (pattern_name))
789+ return Error::malformed_error (" Pattern dictionary does not contain pattern {}" , pattern_name);
790+
791+ auto const pattern = TRY (m_renderer.m_document ->resolve_to <StreamObject>(doc_pattern_dict->get_value (pattern_name)));
792+
793+ auto const type = pattern->dict ()->get (CommonNames::Type)->get <NonnullRefPtr<Object>>()->cast <NameObject>();
794+ if (type->name () != CommonNames::Pattern)
795+ return Error::rendering_unsupported_error (" Unsupported pattern type {}" , type->name ());
796+
797+ auto const pattern_type = pattern->dict ()->get (CommonNames::PatternType)->get_u16 ();
798+ if (pattern_type != 1 )
799+ return Error::rendering_unsupported_error (" Unsupported pattern type {}" , pattern_type);
800+
801+ auto const pattern_paint_type = pattern->dict ()->get (" PaintType" )->get_u16 ();
802+ if (pattern_paint_type != 1 )
803+ return Error::rendering_unsupported_error (" Unsupported pattern paint type {}" , pattern_paint_type);
804+
805+ Vector<Value> pattern_matrix;
806+ if (pattern->dict ()->contains (CommonNames::Matrix)) {
807+ pattern_matrix = pattern->dict ()->get_array (m_renderer.m_document , CommonNames::Matrix).value ()->elements ();
808+ } else {
809+ pattern_matrix = Vector { Value { 1 }, Value { 0 }, Value { 0 }, Value { 1 }, Value { 0 }, Value { 0 } };
810+ }
811+
812+ auto pattern_bounding_box = pattern->dict ()->get_array (m_renderer.m_document , " BBox" ).value ()->elements ();
813+ auto pattern_transform = Gfx::AffineTransform (
814+ pattern_matrix[0 ].to_float (),
815+ pattern_matrix[1 ].to_float (),
816+ pattern_matrix[2 ].to_float (),
817+ pattern_matrix[3 ].to_float (),
818+ pattern_matrix[4 ].to_float (),
819+ pattern_matrix[5 ].to_float ());
820+
821+ // To get the device space size for the bitmap, apply the pattern transform to the pattern space bounding box, and then apply the initial ctm.
822+ // NB: the pattern pattern_matrix maps pattern space to the default (initial) coordinate space of the page. (i.e cannot be updated via cm).
823+
824+ auto initial_ctm = Gfx::AffineTransform (m_renderer.m_graphics_state_stack .first ().ctm );
825+ initial_ctm.set_translation (0 , 0 );
826+ initial_ctm.set_scale (initial_ctm.x_scale (), initial_ctm.y_scale ());
827+
828+ auto pattern_space_lower_left = Gfx::FloatPoint { pattern_bounding_box[0 ].to_int (), pattern_bounding_box[1 ].to_int () };
829+ auto pattern_space_upper_right = Gfx::FloatPoint { pattern_bounding_box[2 ].to_int (), pattern_bounding_box[3 ].to_int () };
830+
831+ auto device_space_lower_left = initial_ctm.map (pattern_transform.map (pattern_space_lower_left));
832+ auto device_space_upper_right = initial_ctm.map (pattern_transform.map (pattern_space_upper_right));
833+
834+ auto bitmap_width = (int )device_space_upper_right.x () - (int )device_space_lower_left.x ();
835+ auto bitmap_height = (int )device_space_upper_right.y () - (int )device_space_lower_left.y ();
836+
837+ auto pattern_cell = TRY (Gfx::Bitmap::create (Gfx::BitmapFormat::BGRA8888, { bitmap_width, bitmap_height }));
838+ auto page = Page (m_renderer.m_page );
839+ page.media_box = Rectangle {
840+ pattern_space_lower_left.x (), pattern_space_lower_left.y (),
841+ pattern_space_upper_right.x (), pattern_space_upper_right.y ()
842+ };
843+ page.crop_box = page.media_box ;
844+
845+ auto pattern_renderer = Renderer (m_renderer.m_document , page, pattern_cell, {}, m_renderer.m_rendering_preferences );
846+
847+ auto operators = TRY (Parser::parse_operators (m_renderer.m_document , pattern->bytes ()));
848+ for (auto & op : operators)
849+ TRY (pattern_renderer.handle_operator (op, resources));
850+
851+ auto x_steps = pattern->dict ()->get (" XStep" ).value_or (bitmap_width).to_int ();
852+ auto y_steps = pattern->dict ()->get (" YStep" ).value_or (bitmap_height).to_int ();
853+
854+ auto device_space_steps = initial_ctm.map (pattern_transform.map (Gfx::IntPoint { x_steps, y_steps }));
855+
856+ NonnullRefPtr<Gfx::PaintStyle> style = MUST (Gfx::RepeatingBitmapPaintStyle::create (
857+ *pattern_cell,
858+ device_space_steps,
859+ {}));
860+
861+ return style;
862+ }
863+ int PatternColorSpace::number_of_components () const
864+ {
865+ // Not permitted
866+ VERIFY_NOT_REACHED ();
867+ }
868+ Vector<float > PatternColorSpace::default_decode () const
869+ {
870+ // Not permitted
871+ VERIFY_NOT_REACHED ();
872+ }
776873}
0 commit comments