diff --git a/Userland/Libraries/LibPDF/Pattern.cpp b/Userland/Libraries/LibPDF/Pattern.cpp index 384b239ea4f..e00f65f5d8a 100644 --- a/Userland/Libraries/LibPDF/Pattern.cpp +++ b/Userland/Libraries/LibPDF/Pattern.cpp @@ -13,6 +13,47 @@ namespace PDF { +namespace { + +// Entries present in both: +// TABLE 4.25 Additional entries specific to a type 1 pattern dictionary +// TABLE 4.26 Entries in a type 2 pattern dictionary +struct CommonEntries { + // "(Optional) An array of six numbers specifying the pattern matrix (see Section + // 4.6.1, “General Properties of Patterns”). Default value: the identity matrix + // [ 1 0 0 1 0 0 ]." + Gfx::AffineTransform matrix { 1, 0, 0, 1, 0, 0 }; +}; + +PDFErrorOr read_common_entries(Document* document, DictObject const& pattern_dict) +{ + // "Type (Optional) The type of PDF object that this dictionary describes; + // if present, shall be Pattern for a pattern dictionary."" + auto const type = pattern_dict.get(CommonNames::Type); + if (type.has_value()) { + auto type_name = type->get>()->cast(); + if (type_name->name() != CommonNames::Pattern) + return Error::rendering_unsupported_error("Unsupported pattern type {}", type_name->name()); + } + + CommonEntries common_entries; + + if (pattern_dict.contains(CommonNames::Matrix)) { + auto pattern_matrix = pattern_dict.get_array(document, CommonNames::Matrix).value()->elements(); + common_entries.matrix = Gfx::AffineTransform( + pattern_matrix[0].to_float(), + pattern_matrix[1].to_float(), + pattern_matrix[2].to_float(), + pattern_matrix[3].to_float(), + pattern_matrix[4].to_float(), + pattern_matrix[5].to_float()); + } + + return common_entries; +} + +} + PDFErrorOr Pattern::style(Document*, NonnullRefPtr pattern, Renderer& renderer) { NonnullRefPtr pattern_dict = [&] { @@ -22,52 +63,19 @@ PDFErrorOr Pattern::style(Document*, NonnullRefPtr pattern return pattern->cast()->dict(); }(); - // PDF 1.7 spec, 4.6 Patterns - // TABLE 4.25 Additional entries specific to a type 1 pattern dictionary - // TABLE 4.26 Entries in a type 2 pattern dictionary - // Both have: - // - /Type (optional) - // - /PatternType (required) - // - /Matrix (optional) - // The other entries are specific to the pattern type. - // PatternType (Required) A code identifying the type of pattern that this dictionary describes; // shall be 1 for a tiling pattern auto const pattern_type = pattern_dict->get(CommonNames::PatternType)->get_u16(); if (pattern_type != 1) return Error::rendering_unsupported_error("Unsupported pattern type {}", pattern_type); - // Type (Optional) The type of PDF object that this dictionary describes; - // if present, shall be Pattern for a pattern dictionary - auto const type = pattern_dict->get(CommonNames::Type); - if (type.has_value()) { - auto type_name = type->get>()->cast(); - if (type_name->name() != CommonNames::Pattern) - return Error::rendering_unsupported_error("Unsupported pattern type {}", type_name->name()); - } + auto common_entries = TRY(read_common_entries(renderer.m_document, *pattern_dict)); // PaintType (Required) A code that determines how the colour of the pattern cell shall be specified auto const pattern_paint_type = pattern_dict->get("PaintType")->get_u16(); if (pattern_paint_type != 1) return Error::rendering_unsupported_error("Unsupported pattern paint type {}", pattern_paint_type); - // Matrix (Optional) An array of six numbers specifying the pattern matrix - Vector pattern_matrix; - if (pattern_dict->contains(CommonNames::Matrix)) { - pattern_matrix = pattern_dict->get_array(renderer.m_document, CommonNames::Matrix).value()->elements(); - } else { - pattern_matrix = Vector { Value { 1 }, Value { 0 }, Value { 0 }, Value { 1 }, Value { 0 }, Value { 0 } }; - } - - auto pattern_bounding_box = pattern_dict->get_array(renderer.m_document, "BBox").value()->elements(); - auto pattern_transform = Gfx::AffineTransform( - pattern_matrix[0].to_float(), - pattern_matrix[1].to_float(), - pattern_matrix[2].to_float(), - pattern_matrix[3].to_float(), - pattern_matrix[4].to_float(), - pattern_matrix[5].to_float()); - // 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. // NB: the pattern pattern_matrix maps pattern space to the default (initial) coordinate space of the page. (i.e cannot be updated via cm). @@ -75,11 +83,13 @@ PDFErrorOr Pattern::style(Document*, NonnullRefPtr pattern initial_ctm.set_translation(0, 0); initial_ctm.set_scale(initial_ctm.x_scale(), initial_ctm.y_scale()); + auto pattern_bounding_box = pattern_dict->get_array(renderer.m_document, "BBox").value()->elements(); + auto pattern_space_lower_left = Gfx::FloatPoint { pattern_bounding_box[0].to_int(), pattern_bounding_box[1].to_int() }; auto pattern_space_upper_right = Gfx::FloatPoint { pattern_bounding_box[2].to_int(), pattern_bounding_box[3].to_int() }; - auto device_space_lower_left = initial_ctm.map(pattern_transform.map(pattern_space_lower_left)); - auto device_space_upper_right = initial_ctm.map(pattern_transform.map(pattern_space_upper_right)); + auto device_space_lower_left = initial_ctm.map(common_entries.matrix.map(pattern_space_lower_left)); + auto device_space_upper_right = initial_ctm.map(common_entries.matrix.map(pattern_space_upper_right)); auto bitmap_width = abs((int)device_space_upper_right.x() - (int)device_space_lower_left.x()); auto bitmap_height = abs((int)device_space_upper_right.y() - (int)device_space_lower_left.y()); @@ -106,7 +116,7 @@ PDFErrorOr Pattern::style(Document*, NonnullRefPtr pattern auto x_steps = pattern_dict->get("XStep").value_or(bitmap_width).to_int(); auto y_steps = pattern_dict->get("YStep").value_or(bitmap_height).to_int(); - auto device_space_steps = initial_ctm.map(pattern_transform.map(Gfx::IntPoint { x_steps, y_steps })); + auto device_space_steps = initial_ctm.map(common_entries.matrix.map(Gfx::IntPoint { x_steps, y_steps })); NonnullRefPtr style = MUST(Gfx::RepeatingBitmapPaintStyle::create( *pattern_cell,