IDML: a left-aligned anchored object's X offset reads in the layout's direction (#555)

This commit is contained in:
aiongg
2026-10-12 05:32:59 +00:00
parent 25c215faf9
commit ab231cde62
3 changed files with 58 additions and 4 deletions
+2 -1
View File
@@ -2055,7 +2055,8 @@ impl<'a> Ex<'a> {
.attr("VerticalReferencePoint", names::anchor_rel_out(*y_relative, true))
.attr("HorizontalAlignment", ["LeftAlign", "CenterAlign", "RightAlign"][(*ref_point % 3) as usize])
.attr("VerticalAlignment", ["TopAlign", "CenterAlign", "BottomAlign"][(*ref_point / 3).min(2) as usize])
.attr("AnchorXoffset", num(*x_offset))
// IDML measures a left-aligned object's X offset leftwards.
.attr("AnchorXoffset", num(if *ref_point % 3 == 0 { 0.0 - *x_offset } else { *x_offset }))
.attr("AnchorYoffset", num(*y_offset))
.attr("PinPosition", bool_s(*keep_within_column)),
};
+2 -1
View File
@@ -1876,7 +1876,8 @@ impl<'r> Importer<'r> {
designcraft_doc::AnchorPosition::Custom {
x_relative: names::anchor_rel_in(s("HorizontalReferencePoint")),
y_relative: names::anchor_rel_in(s("VerticalReferencePoint")),
x_offset: get("AnchorXoffset"),
// IDML measures a left-aligned object's X offset leftwards.
x_offset: if col == 0 { 0.0 - get("AnchorXoffset") } else { get("AnchorXoffset") },
y_offset: get("AnchorYoffset"),
object_point: names::anchor_in(s("AnchorPoint")),
ref_point: row * 3 + col,
+54 -2
View File
@@ -822,13 +822,65 @@ fn anchored_objects_take_unset_settings_from_object_styles() {
assert_eq!(
positions,
[
custom(R::TextFrame, R::Anchor, 5.0, -3.0, 8, true),
custom(R::TextFrame, R::Anchor, -5.0, -3.0, 8, true),
custom(R::TextFrame, R::PageEdge, 0.0, 0.0, 0, true),
custom(R::PageMargin, R::Anchor, 2.0, 0.0, 8, false),
custom(R::PageMargin, R::Anchor, -2.0, 0.0, 8, false),
]
);
}
/// IDML measures a left-aligned anchored object's X offset leftwards; the document model and
/// composition measure every X offset rightwards.
#[test]
fn left_aligned_anchored_x_offsets_flip_at_the_idml_boundary() {
use designcraft_doc::AnchorPosition;
let rect = |id: &str, align: &str, x: &str| {
format!(
r#"<Rectangle Self="{id}" ItemTransform="1 0 0 1 0 0">
<Properties><PathGeometry><GeometryPathType PathOpen="false"><PathPointArray>
<PathPointType Anchor="0 0"/><PathPointType Anchor="0 10"/><PathPointType Anchor="10 10"/><PathPointType Anchor="10 0"/>
</PathPointArray></GeometryPathType></PathGeometry></Properties>
<AnchoredObjectSetting AnchoredPosition="Anchored" AnchorPoint="TopLeftAnchor" HorizontalReferencePoint="TextFrame"
HorizontalAlignment="{align}" VerticalAlignment="TopAlign" VerticalReferencePoint="PageEdge" AnchorXoffset="{x}" AnchorYoffset="0"/>
</Rectangle>"#
)
};
let story = format!(
r#"<idPkg:Story xmlns:idPkg="http://ns.adobe.com/AdobeInDesign/idml/1.0/packaging"><Story Self="s1">
<ParagraphStyleRange><CharacterStyleRange><Content>a</Content>{}{}{}</CharacterStyleRange></ParagraphStyleRange>
</Story></idPkg:Story>"#,
rect("left", "LeftAlign", "-108"),
rect("zero", "LeftAlign", "0"),
rect("center", "CenterAlign", "116"),
);
let d = import_idml(&fixture_with_story(&story)).unwrap();
let x_offsets = |d: &Document| -> Vec<f64> {
let s = d.stories.values().find(|s| !s.objects.is_empty()).unwrap();
s.objects.iter().map(|o| if let AnchorPosition::Custom { x_offset, .. } = o.position { x_offset } else { f64::NAN }).collect()
};
assert_eq!(x_offsets(&d), [108.0, 0.0, 116.0]);
// The left-aligned object lands 108 pt right of the reference point.
let sid = d.stories.iter().find(|(_, s)| !s.objects.is_empty()).map(|(id, _)| *id).unwrap();
let cs = designcraft_compose::compose_story(&d, sid, &Default::default());
let placed: Vec<_> = cs.frames.iter().flat_map(|f| &f.objects).collect();
let x = |i: usize| placed.iter().find(|o| o.index == i).unwrap().origin.x;
assert!((x(0) - x(1) - 108.0).abs() < 1e-6, "{} {}", x(0), x(1));
// Export writes the original IDML values back.
let bytes = export_idml(&d);
let mut zip = zip::ZipArchive::new(std::io::Cursor::new(bytes)).unwrap();
let mut stories = String::new();
for i in 0..zip.len() {
let mut f = zip.by_index(i).unwrap();
if f.name().starts_with("Stories/") {
std::io::Read::read_to_string(&mut f, &mut stories).unwrap();
}
}
for v in [r#"AnchorXoffset="-108""#, r#"AnchorXoffset="0""#, r#"AnchorXoffset="116""#] {
assert!(stories.contains(v), "{v} missing");
}
assert_eq!(x_offsets(&import_idml(&export_idml(&d)).unwrap()), [108.0, 0.0, 116.0]);
}
#[test]
fn round_trips_frames_with_pasted_in_items() {
let mut d = Document::new(&NewDocument::default());