Add String[] as a graphic type for typography (#4141)

* feat: Render List<String> as raw paths in SVG and Vell mode

* chore: code review

* chore: change the hardcoded layout bounds to parley's

* chore: code review

* feat: Split text node to text_layer and text_to_vector node

* fix: CI fail because of difference in nature of Mac and github action

* chore: fix

* chore: replace FontStack as it got removed in parley 0.9

* chore: fmt

* chore: migrate the rendering as of new resource architechture

* chore: add text_layer node to text tool for testing

* code review

* Make boolean ops support the Text type

* chore: Move fallback_font_resource authority from editor to text node

* Fix 'Text Layer' node missing font dropdown

* Change node doc comments from Vec<T> to T[]

* Add migrations from the old Text node to Text -> Text to Vector

* Consolidate

* Rename the text attributes and reorder tilt to come before max_width/height

* Detect legacy Text nodes in the split migration by their trailing separate_glyphs input

* Code review

* Frame Text layer thumbnails by laying out their text for bounds

* Give Text layers click targets and selection outlines via collect_metadata

* Route Text tool through Text to Vector with fill, fixing editing-preview placement

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Jatin Bharti
2026-06-20 01:09:52 +05:30
committed by GitHub
parent 13abf9fa8c
commit 5f100946f2
39 changed files with 967 additions and 204 deletions

View File

@@ -39,6 +39,7 @@ async fn render_intermediate<'a: 'n, T: 'static + Render + WasmNotSend + Send +
Context -> List<Raster<CPU>>,
Context -> List<Color>,
Context -> List<GradientStops>,
Context -> List<String>,
)]
data: impl Node<Context<'static>, Output = T>,
) -> RenderIntermediate {

View File

@@ -1,10 +1,13 @@
use core_types::Ctx;
use core_types::consts::{DEFAULT_FONT_SIZE, DEFAULT_LINE_HEIGHT};
use core_types::list::List;
use core_types::{ATTR_FONT, ATTR_FONT_SIZE, ATTR_LETTER_SPACING, ATTR_LETTER_TILT, ATTR_LINE_HEIGHT, ATTR_MAX_HEIGHT, ATTR_MAX_WIDTH, ATTR_TEXT_ALIGN, Ctx};
use graph_craft::application_io::resource::Resource;
use graphic_types::Vector;
pub use text_nodes::*;
/// Draws a text string as vector geometry with a choice of font and styling.
/// Produces a styled `String[]` carrying all typographic attributes.
///
/// Use the **Text to Vector** node to convert this into vector geometry if desired.
#[node_macro::node(category("Text"))]
fn text(
_: impl Ctx,
@@ -32,8 +35,13 @@ fn text(
/// Additional spacing, in pixels, added between each character.
#[unit(" px")]
#[step(0.1)]
character_spacing: f64,
/// Whether the *Max Width* property is enabled so that lines can wrap to fit its specified block width.
letter_spacing: f64,
/// The angle of faux italic slant applied to each glyph.
#[unit("°")]
#[hard_min(-85.)]
#[hard_max(85.)]
letter_tilt: f64,
/// Enables the maximum width constraint so lines can wrap.
#[widget(ParsedWidgetOverride::Hidden)]
has_max_width: bool,
/// The maximum width that the text block can occupy before wrapping to a new line. Otherwise, lines do not wrap.
@@ -49,27 +57,49 @@ fn text(
#[hard_min(1.)]
#[widget(ParsedWidgetOverride::Custom = "optional_f64")]
max_height: f64,
/// The angle of faux italic slant applied to each glyph.
#[unit("°")]
#[hard_min(-85.)]
#[hard_max(85.)]
tilt: f64,
/// The horizontal alignment of each line of text within its surrounding box.
/// To have an effect on a single line of text, *Max Width* must be set.
/// The horizontal alignment of each line of text within its surrounding box. To have an effect on a single line of text, *Max Width* must be set.
#[widget(ParsedWidgetOverride::Custom = "text_align")]
align: TextAlign,
) -> List<String> {
let mut list = List::new_from_element(text);
if font != Resource::default() {
list.set_attribute(ATTR_FONT, 0, font);
}
if (size - DEFAULT_FONT_SIZE).abs() > f64::EPSILON {
list.set_attribute(ATTR_FONT_SIZE, 0, size);
}
if (line_height - DEFAULT_LINE_HEIGHT).abs() > f64::EPSILON {
list.set_attribute(ATTR_LINE_HEIGHT, 0, line_height);
}
if letter_spacing != 0. {
list.set_attribute(ATTR_LETTER_SPACING, 0, letter_spacing);
}
if letter_tilt != 0. {
list.set_attribute(ATTR_LETTER_TILT, 0, letter_tilt);
}
if has_max_width {
list.set_attribute(ATTR_MAX_WIDTH, 0, Some(max_width));
}
if has_max_height {
list.set_attribute(ATTR_MAX_HEIGHT, 0, Some(max_height));
}
if align != TextAlign::default() {
list.set_attribute(ATTR_TEXT_ALIGN, 0, align);
}
list
}
/// Converts a styled `String[]` into vector geometry.
#[node_macro::node(category("Text"), name("Text to Vector"))]
fn text_to_vector(
_: impl Ctx,
/// A styled list of text strings produced by the **Text** node (or any other `String[]` source).
#[implementations(List<String>)]
strings: List<String>,
/// Whether to split every letterform into its own vector item. Otherwise, a single vector compound path is produced.
separate_glyphs: bool,
) -> List<Vector> {
let typesetting = TypesettingConfig {
font_size: size,
line_height_ratio: line_height,
character_spacing,
max_width: has_max_width.then_some(max_width),
max_height: has_max_height.then_some(max_height),
tilt,
align,
};
to_path(&text, &font, typesetting, separate_glyphs)
shape_text_list(&strings, separate_glyphs)
}