mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 22:22:25 +00:00
An envelope mapped type from its laid-out box: ascent to descent for point type, the frame for area type. The distorted glyphs then filled less of the envelope than other art does, with room above the capitals and below the baseline. The envelope now maps type from the bounds of its glyph outlines, underlines and strikethroughs when the type is on its own, in groups and layers, or used as a clipping path. Inline graphics count by their boxes. Rotated type and type in perspective map from their outlines as they are drawn. Make with Mesh, Make with Top Object and Reset with Mesh without Maintain Shape use those bounds. Type that draws nothing keeps its laid-out box, and so does type inside any other object, such as a blend, a repeat or a symbol instance. Shrink Text to Fit type is measured as it is laid out on the page. vectorcraft-text's envelope_ink measures the type, and register_envelope_ink registers it with the document crate. The engine session, Renderer::new, live evaluation in vectorcraft-effects and the .ai reader register it. The document crate keeps the measured boxes per thread, by object and by a hash of the content, and measures again when the type, the fonts, the hyphenation exceptions or the transform's scale or rotation change. This is VectorCraft's own rule. Envelopes of type made in VectorCraft and in saved files map from the glyph outline bounds instead of the type's laid-out box, so they redraw differently.
64 lines
3.0 KiB
Rust
64 lines
3.0 KiB
Rust
//! Live objects (blends, envelopes, gradient meshes, repeats) evaluated alike for the renderer and
|
|
//! every exporter, with what the pure evaluation in [`vectorcraft_doc::live`] can't do itself
|
|
//! ([`Hooks`]): type inside an envelope distorts as its glyph outlines, run by run
|
|
//! ([`outline_text`]); symbol instances as their symbol's art; with Distort Appearance strokes and
|
|
//! geometry effects bend with the art ([`bake_appearance`]); with Distort Pattern Fills the pattern
|
|
//! tiles do.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use vectorcraft_doc::live::{self, Hooks, Outliner};
|
|
use vectorcraft_doc::{Document, Node, NodeKind};
|
|
use vectorcraft_geom::{Affine, Rect};
|
|
|
|
use crate::{bake_appearance, outline_text};
|
|
|
|
/// The text outliner live evaluation uses: type as its glyph outlines, one path per run in the
|
|
/// run's paint ([`outline_text`]).
|
|
pub fn text_outliner() -> Outliner<'static> {
|
|
Some(&outline_text)
|
|
}
|
|
|
|
/// Type outlined, or a symbol instance as its symbol's art (from `doc`) placed by the instance,
|
|
/// through the symbol's 9-slice guides when it has them ([`Document::place_symbol_art`], `None`
|
|
/// past its object limit). As an [`OutlineHook`](crate::OutlineHook), it gives compound shapes and
|
|
/// Pathfinder operations the symbol instances among their members.
|
|
pub fn outline_object(doc: Option<&Document>, n: &Node) -> Option<Node> {
|
|
match &n.kind {
|
|
NodeKind::SymbolInstance { symbol, xf } => {
|
|
let doc = doc?;
|
|
let sym = doc.symbols.iter().find(|s| s.name == *symbol)?;
|
|
let art = vectorcraft_doc::symbol_overrides::dynamic_art(&sym.art, n);
|
|
let art = doc.place_symbol_art(symbol, &art, *xf)?;
|
|
Some(Node::group(n.id, vec![Arc::new(art)]))
|
|
}
|
|
_ => outline_text(n),
|
|
}
|
|
}
|
|
|
|
/// Run `f` with the hooks live evaluation needs; symbols and patterns come from `doc` (without a
|
|
/// document, symbol instances and pattern tiles stay as they are).
|
|
fn with_hooks<R>(doc: Option<&Document>, f: impl FnOnce(Hooks) -> R) -> R {
|
|
// Envelopes map type from its glyphs here too, without an engine session.
|
|
vectorcraft_text::register_envelope_ink();
|
|
let outline = |n: &Node| outline_object(doc, n);
|
|
let tiles = |name: &str, xf: Affine, region: Rect| Some(doc?.pattern(name)?.instances_in(xf, region));
|
|
f(Hooks { outline: Some(&outline), appearance: Some(&bake_appearance), pattern: Some(&tiles), symbols: doc })
|
|
}
|
|
|
|
/// One level of evaluation of live object `n` ([`live::expand_live_hooks`]).
|
|
pub fn expand_live(doc: Option<&Document>, n: &Node) -> Vec<Node> {
|
|
with_hooks(doc, |h| live::expand_live_hooks(n, h))
|
|
}
|
|
|
|
/// Live object `n` evaluated into a plain group ([`live::expanded_group_hooks`]): Expand.
|
|
pub fn expanded_live_group(doc: Option<&Document>, n: &Node) -> Node {
|
|
with_hooks(doc, |h| live::expanded_group_hooks(n, h))
|
|
}
|
|
|
|
/// `n` with every live object in it replaced by its evaluated art ([`live::expand_deep_hooks`]):
|
|
/// what the exporters write for live objects.
|
|
pub fn expand_live_deep(doc: Option<&Document>, n: &Node) -> Node {
|
|
with_hooks(doc, |h| live::expand_deep_hooks(n, h))
|
|
}
|