mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-26 18:56:55 -06:00
Fix #1471: making and restyling type reports text that doesn't fit its frame
text.create, text.createInPath, text.setText and text.setStyle answer with overflow: [id…] for the area type and type on a path whose text doesn't fit (left out when all fits), and MCP add_text with overflow: true. add_text takes area type's fit and fitMinPercent.
This commit is contained in:
@@ -74,7 +74,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Create Text",
|
||||
[],
|
||||
None,
|
||||
"{x, y, text, vertical?: bool = false, size?: pt, font?: family, style?, color?, area?: {width, height, fit?: none|autoHeight|shrinkText, fitMinPercent?}, placeholder?: bool (fill it with placeholder text instead, as the Type tools do with prefs placeholderText), leadingModel?: \"romanBaseline\"|\"emBoxTop\", charAlign?: \"romanBaseline\"|\"emBoxTop\"|\"emBoxCenter\"|\"emBoxBottom\"|\"icfTop\"|\"icfBottom\" (default: emBoxTop and emBoxCenter while the interface is in Japanese, else romanBaseline)} → {id}; new area type gets Auto Size (fit autoHeight) when the autoSizeAreaType preference is on",
|
||||
"{x, y, text, vertical?: bool = false, size?: pt, font?: family, style?, color?, area?: {width, height, fit?: none|autoHeight|shrinkText, fitMinPercent?}, placeholder?: bool (fill it with placeholder text instead, as the Type tools do with prefs placeholderText), leadingModel?: \"romanBaseline\"|\"emBoxTop\", charAlign?: \"romanBaseline\"|\"emBoxTop\"|\"emBoxCenter\"|\"emBoxBottom\"|\"icfTop\"|\"icfBottom\" (default: emBoxTop and emBoxCenter while the interface is in Japanese, else romanBaseline)} → {id, overflow?: [id] (area type whose text doesn't fit its frame: the lines past it aren't drawn; left out when it fits)}; new area type gets Auto Size (fit autoHeight) when the autoSizeAreaType preference is on",
|
||||
has_doc,
|
||||
text_create
|
||||
),
|
||||
@@ -502,7 +502,9 @@ fn text_create(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
// Lays the text out (and sizes an Auto Size frame).
|
||||
super::typecmd::refresh_bounds(&mut t);
|
||||
}
|
||||
add_node(s, "Type", NodeKind::Text(Box::new(t)), Appearance::default(), None)
|
||||
let made = add_node(s, "Type", NodeKind::Text(Box::new(t)), Appearance::default(), None)?;
|
||||
let id = made.get("id").and_then(Value::as_u64).map(NodeId);
|
||||
Ok(super::typecmd::with_overflow(s, id.as_slice(), made))
|
||||
}
|
||||
|
||||
/// The paragraph attributes new type gets (the Type tools, `text.create`): aligned to the start of
|
||||
|
||||
@@ -45,7 +45,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Area / Path Type",
|
||||
[],
|
||||
None,
|
||||
"{path: id, mode: \"area\"|\"onPath\", text?: \"\", vertical?: bool = false, at?: [x, y] (on-path start: nearest point), size?, font?, style?, color? (as text.create), fit?: none|autoHeight|shrinkText, fitMinPercent? (area: as text.areaOptions; default autoHeight when the autoSizeAreaType preference is on), placeholder?: bool (placeholder text instead, as text.create), leadingModel?, charAlign? (as text.create)} turn a path into an area-type frame or a type-on-a-path baseline (the path's paint is dropped) → {id}",
|
||||
"{path: id, mode: \"area\"|\"onPath\", text?: \"\", vertical?: bool = false, at?: [x, y] (on-path start: nearest point), size?, font?, style?, color? (as text.create), fit?: none|autoHeight|shrinkText, fitMinPercent? (area: as text.areaOptions; default autoHeight when the autoSizeAreaType preference is on), placeholder?: bool (placeholder text instead, as text.create), leadingModel?, charAlign? (as text.create)} turn a path into an area-type frame or a type-on-a-path baseline (the path's paint is dropped) → {id, overflow?: [id] (the text doesn't fit; left out when it fits)}",
|
||||
has_doc,
|
||||
create_in_path
|
||||
),
|
||||
@@ -568,7 +568,7 @@ fn create_in_path(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
sel.set([id]);
|
||||
Ok(id)
|
||||
})?;
|
||||
Ok(json!({"id": id.0}))
|
||||
Ok(super::typecmd::with_overflow(s, &[id], json!({"id": id.0})))
|
||||
}
|
||||
|
||||
/// What `type.step` changes.
|
||||
|
||||
@@ -39,7 +39,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Set Text",
|
||||
[],
|
||||
None,
|
||||
"{id?|ids?, text} replace the contents of text objects (keeps the first run's style)",
|
||||
"{id?|ids?, text} replace the contents of text objects (keeps the first run's style) → {ids, overflow?: [id…] (area type and type on a path whose text no longer fits; left out when all fits)}",
|
||||
has_doc,
|
||||
set_text
|
||||
),
|
||||
@@ -75,7 +75,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Character",
|
||||
[],
|
||||
None,
|
||||
"{ids?|id?, font?, style?, size?: pt, leading?: pt|\"auto\", tracking?: 1/1000 em, justify?: \"auto\" (the start of each paragraph's direction)|\"left\"|\"center\"|\"right\"|\"justifyAll\", fill?: colour, features?: [\"dlig\", \"-liga\", …] OpenType, start?: byte, end?: byte} (size/leading are document points after object scaling; with a range: the character attributes style that range and justify the paragraphs it touches; without: all the text)",
|
||||
"{ids?|id?, font?, style?, size?: pt, leading?: pt|\"auto\", tracking?: 1/1000 em, justify?: \"auto\" (the start of each paragraph's direction)|\"left\"|\"center\"|\"right\"|\"justifyAll\", fill?: colour, features?: [\"dlig\", \"-liga\", …] OpenType, start?: byte, end?: byte} (size/leading are document points after object scaling; with a range: the character attributes style that range and justify the paragraphs it touches; without: all the text) → {ids, overflow?: [id…] (area type and type on a path whose text no longer fits; left out when all fits)}",
|
||||
has_doc,
|
||||
set_style
|
||||
),
|
||||
@@ -203,6 +203,22 @@ pub(crate) fn refresh_bounds_of(d: &mut Document, ids: &[NodeId]) {
|
||||
}
|
||||
}
|
||||
|
||||
/// `result` with `overflow: [id…]`: the area type and type on a path among `ids` whose text
|
||||
/// doesn't fit (the red overflow "+"; its lines past the frame aren't drawn). Left out when all of
|
||||
/// it fits. The commands that make type or change what it holds answer with it, so the call that
|
||||
/// pushed text out of its frame says so itself.
|
||||
pub(crate) fn with_overflow(s: &Session, ids: &[NodeId], mut result: Value) -> Value {
|
||||
let Ok(st) = s.doc() else { return result };
|
||||
let db = vectorcraft_text::FontDb::global();
|
||||
let fits = |t: &TextObject| matches!(t.kind, vectorcraft_doc::TextKind::Point) || !vectorcraft_text::layout(db, t).overflow;
|
||||
let over = |id: &&NodeId| matches!(st.doc.node(**id).map(|n| &n.kind), Some(NodeKind::Text(t)) if !fits(t));
|
||||
let over: Vec<u64> = ids.iter().filter(over).map(|i| i.0).collect();
|
||||
if !over.is_empty() {
|
||||
result["overflow"] = json!(over);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Text objects among `ids` and their descendants.
|
||||
fn text_ids(d: &Document, ids: &[NodeId]) -> Vec<NodeId> {
|
||||
let mut out = vec![];
|
||||
@@ -373,7 +389,7 @@ fn set_text(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(json!({ "ids": ids.iter().map(|i| i.0).collect::<Vec<_>>() }))
|
||||
Ok(with_overflow(s, &ids, json!({ "ids": ids.iter().map(|i| i.0).collect::<Vec<_>>() })))
|
||||
}
|
||||
|
||||
fn justify_param(v: &str) -> Option<Justify> {
|
||||
@@ -501,7 +517,7 @@ fn set_style(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(json!({ "ids": ids.iter().map(|i| i.0).collect::<Vec<_>>() }))
|
||||
Ok(with_overflow(s, &ids, json!({ "ids": ids.iter().map(|i| i.0).collect::<Vec<_>>() })))
|
||||
}
|
||||
|
||||
/// Reshape area type's frame with `f` ([`TextObject::transform_area`],
|
||||
|
||||
Reference in New Issue
Block a user