mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 08:23:20 +00:00
.ai import: type in an envelope may map from its bounds with strokes
With Distort Appearance, an envelope's contents map from the box the file measured for them: their bounds with their strokes or without them. For type, the size the file saves for that box is often out of date, so it can't say which. Envelopes of type are now checked both ways: as before, and mapping from their bounds with their strokes. Whichever draws as the drawn art opens live. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7504a0e2e4
commit
37e3c0215a
@@ -131,6 +131,25 @@ fn saved_margin(o: &Obj, content: &[Arc<Node>]) -> Option<[f32; 4]> {
|
||||
envelope_stroke_margin(stroked, plain)
|
||||
}
|
||||
|
||||
/// Envelope `kind` mapping from its contents' bounds with their strokes, for an envelope with
|
||||
/// Distort Appearance whose contents have strokes it doesn't map from already; `None` otherwise.
|
||||
fn with_strokes(kind: &NodeKind) -> Option<NodeKind> {
|
||||
let NodeKind::Envelope { content, options, .. } = kind else { return None };
|
||||
if !options.distort_appearance {
|
||||
return None;
|
||||
}
|
||||
let (stroked, plain) = envelope_content_boxes(content, Affine::IDENTITY)?;
|
||||
let margin = envelope_stroke_margin(stroked, plain)?;
|
||||
if margin == options.bounds_margin || margin.iter().any(|m| *m < 0.0) || margin.iter().all(|m| *m == 0.0) {
|
||||
return None;
|
||||
}
|
||||
let mut kind = kind.clone();
|
||||
if let NodeKind::Envelope { options, .. } = &mut kind {
|
||||
options.bounds_margin = margin;
|
||||
}
|
||||
Some(kind)
|
||||
}
|
||||
|
||||
/// A warp: its style, bend, distortions and orientation.
|
||||
fn warp(o: &Obj) -> Option<EnvelopeKind> {
|
||||
let style = whole(o, "DeformStyle")??;
|
||||
@@ -479,10 +498,11 @@ impl WarpedType {
|
||||
/// type's). The live envelope takes its drawn art's place and the hidden type goes. A warp that
|
||||
/// doesn't draw as its drawn art is checked again as a mesh envelope on the grid the file saves
|
||||
/// for it ([`live_on_saved_grid`]), which opens in its place where it does: its type stays
|
||||
/// editable in that shape, and the warp's style and bend are lost. Each check uses one of the
|
||||
/// import's checks and, for an envelope that shows, adds to what the page check allows
|
||||
/// ([`drawn_alike`]). Elsewhere, and where the type is set in a font that isn't available, the
|
||||
/// drawn art and the hidden type stay.
|
||||
/// editable in that shape, and the warp's style and bend are lost. With Distort Appearance, each
|
||||
/// is also checked mapping from its contents' bounds with their strokes ([`with_strokes`]). Each
|
||||
/// check uses one of the import's checks and, for an envelope that shows, adds to what the page
|
||||
/// check allows ([`drawn_alike`]). Elsewhere, and where the type is set in a font that isn't
|
||||
/// available, the drawn art and the hidden type stay.
|
||||
pub(crate) fn settle_envelopes(&mut self, doc: &mut Document, type_of: impl Fn(&Document, NodeId) -> Option<(NodeId, Node)>) -> TypedSettled {
|
||||
let mut settled = TypedSettled::default();
|
||||
if self.envelopes.is_empty() {
|
||||
@@ -537,7 +557,17 @@ impl WarpedType {
|
||||
// A warp that VectorCraft's own warp doesn't draw as its drawn art may still draw as the
|
||||
// grid the file saves for it: then it opens as a mesh envelope on that grid.
|
||||
let on_grid = if is_warp(&obj) { live_on_saved_grid(&obj, before.clone(), inner.clone()) } else { None };
|
||||
let kinds: Vec<NodeKind> = live(&obj, before, inner).into_iter().chain(on_grid).collect();
|
||||
// With Distort Appearance, a file's envelope may map its contents from their bounds with
|
||||
// their strokes or without them, and for type the size the file saves for that box is often
|
||||
// out of date (not the size of the type there now): each is checked.
|
||||
let kinds: Vec<NodeKind> = live(&obj, before, inner)
|
||||
.into_iter()
|
||||
.chain(on_grid)
|
||||
.flat_map(|k| {
|
||||
let stroked = with_strokes(&k);
|
||||
std::iter::once(k).chain(stroked)
|
||||
})
|
||||
.collect();
|
||||
if kinds.is_empty() {
|
||||
return Err(TypeKept::Read);
|
||||
}
|
||||
|
||||
@@ -5763,6 +5763,45 @@ fn a_warp_of_type_opens_as_a_mesh_on_its_saved_grid_and_keeps_its_shape_when_its
|
||||
}
|
||||
}
|
||||
|
||||
/// A warp of stroked type with Distort Appearance whose drawn art maps the type's bounds with its
|
||||
/// strokes onto the grid the file saves (not its glyphs' bounds alone) opens live as a mesh envelope
|
||||
/// mapping from that box (#3179).
|
||||
#[test]
|
||||
fn a_warp_of_stroked_type_maps_from_its_bounds_with_strokes_where_its_drawn_art_does() {
|
||||
use vectorcraft_doc::live::{envelope_content_boxes, envelope_stroke_margin};
|
||||
let Some(family) = installed_family() else { return };
|
||||
// "FLUX" stroked 4 points wide in black.
|
||||
let stroked = "/53 << /99 /CAITextPaint /0 << /0 2 /1 [ 1.0 0.0 1.0 0.0 0.0 ] >> >> /54 << /99 /CAITextPaint /0 << /0 2 /1 [ 1.0 0.0 0.0 0.0 1.0 ] >> >> /57 true /63 4.0";
|
||||
let doc = type_envelope_text(&family, None).replace("/53 << /99 /CAITextPaint /0 << /0 2 /1 [ 1.0 0.0 1.0 0.0 0.0 ] >> >>", stroked);
|
||||
let mut text = made_envelope_type(&doc);
|
||||
text.visible = true;
|
||||
assert!(matches!(&text.kind, NodeKind::Text(t) if t.runs[0].style.stroke_width == 4.0), "{text:?}");
|
||||
let content = vec![std::sync::Arc::new(text)];
|
||||
let (with, without) = envelope_content_boxes(&content, kurbo::Affine::IDENTITY).unwrap();
|
||||
let mut options = vectorcraft_doc::live::EnvelopeOptions::NEW;
|
||||
options.bounds_margin = envelope_stroke_margin(with, without).unwrap();
|
||||
assert!(options.bounds_margin.iter().all(|m| *m > 0.0), "{options:?}");
|
||||
let shaped = Node::new(
|
||||
vectorcraft_doc::NodeId(1_000_000),
|
||||
NodeKind::Envelope { content, kind: Box::new(rising_kind()), fidelity: 50.0, editing: false, options, frame: Box::default() },
|
||||
);
|
||||
let (drawn, page) = drawn_envelope(&shaped);
|
||||
let art =
|
||||
native_with_text(&layer("Art", true, &format!("{}\n{}", square(10, 10), type_envelope_on(&arc_keys(0.3), &rising_grid(), &drawn, ""))), &doc);
|
||||
let r = import(&eps(&format!("{}\n{page}", page_square(10, 10)), &art)).unwrap();
|
||||
assert_eq!(names(&r.document), ["Art"], "{:?}", r.warnings);
|
||||
let (envelopes, loose) = envelopes_and_type(&r.document);
|
||||
assert_eq!(envelopes, [vec!["FLUX".to_string()]], "{:?}", r.warnings);
|
||||
assert!(loose.is_empty(), "{loose:?}");
|
||||
let mut margin = None;
|
||||
r.document.walk(|n| {
|
||||
if let NodeKind::Envelope { options, .. } = &n.kind {
|
||||
margin = Some(options.bounds_margin);
|
||||
}
|
||||
});
|
||||
assert_eq!(margin, Some(options.bounds_margin));
|
||||
}
|
||||
|
||||
/// A warp of type whose editing data saves no grid it can be shaped by keeps its drawn art and its
|
||||
/// type, hidden, with the note, when its own warp draws apart from that art (#3179).
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user