Merge pull request #3189 from AJMakesStuff/3179---Envelope-Distort

(3179) Envelope Distort
This commit is contained in:
BFlat
2026-10-21 16:17:56 +07:00
committed by GitHub
2 changed files with 53 additions and 6 deletions
+33 -6
View File
@@ -1412,17 +1412,33 @@ impl<'a> Reader<'a> {
return Ok(());
}
let Some(f) = self.frames.pop() else { return Ok(()) };
let Kind::Obj(mut o) = f.kind else { return Ok(()) };
obj::close(&mut o, &mut self.stack, f.base);
self.obj_hidden = f.hidden;
let Frame { kind, children, base, hidden, gs, after, .. } = f;
let Kind::Obj(mut o) = kind else { return Ok(()) };
obj::close(&mut o, &mut self.stack, base);
self.obj_hidden = hidden;
// Art made inside a dictionary (type's paths) isn't kept.
self.gs = f.gs;
self.after_object = f.after;
self.gs = gs;
self.after_object = after;
if matches!(self.frames.last(), Some(Frame { kind: Kind::Obj(_), .. })) {
return self.push(V::Obj(Rc::new(*o)));
}
match o.ty.as_str() {
"ArtDictionary" => self.art_dictionary(&o),
"ArtDictionary" => {
let envelope = o.get("AIDeformOptionsKey").is_some();
self.art_dictionary(&o);
if envelope {
let mut slots = vec![];
for child in &children {
envelope_text_slots(child, &mut slots);
}
for mut slot in slots {
// The editing data keeps the source text inside the envelope's options;
// its visible result is the outlined art the PDF page carries.
slot.visible = false;
self.add(slot, false)?;
}
}
}
"Document" => self.document(&o),
"AI11Text" => self.text_slot(&o)?,
"SymbolInstance" => self.unreadable("symbols"),
@@ -2336,6 +2352,17 @@ impl<'a> Reader<'a> {
}
}
/// The source text inside an envelope options dictionary, carried through its containing metadata.
fn envelope_text_slots(node: &Node, out: &mut Vec<Node>) {
if slot_of(node.name.as_deref()).is_some() {
out.push(node.clone());
} else if let Some(children) = node.children() {
for child in children {
envelope_text_slots(child, out);
}
}
}
/// The text objects written twice, plainly and on `%_` lines: the commented copy (`hidden`) of a
/// story that also has a plain one goes.
fn drop_commented_copies(layers: &mut [Arc<Node>], hidden: &HashSet<NodeId>) {
+20
View File
@@ -1332,3 +1332,23 @@ fn raster_print_page_recovers_legacy_mesh_vector_mask_and_editable_curve() {
let r = import(wrong.as_bytes()).unwrap();
assert!(r.warnings.iter().any(|w| w.contains("for a 100 × 100 pt page, not the file's 120 × 100 pt page")), "{:?}", r.warnings);
}
#[test]
fn envelope_options_keep_the_source_text_hidden_under_its_outlined_appearance() {
let doc = text_document(&[("CHONKYCATS\r", 0, (8200.0, 8180.0), (0.0, 0.0), 0.0)]);
let slot: String = text_object(0).lines().map(|line| format!("%_{line}\n")).collect();
let envelope =
format!("%_/ArtDictionary :\n%_1 /Int (AIDeformOptionsKey) ,\n%_198 /Real (AIDeformArtHeightKey) ,\n%_X=\n%_0 Ae\n%_u\n{slot}%_U\n%_;\n");
let art = native_with_text(&layer("Art", true, &format!("{}\n{envelope}", square(10, 10))), &doc);
let r = import(&eps(&page_square(10, 10), &art)).unwrap();
assert_eq!(names(&r.document), ["Art"], "{:?}", r.warnings);
assert_eq!(kinds(&r.document.layers[0]), ["path", "text"], "{:?}", r.warnings);
let mut texts = vec![];
r.document.walk(|n| {
if let NodeKind::Text(t) = &n.kind {
texts.push((t.plain_text(), n.visible));
}
});
assert_eq!(texts, [("CHONKYCATS".to_string(), false)], "{:?}", r.warnings);
assert!(r.warnings.is_empty(), "{:?}", r.warnings);
}