11use super :: TypesettingConfig ;
22use super :: text_context:: TextContext ;
3+ use core_types:: blending:: BlendMode ;
34use core_types:: list:: List ;
4- use glam:: DVec2 ;
5+ use core_types:: uuid:: NodeId ;
6+ use core_types:: {
7+ ATTR_BLEND_MODE , ATTR_EDITOR_LAYER_PATH , ATTR_FONT_SIZE , ATTR_OPACITY , ATTR_OPACITY_FILL , ATTR_TEXT_ALIGN , ATTR_TEXT_CHARACTER_SPACING , ATTR_TEXT_FONT , ATTR_TEXT_LINE_HEIGHT ,
8+ ATTR_TEXT_MAX_HEIGHT , ATTR_TEXT_MAX_WIDTH , ATTR_TEXT_TILT , ATTR_TRANSFORM ,
9+ } ;
10+ use glam:: { DAffine2 , DVec2 } ;
511use graphene_resource:: Resource ;
612use vector_types:: Vector ;
713
@@ -16,3 +22,59 @@ pub fn bounding_box(text: &str, font: &Resource, typesetting: TypesettingConfig,
1622pub fn lines_clipping ( text : & str , font : & Resource , typesetting : TypesettingConfig ) -> bool {
1723 TextContext :: with_thread_local ( |ctx| ctx. lines_clipping ( text, font, typesetting) )
1824}
25+
26+ /// Shapes each string item of a styled `List<String>` into vector geometry, reading its font and typesetting
27+ /// from the item's attributes (as set by the 'Text Layer' node) and re-applying its transform and blending
28+ /// attributes onto the produced paths. With `separate_glyphs`, each glyph becomes its own item.
29+ pub fn shape_text_list ( strings : & List < String > , separate_glyphs : bool ) -> List < Vector > {
30+ let mut result = List :: new ( ) ;
31+
32+ for index in 0 ..strings. len ( ) {
33+ let Some ( text) = strings. element ( index) else { continue } ;
34+ if text. is_empty ( ) {
35+ continue ;
36+ }
37+
38+ let font: Resource = strings. attribute_cloned_or_default ( ATTR_TEXT_FONT , index) ;
39+
40+ let defaults = TypesettingConfig :: default ( ) ;
41+ let typesetting = TypesettingConfig {
42+ font_size : strings. attribute_cloned_or ( ATTR_FONT_SIZE , index, defaults. font_size ) ,
43+ line_height_ratio : strings. attribute_cloned_or ( ATTR_TEXT_LINE_HEIGHT , index, defaults. line_height_ratio ) ,
44+ character_spacing : strings. attribute_cloned_or ( ATTR_TEXT_CHARACTER_SPACING , index, defaults. character_spacing ) ,
45+ max_width : strings. attribute_cloned_or :: < Option < f64 > > ( ATTR_TEXT_MAX_WIDTH , index, defaults. max_width ) ,
46+ max_height : strings. attribute_cloned_or :: < Option < f64 > > ( ATTR_TEXT_MAX_HEIGHT , index, defaults. max_height ) ,
47+ tilt : strings. attribute_cloned_or ( ATTR_TEXT_TILT , index, defaults. tilt ) ,
48+ align : strings. attribute_cloned_or ( ATTR_TEXT_ALIGN , index, defaults. align ) ,
49+ } ;
50+
51+ let vectors = to_path ( text, & font, typesetting, separate_glyphs) ;
52+ let transform = strings. attribute_cloned_or_default :: < DAffine2 > ( ATTR_TRANSFORM , index) ;
53+ let layer_path = strings. attribute_cloned_or_default :: < List < NodeId > > ( ATTR_EDITOR_LAYER_PATH , index) ;
54+ let blend_mode = strings. attribute :: < BlendMode > ( ATTR_BLEND_MODE , index) . copied ( ) ;
55+ let opacity = strings. attribute :: < f64 > ( ATTR_OPACITY , index) . copied ( ) ;
56+ let opacity_fill = strings. attribute :: < f64 > ( ATTR_OPACITY_FILL , index) . copied ( ) ;
57+
58+ for mut item in vectors. into_iter ( ) {
59+ if transform != DAffine2 :: IDENTITY {
60+ let local = item. attribute_cloned_or_default :: < DAffine2 > ( ATTR_TRANSFORM ) ;
61+ item. set_attribute ( ATTR_TRANSFORM , transform * local) ;
62+ }
63+ if !layer_path. is_empty ( ) {
64+ item. set_attribute ( ATTR_EDITOR_LAYER_PATH , layer_path. clone ( ) ) ;
65+ }
66+ if let Some ( blend_mode) = blend_mode {
67+ item. set_attribute ( ATTR_BLEND_MODE , blend_mode) ;
68+ }
69+ if let Some ( opacity) = opacity {
70+ item. set_attribute ( ATTR_OPACITY , opacity) ;
71+ }
72+ if let Some ( opacity_fill) = opacity_fill {
73+ item. set_attribute ( ATTR_OPACITY_FILL , opacity_fill) ;
74+ }
75+ result. push ( item) ;
76+ }
77+ }
78+
79+ result
80+ }
0 commit comments