@@ -10,6 +10,8 @@ use core::f64::consts::{FRAC_PI_2, PI, TAU};
1010use glam:: { DAffine2 , DVec2 } ;
1111use graphene_std:: Color ;
1212use graphene_std:: math:: quad:: Quad ;
13+ use graphene_std:: table:: Table ;
14+ use graphene_std:: text:: { TextAlign , TypesettingConfig , load_font, to_path} ;
1315use graphene_std:: vector:: click_target:: ClickTargetType ;
1416use graphene_std:: vector:: { PointId , SegmentId , Vector } ;
1517use std:: collections:: HashMap ;
@@ -980,7 +982,7 @@ impl OverlayContextInternal {
980982 // This matches the Canvas2D checkerboard pattern
981983 let mut data = vec ! [ 0u8 ; ( PATTERN_WIDTH * PATTERN_HEIGHT * 4 ) as usize ] ;
982984 let rgba = color. to_rgba8_srgb ( ) ;
983-
985+
984986 // Set pixels at (0,0) and (2,2) to the specified color
985987 let pixels = [ ( 0 , 0 ) , ( 2 , 2 ) ] ;
986988 for & ( x, y) in & pixels {
@@ -1006,24 +1008,55 @@ impl OverlayContextInternal {
10061008 }
10071009
10081010 fn get_width ( & self , text : & str ) -> f64 {
1009- // Basic text width estimation that matches the text() function implementation
1010- // This uses the same character width estimation for consistency
1011- // Note: This is primarily used for manual positioning calculations, but ideally
1012- // the pivot system in text() should handle centering automatically
1013- const CHAR_WIDTH : f64 = 7.0 ; // Approximate character width at 12px font size
1014- text. len ( ) as f64 * CHAR_WIDTH
1011+ // Use the actual text-to-path system to get precise text width
1012+ const FONT_SIZE : f64 = 12.0 ;
1013+
1014+ let typesetting = TypesettingConfig {
1015+ font_size : FONT_SIZE ,
1016+ line_height_ratio : 1.2 ,
1017+ character_spacing : 0.0 ,
1018+ max_width : None ,
1019+ max_height : None ,
1020+ tilt : 0.0 ,
1021+ align : TextAlign :: Left ,
1022+ } ;
1023+
1024+ // Load Source Sans Pro font data
1025+ const FONT_DATA : & [ u8 ] = include_bytes ! ( "source-sans-pro-regular.ttf" ) ;
1026+ let font_blob = Some ( load_font ( FONT_DATA ) ) ;
1027+
1028+ // Convert text to paths and calculate actual bounds
1029+ let text_table = to_path ( text, font_blob, typesetting, false ) ;
1030+ let text_bounds = self . calculate_text_bounds ( & text_table) ;
1031+ text_bounds. width ( )
10151032 }
10161033
10171034 fn text ( & mut self , text : & str , font_color : & str , background_color : Option < & str > , transform : DAffine2 , padding : f64 , pivot : [ Pivot ; 2 ] ) {
1018- // For now, implement a basic text rendering approach
1019- // This is a simplified version that can be enhanced later with full font support
1020-
1021- // Calculate approximate text dimensions (basic estimation)
1022- let char_width = 7.0 ; // Approximate character width at 12px font size
1023- let char_height = 12.0 ; // Font size
1024- let text_width = text. len ( ) as f64 * char_width;
1025- let text_height = char_height;
1026-
1035+ // Use the proper text-to-path system for accurate text rendering
1036+ const FONT_SIZE : f64 = 12.0 ;
1037+
1038+ // Create typesetting configuration
1039+ let typesetting = TypesettingConfig {
1040+ font_size : FONT_SIZE ,
1041+ line_height_ratio : 1.2 ,
1042+ character_spacing : 0.0 ,
1043+ max_width : None ,
1044+ max_height : None ,
1045+ tilt : 0.0 ,
1046+ align : TextAlign :: Left , // We'll handle alignment manually via pivot
1047+ } ;
1048+
1049+ // Load Source Sans Pro font data
1050+ const FONT_DATA : & [ u8 ] = include_bytes ! ( "source-sans-pro-regular.ttf" ) ;
1051+ let font_blob = Some ( load_font ( FONT_DATA ) ) ;
1052+
1053+ // Convert text to vector paths using the existing text system
1054+ let text_table = to_path ( text, font_blob, typesetting, false ) ;
1055+ // Calculate text bounds from the generated paths
1056+ let text_bounds = self . calculate_text_bounds ( & text_table) ;
1057+ let text_width = text_bounds. width ( ) ;
1058+ let text_height = text_bounds. height ( ) ;
1059+
10271060 // Calculate position based on pivot
10281061 let mut position = DVec2 :: ZERO ;
10291062 match pivot[ 0 ] {
@@ -1032,43 +1065,100 @@ impl OverlayContextInternal {
10321065 Pivot :: End => position. x = -padding - text_width,
10331066 }
10341067 match pivot[ 1 ] {
1035- Pivot :: Start => position. y = padding + text_height,
1036- Pivot :: Middle => position. y = text_height / 2.0 ,
1037- Pivot :: End => position. y = -padding,
1068+ Pivot :: Start => position. y = padding + text_height * 0.8 , // Account for baseline
1069+ Pivot :: Middle => position. y = text_height * 0.3 , // Center on x-height
1070+ Pivot :: End => position. y = -padding - text_height * 0.2 ,
10381071 }
1039-
1072+
10401073 let text_transform = transform * DAffine2 :: from_translation ( position) ;
10411074 let device_transform = self . get_transform ( ) ;
10421075 let combined_transform = kurbo:: Affine :: new ( text_transform. to_cols_array ( ) ) ;
10431076 let vello_transform = device_transform * combined_transform;
1044-
1077+
10451078 // Draw background if specified
10461079 if let Some ( bg_color) = background_color {
10471080 let bg_rect = kurbo:: Rect :: new (
1048- -padding,
1049- -padding,
1050- text_width + padding,
1051- text_height + padding
1052- ) ;
1053- self . scene . fill (
1054- peniko:: Fill :: NonZero ,
1055- vello_transform,
1056- Self :: parse_color ( bg_color) ,
1057- None ,
1058- & bg_rect,
1081+ text_bounds. min_x ( ) - padding,
1082+ text_bounds. min_y ( ) - padding,
1083+ text_bounds. max_x ( ) + padding,
1084+ text_bounds. max_y ( ) + padding,
10591085 ) ;
1086+ self . scene . fill ( peniko:: Fill :: NonZero , vello_transform, Self :: parse_color ( bg_color) , None , & bg_rect) ;
1087+ }
1088+
1089+ // Render the actual text paths
1090+ self . render_text_paths ( & text_table, font_color, vello_transform) ;
1091+ }
1092+
1093+ // Calculate bounds of text from vector table
1094+ fn calculate_text_bounds ( & self , text_table : & Table < Vector > ) -> kurbo:: Rect {
1095+ let mut min_x = f64:: INFINITY ;
1096+ let mut min_y = f64:: INFINITY ;
1097+ let mut max_x = f64:: NEG_INFINITY ;
1098+ let mut max_y = f64:: NEG_INFINITY ;
1099+
1100+ for row in text_table. iter ( ) {
1101+ // Use the existing segment_bezier_iter to get all bezier curves
1102+ for ( _, bezier, _, _) in row. element . segment_bezier_iter ( ) {
1103+ let transformed_bezier = bezier. apply_transformation ( |point| row. transform . transform_point2 ( point) ) ;
1104+
1105+ // Add start and end points to bounds
1106+ let points = [ transformed_bezier. start , transformed_bezier. end ] ;
1107+ for point in points {
1108+ min_x = min_x. min ( point. x ) ;
1109+ min_y = min_y. min ( point. y ) ;
1110+ max_x = max_x. max ( point. x ) ;
1111+ max_y = max_y. max ( point. y ) ;
1112+ }
1113+
1114+ // Add handle points if they exist
1115+ match transformed_bezier. handles {
1116+ bezier_rs:: BezierHandles :: Quadratic { handle } => {
1117+ min_x = min_x. min ( handle. x ) ;
1118+ min_y = min_y. min ( handle. y ) ;
1119+ max_x = max_x. max ( handle. x ) ;
1120+ max_y = max_y. max ( handle. y ) ;
1121+ }
1122+ bezier_rs:: BezierHandles :: Cubic { handle_start, handle_end } => {
1123+ for handle in [ handle_start, handle_end] {
1124+ min_x = min_x. min ( handle. x ) ;
1125+ min_y = min_y. min ( handle. y ) ;
1126+ max_x = max_x. max ( handle. x ) ;
1127+ max_y = max_y. max ( handle. y ) ;
1128+ }
1129+ }
1130+ _ => { }
1131+ }
1132+ }
1133+ }
1134+
1135+ if min_x. is_finite ( ) && min_y. is_finite ( ) && max_x. is_finite ( ) && max_y. is_finite ( ) {
1136+ kurbo:: Rect :: new ( min_x, min_y, max_x, max_y)
1137+ } else {
1138+ // Fallback for empty text
1139+ kurbo:: Rect :: new ( 0.0 , 0.0 , 0.0 , 12.0 )
1140+ }
1141+ }
1142+
1143+ // Render text paths to the vello scene using existing infrastructure
1144+ fn render_text_paths ( & mut self , text_table : & Table < Vector > , font_color : & str , base_transform : kurbo:: Affine ) {
1145+ let color = Self :: parse_color ( font_color) ;
1146+
1147+ for row in text_table. iter ( ) {
1148+ // Use the existing bezier_to_path infrastructure to convert Vector to BezPath
1149+ let mut path = BezPath :: new ( ) ;
1150+ let mut last_point = None ;
1151+
1152+ for ( _, bezier, start_id, end_id) in row. element . segment_bezier_iter ( ) {
1153+ let move_to = last_point != Some ( start_id) ;
1154+ last_point = Some ( end_id) ;
1155+
1156+ self . bezier_to_path ( bezier, row. transform . clone ( ) , move_to, & mut path) ;
1157+ }
1158+
1159+ // Render the path
1160+ self . scene . fill ( peniko:: Fill :: NonZero , base_transform, color, None , & path) ;
10601161 }
1061-
1062- // For now, draw a simple rectangle to represent text
1063- // TODO: Implement proper font rendering using vello's text capabilities
1064- let text_rect = kurbo:: Rect :: new ( 0.0 , 0.0 , text_width, text_height) ;
1065- self . scene . stroke (
1066- & kurbo:: Stroke :: new ( 1.0 ) ,
1067- vello_transform,
1068- Self :: parse_color ( font_color) ,
1069- None ,
1070- & text_rect,
1071- ) ;
10721162 }
10731163
10741164 fn translation_box ( & mut self , translation : DVec2 , quad : Quad , typed_string : Option < String > ) {
0 commit comments