From 9de6a637d0b50f29fd9f350a44ab5fb830409d82 Mon Sep 17 00:00:00 2001 From: Rocky Shi Date: Sat, 20 Dec 2025 00:25:27 +1300 Subject: [PATCH 1/3] feat: Parse `light-dark()` CSS function by extracting the first argument and add tests. --- crates/usvg/src/parser/svgtree/parse.rs | 70 ++++++++++++++++++++++++- crates/usvg/tests/parser.rs | 49 +++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/crates/usvg/src/parser/svgtree/parse.rs b/crates/usvg/src/parser/svgtree/parse.rs index 2eae321b6..503eefa4b 100644 --- a/crates/usvg/src/parser/svgtree/parse.rs +++ b/crates/usvg/src/parser/svgtree/parse.rs @@ -13,6 +13,71 @@ const SVG_NS: &str = "http://www.w3.org/2000/svg"; const XLINK_NS: &str = "http://www.w3.org/1999/xlink"; const XML_NAMESPACE_NS: &str = "http://www.w3.org/XML/1998/namespace"; +/// Resolves CSS `light-dark(value1, value2)` function by extracting the first (light-mode) value. +/// +/// The `light-dark()` CSS function is used for dark mode support. Since usvg doesn't support +/// color scheme preferences, we extract the first value (light-mode color) as the fallback. +/// +/// This function handles nested parentheses (e.g., `light-dark(rgb(0, 0, 0), rgb(255, 255, 255))`) +/// and recursively resolves any nested `light-dark()` calls. +fn resolve_light_dark(value: &str) -> std::borrow::Cow<'_, str> { + use std::borrow::Cow; + + let Some(start_idx) = value.find("light-dark(") else { + return Cow::Borrowed(value); + }; + + let func_start = start_idx + "light-dark(".len(); + let rest = &value[func_start..]; + + // Find the first argument by tracking parentheses depth + let mut depth = 1; + let mut first_arg_end = None; + let mut func_end = None; + + for (i, c) in rest.char_indices() { + match c { + '(' => depth += 1, + ')' => { + depth -= 1; + if depth == 0 { + func_end = Some(i); + if first_arg_end.is_none() { + first_arg_end = Some(i); + } + break; + } + } + ',' if depth == 1 && first_arg_end.is_none() => { + first_arg_end = Some(i); + } + _ => {} + } + } + + let Some(first_arg_end) = first_arg_end else { + return Cow::Borrowed(value); + }; + let func_end = func_end.unwrap_or(rest.len()); + + let first_arg = rest[..first_arg_end].trim(); + + // Reconstruct the value with light-dark() replaced by the first argument + let mut result = String::with_capacity(value.len()); + result.push_str(&value[..start_idx]); + result.push_str(first_arg); + // Append any remaining content after the closing parenthesis + if func_end + 1 < rest.len() { + result.push_str(&rest[func_end + 1..]); + } + + // Recursively resolve any remaining light-dark() calls + match resolve_light_dark(&result) { + Cow::Borrowed(_) => Cow::Owned(result), + Cow::Owned(s) => Cow::Owned(s), + } +} + impl<'input> Document<'input> { /// Parses a [`Document`] from a [`roxmltree::Document`]. pub fn parse_tree( @@ -321,7 +386,10 @@ pub(crate) fn parse_svg_element<'input>( let mut write_declaration = |declaration: &Declaration| { // TODO: perform XML attribute normalization let imp = declaration.important; - let val = declaration.value; + // Resolve CSS light-dark() function by extracting the first (light-mode) value. + // This handles Draw.io SVG exports that use light-dark() for dark mode support. + let val_cow = resolve_light_dark(declaration.value); + let val = val_cow.as_ref(); if declaration.name == "marker" { insert_attribute(AId::MarkerStart, val, imp); diff --git a/crates/usvg/tests/parser.rs b/crates/usvg/tests/parser.rs index 7b34806d8..ec346522d 100644 --- a/crates/usvg/tests/parser.rs +++ b/crates/usvg/tests/parser.rs @@ -547,3 +547,52 @@ fn no_text_nodes() { let tree = usvg::Tree::from_str(&svg, &usvg::Options::default()).unwrap(); assert!(!tree.has_text_nodes()); } + +/// Tests that the CSS `light-dark()` function is correctly parsed. +/// Draw.io exports SVGs with `light-dark()` for dark mode support. +/// usvg should extract the first (light-mode) value. +#[test] +fn light_dark_css_function() { + // Test with simple color values + let svg = r#" + + "#; + + let tree = usvg::Tree::from_str(&svg, &usvg::Options::default()).unwrap(); + let usvg::Node::Path(ref path) = &tree.root().children()[0] else { + unreachable!() + }; + + // Should extract "red" (the first/light-mode value) + assert_eq!( + path.fill().unwrap().paint(), + &usvg::Paint::Color(Color::new_rgb(255, 0, 0)) + ); +} + +/// Tests that `light-dark()` with rgb() function arguments is correctly parsed. +#[test] +fn light_dark_css_function_with_rgb() { + let svg = r#" + + + + "#; + + let tree = usvg::Tree::from_str(&svg, &usvg::Options::default()).unwrap(); + + let usvg::Node::Group(ref group) = &tree.root().children()[0] else { + unreachable!() + }; + let usvg::Node::Path(ref path) = &group.children()[0] else { + unreachable!() + }; + + // Should extract rgb(0, 128, 0) which is green + assert_eq!( + path.fill().unwrap().paint(), + &usvg::Paint::Color(Color::new_rgb(0, 128, 0)) + ); +} From 58f2a53bf8b09f0e16aea5d1acd4f14cbdc9f25f Mon Sep 17 00:00:00 2001 From: Rocky Shi Date: Sat, 20 Dec 2025 11:47:24 +1300 Subject: [PATCH 2/3] feat: Add `--color-scheme` option to resolve CSS `light-dark()` function and inherit it in nested SVGs. --- crates/resvg/src/main.rs | 15 ++++++ crates/usvg/src/main.rs | 15 ++++++ crates/usvg/src/parser/mod.rs | 11 ++-- crates/usvg/src/parser/options.rs | 23 ++++++++ crates/usvg/src/parser/svgtree/parse.rs | 72 +++++++++++++++++++------ crates/usvg/src/parser/svgtree/text.rs | 18 +++++-- crates/usvg/tests/parser.rs | 61 +++++++++++++++++++++ 7 files changed, 193 insertions(+), 22 deletions(-) diff --git a/crates/resvg/src/main.rs b/crates/resvg/src/main.rs index 830eb8d4f..355076cf9 100644 --- a/crates/resvg/src/main.rs +++ b/crates/resvg/src/main.rs @@ -206,6 +206,8 @@ OPTIONS: --export-area-drawing Use drawing's tight bounding box instead of image size. Used during normal rendering and not during --export-id + --color-scheme SCHEME Sets the color scheme for resolving CSS light-dark() function + [default: light] [possible values: light, dark] --perf Prints performance stats --quiet Disables warnings @@ -240,6 +242,7 @@ struct CliArgs { skip_system_fonts: bool, list_fonts: bool, style_sheet: Option, + color_scheme: usvg::ColorScheme, query_all: bool, export_id: Option, @@ -310,6 +313,9 @@ fn collect_args() -> Result { export_area_drawing: input.contains("--export-area-drawing"), style_sheet: input.opt_value_from_str("--stylesheet").unwrap_or_default(), + color_scheme: input + .opt_value_from_fn("--color-scheme", parse_color_scheme)? + .unwrap_or_default(), perf: input.contains("--perf"), quiet: input.contains("--quiet"), @@ -372,6 +378,14 @@ fn parse_languages(s: &str) -> Result, String> { Ok(langs) } +fn parse_color_scheme(s: &str) -> Result { + match s.to_lowercase().as_str() { + "light" => Ok(usvg::ColorScheme::Light), + "dark" => Ok(usvg::ColorScheme::Dark), + _ => Err("invalid color scheme, expected 'light' or 'dark'".to_string()), + } +} + #[derive(Clone, PartialEq, Debug)] enum InputFrom { Stdin, @@ -578,6 +592,7 @@ fn parse_args() -> Result { font_resolver: usvg::FontResolver::default(), fontdb: Arc::new(fontdb::Database::new()), style_sheet, + color_scheme: args.color_scheme, }; Ok(Args { diff --git a/crates/usvg/src/main.rs b/crates/usvg/src/main.rs index 84ae50250..83a1ef873 100644 --- a/crates/usvg/src/main.rs +++ b/crates/usvg/src/main.rs @@ -102,6 +102,8 @@ OPTIONS: --transforms-precision NUM Set the transform values numeric precision. Smaller precision can lead to a malformed output in some cases [values: 2..8 (inclusive)] [default: 8] + --color-scheme SCHEME Sets the color scheme for resolving CSS light-dark() function + [default: light] [possible values: light, dark] --quiet Disables warnings ARGS: @@ -139,6 +141,7 @@ struct Args { coordinates_precision: Option, transforms_precision: Option, style_sheet: Option, + color_scheme: usvg::ColorScheme, quiet: bool, @@ -209,6 +212,9 @@ fn collect_args() -> Result { .opt_value_from_fn("--coordinates-precision", parse_precision)?, transforms_precision: input.opt_value_from_fn("--transforms-precision", parse_precision)?, style_sheet: input.opt_value_from_str("--stylesheet").unwrap_or_default(), + color_scheme: input + .opt_value_from_fn("--color-scheme", parse_color_scheme)? + .unwrap_or_default(), quiet: input.contains("--quiet"), @@ -285,6 +291,14 @@ fn parse_precision(s: &str) -> Result { } } +fn parse_color_scheme(s: &str) -> Result { + match s.to_lowercase().as_str() { + "light" => Ok(usvg::ColorScheme::Light), + "dark" => Ok(usvg::ColorScheme::Dark), + _ => Err("invalid color scheme, expected 'light' or 'dark'".to_string()), + } +} + #[derive(Clone, PartialEq, Debug)] enum InputFrom<'a> { Stdin, @@ -432,6 +446,7 @@ fn process(args: Args) -> Result<(), String> { font_resolver: usvg::FontResolver::default(), fontdb: Arc::new(fontdb), style_sheet, + color_scheme: args.color_scheme, }; let input_svg = match in_svg { diff --git a/crates/usvg/src/parser/mod.rs b/crates/usvg/src/parser/mod.rs index b3fbccdd6..7c5577831 100644 --- a/crates/usvg/src/parser/mod.rs +++ b/crates/usvg/src/parser/mod.rs @@ -21,7 +21,7 @@ mod text; #[cfg(feature = "text")] pub(crate) use converter::Cache; pub use image::{ImageHrefDataResolverFn, ImageHrefResolver, ImageHrefStringResolverFn}; -pub use options::Options; +pub use options::{ColorScheme, Options}; pub(crate) use svgtree::{AId, EId}; /// List of all errors. @@ -136,7 +136,12 @@ impl crate::Tree { (opt.font_resolver.select_fallback)(c, used_fonts, db) }), }, - ..Options::default() + // Inherit font_family from parent + font_family: opt.font_family.clone(), + // Inherit style_sheet from parent + style_sheet: opt.style_sheet.clone(), + // Inherit color_scheme from parent so nested SVGs use the same scheme + color_scheme: opt.color_scheme, }; Self::from_data(data, &nested_opt) @@ -157,7 +162,7 @@ impl crate::Tree { /// Parses `Tree` from `roxmltree::Document`. pub fn from_xmltree(doc: &roxmltree::Document, opt: &Options) -> Result { - let doc = svgtree::Document::parse_tree(doc, opt.style_sheet.as_deref())?; + let doc = svgtree::Document::parse_tree(doc, opt.style_sheet.as_deref(), opt.color_scheme)?; self::converter::convert_doc(&doc, opt) } } diff --git a/crates/usvg/src/parser/options.rs b/crates/usvg/src/parser/options.rs index fcf70b114..5eff957f4 100644 --- a/crates/usvg/src/parser/options.rs +++ b/crates/usvg/src/parser/options.rs @@ -8,6 +8,23 @@ use std::sync::Arc; use crate::FontResolver; use crate::{ImageHrefResolver, ImageRendering, ShapeRendering, Size, TextRendering}; +/// The color scheme preference for resolving CSS `light-dark()` function. +/// +/// The CSS `light-dark()` function allows specifying two color values where the first +/// is for light mode and the second is for dark mode. This option controls which +/// value is extracted. +/// +/// This is useful for rendering SVGs exported from applications like Draw.io that +/// use `light-dark()` for dark mode support. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub enum ColorScheme { + /// Use the first value (light mode color). This is the default. + #[default] + Light, + /// Use the second value (dark mode color). + Dark, +} + /// Processing options. #[derive(Debug)] pub struct Options<'a> { @@ -98,6 +115,11 @@ pub struct Options<'a> { /// A CSS stylesheet that should be injected into the SVG. Can be used to overwrite /// certain attributes. pub style_sheet: Option, + + /// The color scheme to use when resolving CSS `light-dark()` function. + /// + /// Default: `ColorScheme::Light` + pub color_scheme: ColorScheme, } impl Default for Options<'_> { @@ -119,6 +141,7 @@ impl Default for Options<'_> { #[cfg(feature = "text")] fontdb: Arc::new(fontdb::Database::new()), style_sheet: None, + color_scheme: ColorScheme::default(), } } } diff --git a/crates/usvg/src/parser/svgtree/parse.rs b/crates/usvg/src/parser/svgtree/parse.rs index 503eefa4b..51d33a012 100644 --- a/crates/usvg/src/parser/svgtree/parse.rs +++ b/crates/usvg/src/parser/svgtree/parse.rs @@ -13,14 +13,16 @@ const SVG_NS: &str = "http://www.w3.org/2000/svg"; const XLINK_NS: &str = "http://www.w3.org/1999/xlink"; const XML_NAMESPACE_NS: &str = "http://www.w3.org/XML/1998/namespace"; -/// Resolves CSS `light-dark(value1, value2)` function by extracting the first (light-mode) value. +use crate::ColorScheme; + +/// Resolves CSS `light-dark(value1, value2)` function based on the specified color scheme. /// -/// The `light-dark()` CSS function is used for dark mode support. Since usvg doesn't support -/// color scheme preferences, we extract the first value (light-mode color) as the fallback. +/// The `light-dark()` CSS function is used for dark mode support. This function extracts +/// the appropriate value based on the color scheme: first value for light mode, second for dark. /// /// This function handles nested parentheses (e.g., `light-dark(rgb(0, 0, 0), rgb(255, 255, 255))`) /// and recursively resolves any nested `light-dark()` calls. -fn resolve_light_dark(value: &str) -> std::borrow::Cow<'_, str> { +fn resolve_light_dark(value: &str, color_scheme: ColorScheme) -> std::borrow::Cow<'_, str> { use std::borrow::Cow; let Some(start_idx) = value.find("light-dark(") else { @@ -30,9 +32,10 @@ fn resolve_light_dark(value: &str) -> std::borrow::Cow<'_, str> { let func_start = start_idx + "light-dark(".len(); let rest = &value[func_start..]; - // Find the first argument by tracking parentheses depth + // Find both arguments by tracking parentheses depth let mut depth = 1; let mut first_arg_end = None; + let mut second_arg_start = None; let mut func_end = None; for (i, c) in rest.char_indices() { @@ -50,6 +53,7 @@ fn resolve_light_dark(value: &str) -> std::borrow::Cow<'_, str> { } ',' if depth == 1 && first_arg_end.is_none() => { first_arg_end = Some(i); + second_arg_start = Some(i + 1); } _ => {} } @@ -60,19 +64,30 @@ fn resolve_light_dark(value: &str) -> std::borrow::Cow<'_, str> { }; let func_end = func_end.unwrap_or(rest.len()); - let first_arg = rest[..first_arg_end].trim(); + // Select the appropriate argument based on color scheme + let selected_arg = match color_scheme { + ColorScheme::Light => rest[..first_arg_end].trim(), + ColorScheme::Dark => { + if let Some(start) = second_arg_start { + rest[start..func_end].trim() + } else { + // No second argument, fall back to first + rest[..first_arg_end].trim() + } + } + }; - // Reconstruct the value with light-dark() replaced by the first argument + // Reconstruct the value with light-dark() replaced by the selected argument let mut result = String::with_capacity(value.len()); result.push_str(&value[..start_idx]); - result.push_str(first_arg); + result.push_str(selected_arg); // Append any remaining content after the closing parenthesis if func_end + 1 < rest.len() { result.push_str(&rest[func_end + 1..]); } // Recursively resolve any remaining light-dark() calls - match resolve_light_dark(&result) { + match resolve_light_dark(&result, color_scheme) { Cow::Borrowed(_) => Cow::Owned(result), Cow::Owned(s) => Cow::Owned(s), } @@ -83,8 +98,9 @@ impl<'input> Document<'input> { pub fn parse_tree( xml: &roxmltree::Document<'input>, injected_stylesheet: Option<&'input str>, + color_scheme: ColorScheme, ) -> Result, Error> { - parse(xml, injected_stylesheet) + parse(xml, injected_stylesheet, color_scheme) } pub(crate) fn append(&mut self, parent_id: NodeId, kind: NodeKind) -> NodeId { @@ -130,6 +146,7 @@ impl<'input> Document<'input> { fn parse<'input>( xml: &roxmltree::Document<'input>, injected_stylesheet: Option<&'input str>, + color_scheme: ColorScheme, ) -> Result, Error> { let mut doc = Document { nodes: Vec::new(), @@ -166,6 +183,7 @@ fn parse<'input>( 0, &mut doc, &id_map, + color_scheme, )?; // Check that the root element is `svg`. @@ -217,6 +235,7 @@ fn parse_xml_node_children<'input>( depth: u32, doc: &mut Document<'input>, id_map: &HashMap<&str, roxmltree::Node<'_, 'input>>, + color_scheme: ColorScheme, ) -> Result<(), Error> { for node in parent.children() { parse_xml_node( @@ -228,6 +247,7 @@ fn parse_xml_node_children<'input>( depth, doc, id_map, + color_scheme, )?; } @@ -243,6 +263,7 @@ fn parse_xml_node<'input>( depth: u32, doc: &mut Document<'input>, id_map: &HashMap<&str, roxmltree::Node<'_, 'input>>, + color_scheme: ColorScheme, ) -> Result<(), Error> { if depth > 1024 { return Err(Error::NodesLimitReached); @@ -263,11 +284,28 @@ fn parse_xml_node<'input>( tag_name = EId::G; } - let node_id = parse_svg_element(node, parent_id, tag_name, style_sheet, ignore_ids, doc)?; + let node_id = parse_svg_element( + node, + parent_id, + tag_name, + style_sheet, + ignore_ids, + doc, + color_scheme, + )?; if tag_name == EId::Text { - super::text::parse_svg_text_element(node, node_id, style_sheet, doc)?; + super::text::parse_svg_text_element(node, node_id, style_sheet, doc, color_scheme)?; } else if tag_name == EId::Use { - parse_svg_use_element(node, origin, node_id, style_sheet, depth + 1, doc, id_map)?; + parse_svg_use_element( + node, + origin, + node_id, + style_sheet, + depth + 1, + doc, + id_map, + color_scheme, + )?; } else { parse_xml_node_children( node, @@ -278,6 +316,7 @@ fn parse_xml_node<'input>( depth + 1, doc, id_map, + color_scheme, )?; } @@ -291,6 +330,7 @@ pub(crate) fn parse_svg_element<'input>( style_sheet: &simplecss::StyleSheet, ignore_ids: bool, doc: &mut Document<'input>, + color_scheme: ColorScheme, ) -> Result { let attrs_start_idx = doc.attrs.len(); @@ -386,9 +426,9 @@ pub(crate) fn parse_svg_element<'input>( let mut write_declaration = |declaration: &Declaration| { // TODO: perform XML attribute normalization let imp = declaration.important; - // Resolve CSS light-dark() function by extracting the first (light-mode) value. + // Resolve CSS light-dark() function by extracting the appropriate value. // This handles Draw.io SVG exports that use light-dark() for dark mode support. - let val_cow = resolve_light_dark(declaration.value); + let val_cow = resolve_light_dark(declaration.value, color_scheme); let val = val_cow.as_ref(); if declaration.name == "marker" { @@ -619,6 +659,7 @@ fn parse_svg_use_element<'input>( depth: u32, doc: &mut Document<'input>, id_map: &HashMap<&str, roxmltree::Node<'_, 'input>>, + color_scheme: ColorScheme, ) -> Result<(), Error> { let link = match resolve_href(node, id_map) { Some(v) => v, @@ -686,6 +727,7 @@ fn parse_svg_use_element<'input>( depth + 1, doc, id_map, + color_scheme, ) } diff --git a/crates/usvg/src/parser/svgtree/text.rs b/crates/usvg/src/parser/svgtree/text.rs index 11877da3f..6430a7823 100644 --- a/crates/usvg/src/parser/svgtree/text.rs +++ b/crates/usvg/src/parser/svgtree/text.rs @@ -6,6 +6,7 @@ use roxmltree::Error; use super::{AId, Document, EId, NodeId, NodeKind, SvgNode}; +use crate::ColorScheme; const XLINK_NS: &str = "http://www.w3.org/1999/xlink"; @@ -14,6 +15,7 @@ pub(crate) fn parse_svg_text_element<'input>( parent_id: NodeId, style_sheet: &simplecss::StyleSheet, doc: &mut Document<'input>, + color_scheme: ColorScheme, ) -> Result<(), Error> { debug_assert_eq!(parent.tag_name().name(), "text"); @@ -31,7 +33,7 @@ pub(crate) fn parse_svg_text_element<'input>( } }; - parse_svg_text_element_impl(parent, parent_id, style_sheet, space, doc)?; + parse_svg_text_element_impl(parent, parent_id, style_sheet, space, doc, color_scheme)?; trim_text_nodes(parent_id, space, doc); Ok(()) @@ -43,6 +45,7 @@ fn parse_svg_text_element_impl<'input>( style_sheet: &simplecss::StyleSheet, space: XmlSpace, doc: &mut Document<'input>, + color_scheme: ColorScheme, ) -> Result<(), Error> { for node in parent.children() { if node.is_text() { @@ -77,8 +80,15 @@ fn parse_svg_text_element_impl<'input>( is_tref = true; } - let node_id = - super::parse::parse_svg_element(node, parent_id, tag_name, style_sheet, false, doc)?; + let node_id = super::parse::parse_svg_element( + node, + parent_id, + tag_name, + style_sheet, + false, + doc, + color_scheme, + )?; let space = get_xmlspace(doc, node_id, space); if is_tref { @@ -93,7 +103,7 @@ fn parse_svg_text_element_impl<'input>( } } } else { - parse_svg_text_element_impl(node, node_id, style_sheet, space, doc)?; + parse_svg_text_element_impl(node, node_id, style_sheet, space, doc, color_scheme)?; } } diff --git a/crates/usvg/tests/parser.rs b/crates/usvg/tests/parser.rs index ec346522d..39b2d036d 100644 --- a/crates/usvg/tests/parser.rs +++ b/crates/usvg/tests/parser.rs @@ -596,3 +596,64 @@ fn light_dark_css_function_with_rgb() { &usvg::Paint::Color(Color::new_rgb(0, 128, 0)) ); } + +#[test] +fn light_dark_css_function_with_dark_scheme() { + use usvg::Color; + + let svg = r#" + + + + "#; + + let opt = usvg::Options { + color_scheme: usvg::ColorScheme::Dark, + ..Default::default() + }; + let tree = usvg::Tree::from_str(&svg, &opt).unwrap(); + + let usvg::Node::Group(ref group) = &tree.root().children()[0] else { + unreachable!() + }; + let usvg::Node::Path(ref path) = &group.children()[0] else { + unreachable!() + }; + + // With Dark scheme, should extract the second value (blue) + assert_eq!( + path.fill().unwrap().paint(), + &usvg::Paint::Color(Color::new_rgb(0, 0, 255)) + ); +} + +/// Regression test: Tree::from_data_nested should inherit color_scheme from parent Options. +/// This is used when resolving that reference other SVG files. +#[test] +fn nested_svg_inherits_color_scheme() { + use usvg::Color; + + // A nested SVG using light-dark() - this would be embedded via + let nested_svg = br#" + + "#; + + // Test with Dark color scheme + let opt = usvg::Options { + color_scheme: usvg::ColorScheme::Dark, + ..Default::default() + }; + + // from_data_nested should inherit color_scheme from opt + let tree = usvg::Tree::from_data_nested(nested_svg, &opt).unwrap(); + + let usvg::Node::Path(ref path) = &tree.root().children()[0] else { + unreachable!() + }; + + // With Dark scheme inherited, should extract blue (the second value) + assert_eq!( + path.fill().unwrap().paint(), + &usvg::Paint::Color(Color::new_rgb(0, 0, 255)) + ); +} From 5437127a6c46d4a815ff5b3a53e2c0ceeab69adb Mon Sep 17 00:00:00 2001 From: Rocky Shi Date: Sun, 21 Dec 2025 03:22:38 +1300 Subject: [PATCH 3/3] chore: Add TODO comments for future `svgtypes` color scheme and `light-dark` resolution integration. --- crates/usvg/src/parser/options.rs | 1 + crates/usvg/src/parser/svgtree/parse.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/usvg/src/parser/options.rs b/crates/usvg/src/parser/options.rs index 5eff957f4..918e63aec 100644 --- a/crates/usvg/src/parser/options.rs +++ b/crates/usvg/src/parser/options.rs @@ -8,6 +8,7 @@ use std::sync::Arc; use crate::FontResolver; use crate::{ImageHrefResolver, ImageRendering, ShapeRendering, Size, TextRendering}; +// TODO: Use svgtypes::ColorScheme once https://github.com/linebender/svgtypes/pull/59 is merged /// The color scheme preference for resolving CSS `light-dark()` function. /// /// The CSS `light-dark()` function allows specifying two color values where the first diff --git a/crates/usvg/src/parser/svgtree/parse.rs b/crates/usvg/src/parser/svgtree/parse.rs index 51d33a012..89a5dc81a 100644 --- a/crates/usvg/src/parser/svgtree/parse.rs +++ b/crates/usvg/src/parser/svgtree/parse.rs @@ -15,6 +15,7 @@ const XML_NAMESPACE_NS: &str = "http://www.w3.org/XML/1998/namespace"; use crate::ColorScheme; +// TODO: Use svgtypes::resolve_light_dark once https://github.com/linebender/svgtypes/pull/59 is merged /// Resolves CSS `light-dark(value1, value2)` function based on the specified color scheme. /// /// The `light-dark()` CSS function is used for dark mode support. This function extracts