TPDFlibDynamicComponentSession
Page layout, pagination, data types
Description
Composes a stateful component one destination page at a time without constructing content for later pages in advance
Each attempt runs on a clone inside an isolated Form XObject and commits the clone only after validated content is placed successfully
Extended placement can reject overflow, clip natural-size content or scale it down proportionally while aligning it within a destination slot
Pagination policy can allow normal splitting, defer the first partial fragment to a fresh region or require the whole component to remain visible before committing anything
Flow-space constraints can reject a short region before invoking the component or reserve enough height after its final fragment for a following component
A rejected result, failed drawing call or callback exception removes the temporary page, restores the caller-selected page and leaves both destination content and current component state unchanged
The attached TPDFlib instance remains caller-owned and has to outlive the session
Declaration
const
PDF_DYNAMIC_COMPONENT_ERROR = 0;
PDF_DYNAMIC_COMPONENT_MORE = 1;
PDF_DYNAMIC_COMPONENT_COMPLETE = 2;
PDF_DYNAMIC_COMPONENT_RETRY = 3;
type
TPDFlibDynamicPageContext = record
LibraryInstance: TPDFlib;
PageNumber: Integer;
CompositionIndex: Integer;
DestinationLeft: Double;
DestinationTop: Double;
AvailableWidth: Double;
AvailableHeight: Double;
end;
TPDFlibDynamicPageResult = record
HasContent: Boolean;
HasMoreContent: Boolean;
UsedWidth: Double;
UsedHeight: Double;
end;
TPDFlibDynamicOverflowPolicy = (dopReject, dopClip, dopScaleDown);
TPDFlibDynamicHorizontalAlignment = (dhaLeft, dhaCenter, dhaRight);
TPDFlibDynamicVerticalAlignment = (dvaTop, dvaCenter, dvaBottom);
TPDFlibDynamicPaginationPolicy = (dppAllowSplit, dppKeepTogether,
dppRequireComplete);
TPDFlibDynamicRetryReason = (drrNone, drrKeepTogether,
drrRequireComplete, drrMinimumSlotHeight, drrMinimumFollowingHeight);
TPDFlibDynamicLayoutOptions = record
ContentWidth: Double;
ContentHeight: Double;
OverflowPolicy: TPDFlibDynamicOverflowPolicy;
HorizontalAlignment: TPDFlibDynamicHorizontalAlignment;
VerticalAlignment: TPDFlibDynamicVerticalAlignment;
PaginationPolicy: TPDFlibDynamicPaginationPolicy;
MinimumSlotHeight: Double;
MinimumFollowingHeight: Double;
end;
TPDFlibDynamicPlacementTransform = record
M11, M12, M21, M22, MDX, MDY: Double;
end;
TPDFlibDynamicPagePlacement = record
PageNumber: Integer;
CompositionIndex: Integer;
Left: Double;
Top: Double;
Width: Double;
Height: Double;
HasContent: Boolean;
HasMoreContent: Boolean;
SourceWidth: Double;
SourceHeight: Double;
SlotLeft: Double;
SlotTop: Double;
SlotWidth: Double;
SlotHeight: Double;
Transform: TPDFlibDynamicPlacementTransform;
WasClipped: Boolean;
WasScaled: Boolean;
end;
TPDFlibDynamicComponent = class
public
function CloneComponent: TPDFlibDynamicComponent; virtual; abstract;
function Compose(const Context: TPDFlibDynamicPageContext;
out ComposeResult: TPDFlibDynamicPageResult): Integer; virtual; abstract;
end;
TPDFlibDynamicComponentSession = class
private
FLibrary: TPDFlib;
FCompositionCount: Integer;
FCompleted: Boolean;
FKeepTogetherRetryPending: Boolean;
FLastRetryReason: TPDFlibDynamicRetryReason;
FLastRequiredSlotHeight: Double;
public
constructor Create(ALibrary: TPDFlib;
AComponent: TPDFlibDynamicComponent);
function AttachComponent(AComponent: TPDFlibDynamicComponent): Integer;
function Reset: Integer;
function CloneCurrentComponent: TPDFlibDynamicComponent;
class function CreateLayoutOptions(ContentWidth, ContentHeight: Double;
OverflowPolicy: TPDFlibDynamicOverflowPolicy = dopReject;
HorizontalAlignment: TPDFlibDynamicHorizontalAlignment = dhaLeft;
VerticalAlignment: TPDFlibDynamicVerticalAlignment = dvaTop;
PaginationPolicy: TPDFlibDynamicPaginationPolicy = dppAllowSplit;
MinimumSlotHeight: Double = 0;
MinimumFollowingHeight: Double = 0):
TPDFlibDynamicLayoutOptions; static;
function ComposeNext(Page: Integer; Left, Top, Width, Height: Double;
out Placement: TPDFlibDynamicPagePlacement): Integer;
function ComposeNextLayout(Page: Integer; SlotLeft, SlotTop, SlotWidth,
SlotHeight: Double; const Options: TPDFlibDynamicLayoutOptions;
out Placement: TPDFlibDynamicPagePlacement): Integer;
property LibraryInstance: TPDFlib read FLibrary;
property CompositionCount: Integer read FCompositionCount;
property Completed: Boolean read FCompleted;
property KeepTogetherRetryPending: Boolean
read FKeepTogetherRetryPending;
property LastRetryReason: TPDFlibDynamicRetryReason read FLastRetryReason;
property LastRequiredSlotHeight: Double read FLastRequiredSlotHeight;
end;
Component contract
CloneComponentreturns a caller-independent copy of all mutable pagination stateComposedraws throughContext.LibraryInstancein local Form coordinates whose available size isAvailableWidthbyAvailableHeightPageNumberidentifies the destination page even though the library selects an isolated temporary page while the callback runs- The callback may use drawing and content APIs but shall not change documents, pages or nested drawing containers
- Content results require finite positive
UsedWidthandUsedHeightvalues within the content canvas - The used rectangle starts at local coordinate zero and shall bound all visible component content
- A no-content result is valid only when
HasMoreContentisFalseand both used dimensions are zero - Returning zero rejects the attempt; raised exceptions propagate after temporary state has been cleaned up
Methods
| Create | Attaches a caller-owned library and clones the supplied component into independent initial and current snapshots |
|---|---|
| AttachComponent | Atomically replaces both snapshots with clones of a caller-owned component and clears completion state |
| Reset | Restores a new current clone from the initial snapshot and resets the successful composition count |
| CloneCurrentComponent | Returns a caller-owned clone for inspecting the current committed component state |
| CreateLayoutOptions | Builds typed natural-canvas, overflow, alignment, pagination and flow-space settings for extended placement |
| ComposeNext | Composes one page slot, validates and places its Form XObject, releases the capture handle and commits the working state |
| ComposeNextLayout | Composes on the configured natural canvas, applies overflow and alignment policy and returns source, slot and final transform data |
Overflow and placement
dopReject | Rejects content wider or taller than the slot without changing destination content or committed component state |
|---|---|
dopClip | Keeps scale 1 and intersects the page clipping path with the destination slot before invoking the Form XObject |
dopScaleDown | Uses the smaller width or height ratio only when needed and never enlarges content |
Alignment is resolved from the transformed used rectangle, so centred or trailing clipped content selects the corresponding visible portion
Transform reports the logical layout matrix [M11 M12 M21 M22 MDX MDY] from component-local coordinates to the final top-down document coordinates
SlotLeft, SlotTop, SlotWidth and SlotHeight preserve the caller-supplied region even when the final content origin lies outside it under clipping
Pagination policies
dppAllowSplit | Preserves the default behaviour and commits any valid fragment whose callback reports more content |
|---|---|
dppKeepTogether | Returns PDF_DYNAMIC_COMPONENT_RETRY for the first partial or non-visible trial, then permits splitting or the selected clipping fallback on the next valid attempt for the same uncommitted fragment |
dppRequireComplete | Returns PDF_DYNAMIC_COMPONENT_RETRY until the callback reports no remaining content and the full used rectangle either fits the slot or remains visible through proportional scale-down |
A retry discards the Form XObject trial, preserves the destination page, component snapshot, completion flag and composition count, and returns an empty Placement record
KeepTogetherRetryPending identifies the one-shot fallback state and is cleared after a successful commit, Reset or AttachComponent
Flow-space constraints
MinimumSlotHeight | Returns PDF_DYNAMIC_COMPONENT_RETRY before cloning or invoking the component when the destination slot is shorter than the configured height |
|---|---|
MinimumFollowingHeight | Applies only to a final content fragment and requires its resolved height plus the reserved following height to fit the slot |
A following-height retry discards the isolated trial while preserving earlier committed fragments, allowing a heading, label or short lead-in to move with the first usable region of the next component
LastRetryReason distinguishes whole-component, strict-completion, minimum-slot and following-space retries, while LastRequiredSlotHeight reports the known required height for flow-space constraints
The minimum-slot check runs before component cloning and Form capture, so repeatedly rejected short regions incur no component callback or temporary page cost
Return values
PDF_DYNAMIC_COMPONENT_ERROR | The request, component callback, result geometry or Form placement failed and no state was committed |
|---|---|
PDF_DYNAMIC_COMPONENT_MORE | Content was committed and the component requested another page slot |
PDF_DYNAMIC_COMPONENT_COMPLETE | The component completed, or the session was already complete and no callback was made |
PDF_DYNAMIC_COMPONENT_RETRY | The selected pagination or flow-space policy deferred placement without committing content or state |
Example
type
TNumberedPagesComponent = class(TPDFlibDynamicComponent)
private
FNextPage: Integer;
FPageCount: Integer;
public
constructor Create(ANextPage, APageCount: Integer);
function CloneComponent: TPDFlibDynamicComponent; override;
function Compose(const Context: TPDFlibDynamicPageContext;
out ComposeResult: TPDFlibDynamicPageResult): Integer; override;
end;
constructor TNumberedPagesComponent.Create(ANextPage, APageCount: Integer);
begin
inherited Create;
FNextPage := ANextPage;
FPageCount := APageCount;
end;
function TNumberedPagesComponent.CloneComponent: TPDFlibDynamicComponent;
begin
Result := TNumberedPagesComponent.Create(FNextPage, FPageCount);
end;
function TNumberedPagesComponent.Compose(
const Context: TPDFlibDynamicPageContext;
out ComposeResult: TPDFlibDynamicPageResult): Integer;
begin
Context.LibraryInstance.DrawText(0, 0, IntToStr(FNextPage));
Inc(FNextPage);
ComposeResult.HasContent := True;
ComposeResult.HasMoreContent := FNextPage <= FPageCount;
ComposeResult.UsedWidth := Context.AvailableWidth;
ComposeResult.UsedHeight := 24;
Result := 1;
end;
Template := TNumberedPagesComponent.Create(1, 10);
try
Session := TPDFlibDynamicComponentSession.Create(PDF, Template);
try
repeat
Status := Session.ComposeNext(PageNumber, 36, 36, 540, 720,
Placement);
if Status = PDF_DYNAMIC_COMPONENT_MORE then
PageNumber := PDF.NewPage;
until Status <> PDF_DYNAMIC_COMPONENT_MORE;
finally
Session.Free;
end;
finally
Template.Free;
end;
See also
BeginFormXObject, ReleaseCapturedPage, ApplyRepeatedPageFragment