Frame Decoration
Frame decoration is the frame-facing layout boundary: decorators bind frame state and expose frame-specific behavior, while reflowers perform sizing, positioning, margin handling, and child traversal. No successful PDF-render execution record was observed; the behavior below is source-defined.
Decoration-to-reflow boundary
flowchart TD
frame["Frame"]
decorator["AbstractFrameDecorator"]
reflower["Assigned FrameReflower"]
children["Child reflow"]
line["Block line assembly"]
frame -->|"bound by decorator constructor"| decorator
decorator -->|"reflow($block)"| reflower
reflower -->|"passes containing context"| children
reflower -->|"supplies frames when applicable"| line
Frame-facing responsibilities
[
{
"title": "AbstractFrameDecorator",
"body": "**AbstractFrameDecorator** stores the supplied **Frame** and **Dompdf** instance, then associates the frame with the decorator through `set_decorator($this)`. Source: `pdf/src/FrameDecorator/AbstractFrameDecorator.php`."
},
{
"title": "Block",
"body": "The **Block** decorator's **add_frame_to_line** first returns for out-of-flow frames, handles inline wrappers separately, and returns for zero-width frames other than `hr` and preformatted frames. For a frame that passes those guards, it measures for containing-block overflow, may start a new line, then positions and records the frame; the method increases line width and updates the line's maximum height from the frame's margin height. Source: `pdf/src/FrameDecorator/Block.php`."
},
{
"title": "Inline",
"body": "If no frame is supplied, **Inline::split** delegates to the parent and returns; it throws when the supplied frame is not a child. For a valid non-null child, it clones the inline node and inserts the clone after the current frame. It removes duplicated right-edge properties from the current node and left-edge properties from the split node, then moves the selected child and following siblings into the clone. The loop leaves `$frame` bound to the last frame moved into the clone; a forced page break, or a qualifying `page_break_before` or `page_break_after` value on that last moved frame, propagates a split to the parent. Source: `pdf/src/FrameDecorator/Inline.php`."
},
{
"title": "Image",
"body": "**Image::__construct** resolves the source attribute through **Cache::resolve_url** using the document protocol, base host, base path, and **Dompdf** context, and stores the resolved URL and image message. When the resolved image is broken and the `alt` value is non-empty, it derives a fallback width from the alternate text and a fallback height from the configured font metrics. Source: `pdf/src/FrameDecorator/Image.php`."
},
{
"title": "ListBullet",
"body": "**ListBullet::get_margin_width** and **get_margin_height** return zero when `list_style_type === \"none\"`. Otherwise, each returns the font-size-scaled bullet size plus twice the configured bullet padding: `font_size * BULLET_SIZE + 2 * BULLET_PADDING`. Source: `pdf/src/FrameDecorator/ListBullet.php`."
}
]
Reflow responsibilities and order
[
{
"title": "AbstractFrameReflower",
"body": "**AbstractFrameReflower** declares the shared `reflow(Block $block = null)` contract and provides helpers for frame access, sizing, generated content, margins, and minimum/maximum widths. **_collapse_margins** skips out-of-flow, inline-block, root, and direct-root-child frames; otherwise it converts applicable auto margins to zero and rewrites adjacent, first-child, and last-child vertical margins using collapsed values. **get_min_max_width** includes horizontal padding, borders, and margins, aggregates adjacent inline and child bounds, honors specified non-percentage widths, and caches the result. Source: `pdf/src/FrameReflower/AbstractFrameReflower.php`."
},
{
"title": "Block",
"body": "**Block::reflow** checks forced page breaks and page fullness before doing layout work. For a non-full page it generates content, collapses margins, calculates and stores the restricted width and related horizontal properties, establishes the content-area containing block, and processes children in source order. Each child reached before a loop break receives a containing block, passes through clear handling, is reflowed with the current block as context, and is checked for a page break before float processing. A page-full check at the start of an iteration or a truthy `check_page_break($child)` result can break the loop, so later children or subsequent float processing may be skipped; post-loop height calculation and alignment still follow. Source: `pdf/src/FrameReflower/Block.php`."
},
{
"title": "Inline",
"body": "**Inline::reflow** checks forced page breaks and page fullness, materializes generated content, and positions the inline frame. It transfers horizontal edge properties to the first and last text children, passes the inline frame to the enclosing block's line assembly when a block context is supplied, and leaves recording to **Block::add_frame_to_line** and its out-of-flow, inline-wrapper, and zero-width guards. It then assigns the inline containing block to each child and reflows the children with the current block context. Source: `pdf/src/FrameReflower/Inline.php`."
},
{
"title": "Image",
"body": "**Image::get_min_max_width** obtains natural image dimensions when width or height is auto, preserves the aspect ratio when only one dimension is forced, applies minimum and maximum width and height constraints, writes the resolved dimensions back in points, and returns equal minimum and maximum widths. **Image::reflow** positions the image, invokes that sizing calculation, and passes the image frame to the supplied block's line assembly when a block context is present; insertion is subject to **Block::add_frame_to_line**'s out-of-flow and zero-width guards. Source: `pdf/src/FrameReflower/Image.php`."
},
{
"title": "ListBullet",
"body": "**ListBullet::reflow** sets the style width from the decorator's bullet width and positions the frame. When `list_style_position === \"inside\"`, it finds the nearest block parent and passes the bullet to that parent's line assembly. Actual insertion remains subject to **Block::add_frame_to_line**'s out-of-flow and zero-width guards; the outside-position branch performs no corresponding parent-line insertion in this method. Source: `pdf/src/FrameReflower/ListBullet.php`."
}
]
[!WARNING] Two source-defined limits affect how this boundary should be interpreted. The image-specific
$page->add_floating_frame($this)call inpdf/src/FrameReflower/Image.phpis commented out, so active float processing inpdf/src/FrameReflower/Block.phpdoes not prove that images register through that disabled path. In Block::reflow, the initial page-full guard returns before later layout. During the child loop, a page-full check at the start of an iteration or a truthycheck_page_break($child)result breaks the loop, which can skip remaining children and subsequent float processing; post-loop height calculation and alignment still continue after that loop break.
Updated