1+ use std:: str:: FromStr ;
12use super :: transform_utils;
23use super :: utility_types:: ModifyInputsContext ;
34use crate :: consts:: { LAYER_INDENT_OFFSET , STACK_VERTICAL_GAP } ;
@@ -15,7 +16,7 @@ use graphene_std::renderer::Quad;
1516use graphene_std:: renderer:: convert_usvg_path:: convert_usvg_path;
1617use graphene_std:: table:: Table ;
1718use graphene_std:: text:: { Font , TypesettingConfig } ;
18- use graphene_std:: vector:: style:: { Fill , Gradient , GradientSpreadMethod , GradientStop , GradientStops , GradientType , PaintOrder , Stroke , StrokeAlign , StrokeCap , StrokeJoin } ;
19+ use graphene_std:: vector:: style:: { Fill , Gradient , GradientSpreadMethod , GradientStop , GradientStops , GradientType , GradientUnits , PaintOrder , Stroke , StrokeAlign , StrokeCap , StrokeJoin } ;
1920
2021#[ derive( ExtractField ) ]
2122pub struct GraphOperationMessageContext < ' a > {
@@ -502,7 +503,6 @@ const GRAPHITE_NAMESPACE: &str = "https://graphite.art";
502503fn extract_graphite_gradient_stops ( svg : & str ) -> HashMap < String , GradientStops > {
503504 let mut result = HashMap :: new ( ) ;
504505
505- // Quick check: if the SVG doesn't reference `graphite:midpoint` at all, skip parsing
506506 if !svg. contains ( "graphite:midpoint" ) {
507507 return result;
508508 }
@@ -512,6 +512,8 @@ fn extract_graphite_gradient_stops(svg: &str) -> HashMap<String, GradientStops>
512512 Err ( _) => return result,
513513 } ;
514514
515+ let mut raw_stops: HashMap < String , Vec < GradientStop > > = HashMap :: new ( ) ;
516+
515517 for node in doc. descendants ( ) {
516518 match node. tag_name ( ) . name ( ) {
517519 "linearGradient" | "radialGradient" => { }
@@ -523,7 +525,7 @@ fn extract_graphite_gradient_stops(svg: &str) -> HashMap<String, GradientStops>
523525 None => continue ,
524526 } ;
525527
526- let mut real_stops = Vec :: new ( ) ;
528+ let mut stops = Vec :: new ( ) ;
527529 let mut has_any_midpoint = false ;
528530
529531 for child in node. children ( ) {
@@ -532,35 +534,95 @@ fn extract_graphite_gradient_stops(svg: &str) -> HashMap<String, GradientStops>
532534 }
533535
534536 let midpoint = child. attribute ( ( GRAPHITE_NAMESPACE , "midpoint" ) ) . and_then ( |v| v. parse :: < f64 > ( ) . ok ( ) ) ;
535-
536537 if let Some ( midpoint) = midpoint {
537538 has_any_midpoint = true ;
538539
539- let offset = child. attribute ( "offset" ) . and_then ( |v| v . parse :: < f64 > ( ) . ok ( ) ) . unwrap_or ( 0. ) ;
540+ let offset = parse_stop_offset ( child. attribute ( "offset" ) . unwrap_or ( "0" ) ) ;
540541 let opacity = child. attribute ( "stop-opacity" ) . and_then ( |v| v. parse :: < f32 > ( ) . ok ( ) ) . unwrap_or ( 1. ) ;
541- let color = child. attribute ( "stop-color" ) . and_then ( |hex| parse_hex_stop_color ( hex , opacity) ) . unwrap_or ( Color :: BLACK ) ;
542+ let color = child. attribute ( "stop-color" ) . and_then ( |c| parse_stop_color ( c , opacity) ) . unwrap_or ( Color :: BLACK ) ;
542543
543- real_stops. push ( GradientStop { position : offset, midpoint, color } ) ;
544+ stops. push ( GradientStop {
545+ position : offset,
546+ midpoint,
547+ color,
548+ } ) ;
544549 }
545550 }
546551
547- if has_any_midpoint && !real_stops . is_empty ( ) {
548- result . insert ( gradient_id, GradientStops :: new ( real_stops ) ) ;
552+ if has_any_midpoint && !stops . is_empty ( ) {
553+ raw_stops . insert ( gradient_id, stops ) ;
549554 }
550555 }
551556
557+ for node in doc. descendants ( ) {
558+ match node. tag_name ( ) . name ( ) {
559+ "linearGradient" | "radialGradient" => { }
560+ _ => continue ,
561+ }
562+
563+ let gradient_id = match node. attribute ( "id" ) {
564+ Some ( id) => id. to_string ( ) ,
565+ None => continue ,
566+ } ;
567+
568+ if raw_stops. contains_key ( & gradient_id) {
569+ continue ;
570+ }
571+
572+ let href = node. attribute ( "href" ) . or_else ( || node. attribute ( ( "http://www.w3.org/1999/xlink" , "href" ) ) ) ;
573+ if let Some ( referenced_id) = href. and_then ( |h| h. strip_prefix ( '#' ) ) {
574+ if let Some ( inherited) = raw_stops. get ( referenced_id) {
575+ raw_stops. insert ( gradient_id, inherited. clone ( ) ) ;
576+ }
577+ }
578+ }
579+
580+ for ( id, stops) in raw_stops {
581+ result. insert ( id, GradientStops :: new ( stops) ) ;
582+ }
583+
552584 result
553585}
554586
555- fn parse_hex_stop_color ( hex : & str , opacity : f32 ) -> Option < Color > {
556- let hex = hex. strip_prefix ( '#' ) ?;
557- if hex. len ( ) != 6 {
558- return None ;
587+ fn parse_stop_offset ( s : & str ) -> f64 {
588+ if let Some ( pct) = s. strip_suffix ( '%' ) {
589+ pct. trim ( ) . parse :: < f64 > ( ) . unwrap_or ( 0. ) / 100.
590+ } else {
591+ s. trim ( ) . parse :: < f64 > ( ) . unwrap_or ( 0. )
559592 }
560- let r = u8:: from_str_radix ( & hex[ 0 ..2 ] , 16 ) . ok ( ) ? as f32 / 255. ;
561- let g = u8:: from_str_radix ( & hex[ 2 ..4 ] , 16 ) . ok ( ) ? as f32 / 255. ;
562- let b = u8:: from_str_radix ( & hex[ 4 ..6 ] , 16 ) . ok ( ) ? as f32 / 255. ;
563- Some ( Color :: from_rgbaf32_unchecked ( r, g, b, opacity) )
593+ }
594+
595+ fn parse_stop_color ( value : & str , opacity : f32 ) -> Option < Color > {
596+ let value = value. trim ( ) ;
597+ if let Some ( hex) = value. strip_prefix ( '#' ) {
598+ return parse_hex_color ( hex, opacity) ;
599+ }
600+ named_css_color ( value) . map ( |( r, g, b, alpha) | Color :: from_rgbaf32_unchecked ( r as f32 / 255. , g as f32 / 255. , b as f32 / 255. , alpha. unwrap_or ( opacity) ) )
601+ }
602+
603+ fn parse_hex_color ( hex : & str , opacity : f32 ) -> Option < Color > {
604+ let ( r, g, b) = match hex. len ( ) {
605+ 6 => (
606+ u8:: from_str_radix ( & hex[ 0 ..2 ] , 16 ) . ok ( ) ?,
607+ u8:: from_str_radix ( & hex[ 2 ..4 ] , 16 ) . ok ( ) ?,
608+ u8:: from_str_radix ( & hex[ 4 ..6 ] , 16 ) . ok ( ) ?,
609+ ) ,
610+ 3 => {
611+ let r = u8:: from_str_radix ( & hex[ 0 ..1 ] , 16 ) . ok ( ) ?;
612+ let g = u8:: from_str_radix ( & hex[ 1 ..2 ] , 16 ) . ok ( ) ?;
613+ let b = u8:: from_str_radix ( & hex[ 2 ..3 ] , 16 ) . ok ( ) ?;
614+ ( r * 17 , g * 17 , b * 17 )
615+ }
616+ _ => return None ,
617+ } ;
618+ Some ( Color :: from_rgbaf32_unchecked ( r as f32 / 255. , g as f32 / 255. , b as f32 / 255. , opacity) )
619+ }
620+
621+ fn named_css_color ( name : & str ) -> Option < ( u8 , u8 , u8 , Option < f32 > ) > {
622+ if name. to_ascii_lowercase ( ) == "transparent" {
623+ return Some ( ( 0 , 0 , 0 , Some ( 0. ) ) ) ;
624+ }
625+ svgtypes:: Color :: from_str ( name) . ok ( ) . map ( |c| ( c. red , c. green , c. blue , None ) )
564626}
565627
566628/// Import a usvg node as the root of an SVG import operation.
@@ -826,17 +888,27 @@ fn apply_usvg_fill(fill: &usvg::Fill, modify_inputs: &mut ModifyInputsContext, b
826888 end,
827889 gradient_type,
828890 stops,
891+ focal_center : start,
892+ focal_radius : 0. ,
893+ gradient_units : GradientUnits :: UserSpaceOnUse ,
829894 spread_method,
830895 } )
831896 }
832897 usvg:: Paint :: RadialGradient ( radial) => {
833898 let gradient_transform = usvg_transform ( radial. transform ( ) ) ;
899+ let inv_bounds = bounds_transform. inverse ( ) ;
900+
834901 let center = DVec2 :: new ( radial. cx ( ) as f64 , radial. cy ( ) as f64 ) ;
835902 let edge = center + DVec2 :: X * radial. r ( ) . get ( ) as f64 ;
836- let ( start, end ) = ( gradient_transform . transform_point2 ( center ) , gradient_transform. transform_point2 ( edge ) ) ;
837- let ( start , end) = ( bounds_transform . inverse ( ) . transform_point2 ( start ) , bounds_transform . inverse ( ) . transform_point2 ( end ) ) ;
903+ let start = inv_bounds . transform_point2 ( gradient_transform. transform_point2 ( center ) ) ;
904+ let end = inv_bounds . transform_point2 ( gradient_transform . transform_point2 ( edge ) ) ;
838905
839- let gradient_type = GradientType :: Radial ;
906+ let focal = DVec2 :: new ( radial. fx ( ) as f64 , radial. fy ( ) as f64 ) ;
907+ let focal_center = inv_bounds. transform_point2 ( gradient_transform. transform_point2 ( focal) ) ;
908+
909+ let focal_edge = focal + DVec2 :: X * radial. fr ( ) . get ( ) as f64 ;
910+ let focal_edge_transformed = inv_bounds. transform_point2 ( gradient_transform. transform_point2 ( focal_edge) ) ;
911+ let focal_radius = focal_center. distance ( focal_edge_transformed) ;
840912
841913 let stops = match graphite_gradient_stops. get ( radial. id ( ) ) {
842914 Some ( graphite_stops) => graphite_stops. clone ( ) ,
@@ -854,8 +926,11 @@ fn apply_usvg_fill(fill: &usvg::Fill, modify_inputs: &mut ModifyInputsContext, b
854926 Fill :: Gradient ( Gradient {
855927 start,
856928 end,
857- gradient_type,
929+ gradient_type : GradientType :: Radial ,
858930 stops,
931+ focal_center,
932+ focal_radius,
933+ gradient_units : GradientUnits :: UserSpaceOnUse ,
859934 spread_method,
860935 } )
861936 }
0 commit comments