* #3099: placed .ai pages fill the size After Effects stored; null anchors default to the corner
Footage of an .aep keeps the size After Effects stored for a placed PDF / Illustrator / EPS
file (the ArtBox, rounded up), and masks and anchors are measured against it. The page was
drawn as a PDF viewer frames it (CropBox, /Rotate) at its own size, scaled by the width ratio
at full resolution and stretched to the stored aspect below 3/4: masks too large, art off
centre, squeezed at half resolution.
- effectcraft-pdf: Framing::Placed / parse_page_framed (ArtBox clipped to the CropBox,
/Rotate ignored); boxes with a non-finite corner or span are not used
- Footage::placed_page (set by the .aep importer, serialized only when true): the page is
framed so and filled into the stored size (effectcraft_render::vector_fit), decoded,
continuously rasterised and in Create Shapes from Vector Layer; Reload keeps the size;
the frame cache keys it by size and framing
- .aep import/export: a null's absent Anchor Point is [0, 0], as After Effects reads it
- fixture ai-mask-3099.aep (own synthetic files, scrubbed, profiles stubbed) and tests
against After Effects 2026's read-back
* #3099: placed-page framing only for .ai and .pdf, EPS as before; doc comments
After Effects' sizing was measured with PDFs named .ai only; an EPS from an .aep keeps its
bounding box at its own size (listed as not measured in docs/gaps.md and docs/aep-import.md).
Doc comments on the touched functions.
* #3099: shapes from a placed page keep its frame (track matte)
Create Shapes from Vector Layer converted every path of a placed page, so bleed, art past the
artboard or the page's background showed where the footage's raster hides them. The layers it
makes from a placed page now take a hidden "<layer> Frame" shape layer (the footage's
width x height) as their Alpha track matte: drawn at the comp's resolution, its edge is the
raster's on shape and image layers alike. The command returns the frame layer's id too.
A text layer's name followed its Source Text whenever the name started with "Text", so a user's
"Text Title" was replaced by the next text edit. The layer now records whether its name follows
the text (`name_follows_text`, optional in the project file). A new text layer named after its
text follows it; a name given at creation or by Rename (Enter, layer.rename, a script's
layer.name, Layer Settings' name) is the user's and stays; renaming to an empty name shows the
text again and follows it; committing the unchanged name changes nothing; copies keep the state.
On-canvas typing and layer.setText share the rule. The name is the text's first paragraph, which
ends at a line feed or a bare carriage return (After Effects' paragraph break).
Projects saved without the state keep the old rule for the names that followed: a name that is
empty, the text's own, or "Text"/"Text 2"... follows; any other name stays. AEP import records a
stored layer name as the user's and no name as following, and names an empty text layer without
a name "Text" instead of "Layer"; export writes no name for a following layer and keeps the name
the user gave, even when it equals the text.
Fixes#3233
#3119 (the base of this branch) is merged. Conflicts with the Cryptomatte
import (#3109) and the version report: Extra keeps Extractor, Identifier
and Cryptomatte, arbitrary.rs keeps ExtractorChannels /
identifier_channel_type next to CryptomatteData, the effect table counts
66, the docs keep both sides. The exporter from #2594 gets the Extractor
and Identifier arms: like Lumetri they have no prototype, so CC Color
Neutralizer, EXtractoR and IDentifier are reported, not written (new
test imported_plug_in_effects_are_reported_not_written).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>