LibPDF: Extract common_entries() in Pattern.cpp

No behavior change.
This commit is contained in:
Nico Weber committed 2026-08-15 21:42:41 -04:00
1 parent dddae788ab
commit 95b0add504
1 file changed
+47 -37
+47 -37
View File
@@ -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<CommonEntries> 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<NonnullRefPtr<Object>>()->cast<NameObject>();
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<ColorOrStyle> Pattern::style(Document*, NonnullRefPtr<Object> pattern, Renderer& renderer)
{
NonnullRefPtr<DictObject> pattern_dict = [&] {
@@ -22,52 +63,19 @@ PDFErrorOr<ColorOrStyle> Pattern::style(Document*, NonnullRefPtr<Object> pattern
return pattern->cast<StreamObject>()->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<NonnullRefPtr<Object>>()->cast<NameObject>();
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<Value> 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<ColorOrStyle> Pattern::style(Document*, NonnullRefPtr<Object> 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<ColorOrStyle> Pattern::style(Document*, NonnullRefPtr<Object> 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<Gfx::PaintStyle> style = MUST(Gfx::RepeatingBitmapPaintStyle::create(
*pattern_cell,