mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-09 08:28:51 +00:00
#3233: AEP import names an unnamed text layer as a following name is cut, so export and import agree
This commit is contained in:
@@ -26,10 +26,11 @@ pub(crate) const MAX_TEXT_CHARS: usize = 1 << 20;
|
||||
/// U+0003 (END OF TEXT): After Effects' other paragraph break besides `\r`.
|
||||
const ETX: char = '\u{3}';
|
||||
|
||||
/// The first paragraph of a stored text, at most 64 characters: the name After Effects shows for
|
||||
/// a text layer without a name of its own (U+0003 ends a paragraph like `\r`).
|
||||
/// The name of a text layer without a name of its own: its first paragraph, cut as EffectCraft
|
||||
/// cuts a following name ([`effectcraft_project::text_layer_name`]), so a following name that
|
||||
/// export writes as no name comes back unchanged (#3233). U+0003 ends a paragraph like `\r`.
|
||||
pub(crate) fn first_paragraph(raw: &str) -> String {
|
||||
raw.split(['\r', ETX]).next().unwrap_or_default().chars().take(64).collect()
|
||||
effectcraft_project::text_layer_name(raw.split(ETX).next().unwrap_or_default())
|
||||
}
|
||||
|
||||
/// The Source Text value of `doc`, with what cannot be mapped reported for the layer `what`.
|
||||
|
||||
@@ -238,6 +238,27 @@ fn a_text_layer_name_follows_its_text_through_export_and_import() {
|
||||
assert_eq!((l.name.as_str(), l.name_follows_text), ("Text", Some(true)), "an empty text layer");
|
||||
}
|
||||
|
||||
/// #3233: a following name cut from a first line longer than 40 characters is written as no name
|
||||
/// and comes back as the same cut name, still following; a long name the user gave stays whole.
|
||||
#[test]
|
||||
fn a_long_first_line_names_a_text_layer_the_same_after_export_and_import() {
|
||||
let line = "A headline that runs past forty characters, to 64";
|
||||
assert!((41..=64).contains(&line.chars().count()));
|
||||
let text = format!("{line}\rSecond");
|
||||
let cut = effectcraft_project::text_layer_name(&text);
|
||||
assert_eq!(cut.chars().count(), 40);
|
||||
for (name, follows, back_name) in [(cut.as_str(), Some(true), cut.as_str()), (line, Some(false), line)] {
|
||||
let (mut p, c) = text_project(vec![TextDoc::plain(&text)]);
|
||||
with_layer(&mut p, c, 0, |l| {
|
||||
l.name = name.into();
|
||||
l.name_follows_text = follows;
|
||||
});
|
||||
let back = reimport(&export_all(&p).bytes);
|
||||
let l = back.comps().flat_map(|(_, c)| c.layers.iter()).find(|l| matches!(l.source, LayerSource::Text)).expect("text layer");
|
||||
assert_eq!((l.name.as_str(), l.name_follows_text), (back_name, follows), "{name:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn what_a_text_document_cannot_hold_is_reported() {
|
||||
let mut d = TextDoc::plain("x");
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ refused losses) leaves the project unchanged.
|
||||
| Project settings | colour depth, working space (None, sRGB, Rec. 709 Gamma 2.4; another one is reported as `project settings`), working gamma, Linearize Working Space, Blend Colors Using 1.0 Gamma, time display (timecode, frames, feet + frames 35 / 16 mm), frame count start (620-634). A colour depth, time display, frames-per-foot or frame count start code
|
||||
outside the sampled ones keeps the current value and is reported as `project settings`. After Effects renders in the working space (902: in a Rec. 709 Gamma 2.4 project an sRGB PNG step of 128 comes out as 135 and a grey 0.5 solid as 128, in the Render Queue output and in the captures), while EffectCraft converts its output to `output_space` (sRGB when unset): a Rec. 709 working space therefore also sets the output space to Rec. 709 (902 within 1 level of After Effects, was up to 13). They come in `ImportResult::settings`; the library never changes the project's own settings, `file.importAep` applies them when the project has no compositions yet (or with `applySettings: true`) |
|
||||
| Composition | name, size, exact frame rate, duration, work area, background colour (8 bit), pixel aspect, composition motion blur switch, shutter angle and phase (as stored), display start time, Classic/Advanced 3D renderer |
|
||||
| Layers | solid, null, adjustment, footage, precomposition and **text** layers: name (the source's name when the layer has none; for a text layer its first line), in/out/start and time stretch (exact ratios, e.g. 50 % and 150 %); a layer of a clip, sequence or sound ends at its source's end as After Effects ends it (unless remapped, looped or less than a frame past; f1), enabled/audio/solo/shy/locked/effects/motion blur/collapse/3D/guide switches, draft quality, label, blend mode (Normal, Multiply, Add, Screen, Overlay, Difference; Soft Light, Hard Light, Darken, Lighten, Exclusion, Color Dodge, Color Burn by name, 906; Classic Difference, Hue, Saturation, Color, Luminosity, the Stencil and Silhouette modes, Classic Color Dodge / Burn, Linear Dodge / Burn, Linear, Vivid and Pin Light, Hard Mix by render, 907: each code rendered by After Effects matched exactly one EffectCraft mode; 906 renders within 1/255 of After Effects), parent, **track matte** (alpha, alpha inverted, luma, luma inverted; the matte layer anywhere in the composition, also below its target; 402-408) |
|
||||
| Layers | solid, null, adjustment, footage, precomposition and **text** layers: name (the source's name when the layer has none; for a text layer the first 40 characters of its first line, as EffectCraft names a text layer after its text), in/out/start and time stretch (exact ratios, e.g. 50 % and 150 %); a layer of a clip, sequence or sound ends at its source's end as After Effects ends it (unless remapped, looped or less than a frame past; f1), enabled/audio/solo/shy/locked/effects/motion blur/collapse/3D/guide switches, draft quality, label, blend mode (Normal, Multiply, Add, Screen, Overlay, Difference; Soft Light, Hard Light, Darken, Lighten, Exclusion, Color Dodge, Color Burn by name, 906; Classic Difference, Hue, Saturation, Color, Luminosity, the Stencil and Silhouette modes, Classic Color Dodge / Burn, Linear Dodge / Burn, Linear, Vivid and Pin Light, Hard Mix by render, 907: each code rendered by After Effects matched exactly one EffectCraft mode; 906 renders within 1/255 of After Effects), parent, **track matte** (alpha, alpha inverted, luma, luma inverted; the matte layer anywhere in the composition, also below its target; 402-408) |
|
||||
| Transform | Anchor Point, Position, Scale, Rotation, Opacity, **X and Y Rotation** (486, 487) and **Orientation**, static (488) or keyed (703: key values, times, linear and hold sides; a bezier side becomes linear and is reported; After Effects slerps between the keys, EffectCraft interpolates each angle, so in-between values differ, e.g. 703 at 1 s `[6.26, 9.35, 15.22]` against `[5, 10, 15]`; an expression-driven orientation is reported): the stored value, or the default when the file stores none; keyframes with time, linear/bezier/hold on each side, temporal ease (speed and influence, per dimension for Scale), automatic or manual spatial tangents, roving and key labels |
|
||||
| Expressions | on the transform and mask properties and on effect, camera and light parameters (534): source text and enable switch |
|
||||
| Cameras and lights | **cameras** (one-node and two-node, from `ldta` byte 38 bit `0x40`): point of interest, Position as stored (also the auto Position `[0, 0, -zoom]`, where After Effects renders it, 537), orientation and rotations, Zoom (absent: `-z` of the auto Position, else the 50 mm default), Depth of Field, Focus Distance (absent: the zoom), Aperture (absent: 25.309 at 1920 px, read back from 256; scaled with the width, which is not sampled, E); **spot, point, parallel and ambient lights** (light type 1, 2, 0, 3: 701, 702): point of interest (not for point and ambient lights), Position, Intensity, Color, Cone Angle, Cone Feather, Casts Shadows; keys and expressions as for the transform; 251-256, 341-345, 535-537, 701, 702. Environment lights are reported |
|
||||
|
||||
Reference in New Issue
Block a user