mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 17:56:50 -06:00
AI import: non-native art stays upright unless its placement mirrors it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
committed by
bflatastic
co-authored by
Claude Opus 5.5
parent
0a4e9ea8c8
commit
710b6058ca
@@ -1283,12 +1283,15 @@ impl<'a> Reader<'a> {
|
||||
if bbox.width() <= 0.0 || bbox.height() <= 0.0 {
|
||||
return Ok(());
|
||||
}
|
||||
// The art's own box onto the object's box (its measure of the same art), upside down:
|
||||
// the object's space has y down, and the PDF's content is kept in its own y-up
|
||||
// coordinates, so the PDF's bottom is the top of the box.
|
||||
let fit = Affine::translate((target.x0, target.y1))
|
||||
* Affine::scale_non_uniform(target.width() / bbox.width(), -target.height() / bbox.height())
|
||||
* Affine::translate((-bbox.x0, -bbox.y0));
|
||||
// The art's own box onto the object's box (its measure of the same art). The PDF importer
|
||||
// hands the art back as the page shows it, and it keeps that orientation unless its
|
||||
// placement mirrors it (a negative determinant): the usual `/RTransform` `1 0 0 -1 …` with
|
||||
// the origin's flip doesn't, and the art then shows as the file's own PDF part draws it.
|
||||
let fit = if place.determinant() < 0.0 {
|
||||
Affine::translate((target.x0, target.y1)) * Affine::scale_non_uniform(target.width() / bbox.width(), -target.height() / bbox.height())
|
||||
} else {
|
||||
Affine::translate((target.x0, target.y0)) * Affine::scale_non_uniform(target.width() / bbox.width(), target.height() / bbox.height())
|
||||
} * Affine::translate((-bbox.x0, -bbox.y0));
|
||||
for n in &mut art {
|
||||
n.transform(fit, true);
|
||||
// The PDF's swatches stay with it: its colours keep their values, unlinked.
|
||||
|
||||
@@ -718,8 +718,8 @@ fn ascii85_comment_lines(bytes: &[u8]) -> String {
|
||||
/// `/Bounds` in a space measured from `/Origin` with y down, then `/RTransform`. Two squares at
|
||||
/// 1..5 × 2..8 of a 10-point page, blue below red: bounds (1, 2)–(5, 8) → from the origin
|
||||
/// (10, 20), y down, (11, 18)–(15, 12) → twice that plus (100, 50): x 122..130, y 74..86 in art
|
||||
/// space, which is y 14..26 in the document (crop marks 100 high). The space has y down, so the
|
||||
/// PDF's bottom (blue) is the top of the box.
|
||||
/// space, which is y 14..26 in the document (crop marks 100 high). That placement mirrors (`2 0 0 2`
|
||||
/// with the origin's flip), so the PDF's bottom (blue) is the top of the box.
|
||||
#[test]
|
||||
fn non_native_art_is_drawn_from_its_pdf_fitted_to_its_bounds() {
|
||||
let pdf = tiny_pdf(10.0, "0 0 1 rg 1 2 4 3 re f 1 0 0 rg 1 5 4 3 re f");
|
||||
@@ -756,6 +756,34 @@ fn non_native_art_is_drawn_from_its_pdf_fitted_to_its_bounds() {
|
||||
assert_ne!(paths[0].id, paths[1].id);
|
||||
}
|
||||
|
||||
/// The usual placement, `/RTransform` `1 0 0 -1 …`, doesn't mirror (with the origin's flip its
|
||||
/// determinant is positive), so the art shows upright, as the file's own PDF part draws it: the
|
||||
/// PDF's top (red) at the top. Bounds
|
||||
/// (1, 2)–(5, 8) from the origin (10, 20), y down, → (11, 18)–(15, 12) → x 111..115, y 32..38 in
|
||||
/// art space, y 62..68 in the document (crop marks 100 high).
|
||||
#[test]
|
||||
fn non_native_art_placed_without_a_mirror_stays_upright() {
|
||||
let pdf = tiny_pdf(10.0, "0 0 1 rg 1 2 4 3 re f 1 0 0 rg 1 5 4 3 re f");
|
||||
let foreign = format!(
|
||||
"q\n/ForeignObject :\n1 /Version ,\n1 0 0 -1 100 50 /RTransform ,\n10 20 /Origin ,\n1 2 5 8 /Bounds ,\n/Data ,\n{};\n0 0 Xd\n6 () XW\n0 Ae\n{}h\nW\nn\nQ\n",
|
||||
ascii85_comment_lines(&pdf),
|
||||
rect_path(110.0, 30.0, 116.0, 40.0)
|
||||
);
|
||||
let s = read_ok(&layer(&foreign));
|
||||
assert!(s.warnings.is_empty(), "{:?}", s.warnings);
|
||||
let objects = art(&s.doc);
|
||||
let NodeKind::Group { children, clip: true } = &objects[0].kind else { panic!("{:?}", objects[0].kind) };
|
||||
let group = &children[1];
|
||||
let bounds = group.geometric_bounds().unwrap();
|
||||
for (got, want) in [(bounds.x0, 111.0), (bounds.y0, 62.0), (bounds.x1, 115.0), (bounds.y1, 68.0)] {
|
||||
assert!((got - want).abs() < 1e-6, "{bounds:?}");
|
||||
}
|
||||
let paths = group.children().unwrap();
|
||||
let (blue, red) = (paths[0].geometric_bounds().unwrap(), paths[1].geometric_bounds().unwrap());
|
||||
assert!((red.y0 - 62.0).abs() < 1e-6 && (red.y1 - 65.0).abs() < 1e-6, "red {red:?}");
|
||||
assert!((blue.y0 - 65.0).abs() < 1e-6 && (blue.y1 - 68.0).abs() < 1e-6, "blue {blue:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmyk_documents() {
|
||||
let doc = read_ok(&format!("%AI9_ColorModel: 2\n{}", layer(""))).doc;
|
||||
|
||||
Reference in New Issue
Block a user