summaryrefslogtreecommitdiff
path: root/rsvg/src/cond.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rsvg/src/cond.rs')
-rw-r--r--rsvg/src/cond.rs243
1 files changed, 243 insertions, 0 deletions
diff --git a/rsvg/src/cond.rs b/rsvg/src/cond.rs
new file mode 100644
index 00000000..57e4817c
--- /dev/null
+++ b/rsvg/src/cond.rs
@@ -0,0 +1,243 @@
+//! Conditional processing attributes: `requiredExtensions`, `requiredFeatures`, `systemLanguage`.
+
+#[allow(unused_imports, deprecated)]
+use std::ascii::AsciiExt;
+
+use std::str::FromStr;
+
+use language_tags::LanguageTag;
+
+use crate::accept_language::{LanguageTags, UserLanguage};
+use crate::error::*;
+use crate::session::Session;
+
+// No extensions at the moment.
+static IMPLEMENTED_EXTENSIONS: &[&str] = &[];
+
+#[derive(Debug, PartialEq)]
+pub struct RequiredExtensions(pub bool);
+
+impl RequiredExtensions {
+ /// Parse a requiredExtensions attribute.
+ ///
+ /// <http://www.w3.org/TR/SVG/struct.html#RequiredExtensionsAttribute>
+ pub fn from_attribute(s: &str) -> RequiredExtensions {
+ RequiredExtensions(
+ s.split_whitespace()
+ .all(|f| IMPLEMENTED_EXTENSIONS.binary_search(&f).is_ok()),
+ )
+ }
+
+ /// Evaluate a requiredExtensions value for conditional processing.
+ pub fn eval(&self) -> bool {
+ self.0
+ }
+}
+
+// Keep these sorted alphabetically for binary_search.
+static IMPLEMENTED_FEATURES: &[&str] = &[
+ "http://www.w3.org/TR/SVG11/feature#BasicFilter",
+ "http://www.w3.org/TR/SVG11/feature#BasicGraphicsAttribute",
+ "http://www.w3.org/TR/SVG11/feature#BasicPaintAttribute",
+ "http://www.w3.org/TR/SVG11/feature#BasicStructure",
+ "http://www.w3.org/TR/SVG11/feature#BasicText",
+ "http://www.w3.org/TR/SVG11/feature#ConditionalProcessing",
+ "http://www.w3.org/TR/SVG11/feature#ContainerAttribute",
+ "http://www.w3.org/TR/SVG11/feature#Filter",
+ "http://www.w3.org/TR/SVG11/feature#Gradient",
+ "http://www.w3.org/TR/SVG11/feature#Image",
+ "http://www.w3.org/TR/SVG11/feature#Marker",
+ "http://www.w3.org/TR/SVG11/feature#Mask",
+ "http://www.w3.org/TR/SVG11/feature#OpacityAttribute",
+ "http://www.w3.org/TR/SVG11/feature#Pattern",
+ "http://www.w3.org/TR/SVG11/feature#SVG",
+ "http://www.w3.org/TR/SVG11/feature#SVG-static",
+ "http://www.w3.org/TR/SVG11/feature#Shape",
+ "http://www.w3.org/TR/SVG11/feature#Structure",
+ "http://www.w3.org/TR/SVG11/feature#Style",
+ "http://www.w3.org/TR/SVG11/feature#View",
+ "org.w3c.svg.static", // deprecated SVG 1.0 feature string
+];
+
+#[derive(Debug, PartialEq)]
+pub struct RequiredFeatures(pub bool);
+
+impl RequiredFeatures {
+ // Parse a requiredFeatures attribute
+ // http://www.w3.org/TR/SVG/struct.html#RequiredFeaturesAttribute
+ pub fn from_attribute(s: &str) -> RequiredFeatures {
+ RequiredFeatures(
+ s.split_whitespace()
+ .all(|f| IMPLEMENTED_FEATURES.binary_search(&f).is_ok()),
+ )
+ }
+
+ /// Evaluate a requiredFeatures value for conditional processing.
+ pub fn eval(&self) -> bool {
+ self.0
+ }
+}
+
+/// The systemLanguage attribute inside `<cond>` element's children.
+///
+/// Parsing the value of a `systemLanguage` attribute may fail if the document supplies
+/// invalid BCP47 language tags. In that case, we store an `Invalid` variant.
+///
+/// That variant is used later, during [`SystemLanguage::eval`], to see whether the
+/// `<cond>` should match or not.
+#[derive(Debug, PartialEq)]
+pub enum SystemLanguage {
+ Valid(LanguageTags),
+ Invalid,
+}
+
+impl SystemLanguage {
+ /// Parse a `systemLanguage` attribute and match it against a given `Locale`
+ ///
+ /// The [`systemLanguage`] conditional attribute is a
+ /// comma-separated list of [BCP47] Language Tags. This function
+ /// parses the attribute and matches the result against a given
+ /// `locale`. If there is a match, i.e. if the given locale
+ /// supports one of the languages listed in the `systemLanguage`
+ /// attribute, then the `SystemLanguage.0` will be `true`;
+ /// otherwise it will be `false`.
+ ///
+ /// Normally, calling code will pass `&Locale::current()` for the
+ /// `locale` attribute; this is the user's current locale.
+ ///
+ /// [`systemLanguage`]: https://www.w3.org/TR/SVG/struct.html#ConditionalProcessingSystemLanguageAttribute
+ /// [BCP47]: http://www.ietf.org/rfc/bcp/bcp47.txt
+ pub fn from_attribute(s: &str, session: &Session) -> SystemLanguage {
+ let attribute_tags = s
+ .split(',')
+ .map(str::trim)
+ .map(|s| {
+ LanguageTag::from_str(s).map_err(|e| {
+ ValueErrorKind::parse_error(&format!("invalid language tag \"{s}\": {e}"))
+ })
+ })
+ .collect::<Result<Vec<LanguageTag>, _>>();
+
+ match attribute_tags {
+ Ok(tags) => SystemLanguage::Valid(LanguageTags::from(tags)),
+
+ Err(e) => {
+ rsvg_log!(
+ session,
+ "ignoring systemLanguage attribute with invalid value: {}",
+ e
+ );
+ SystemLanguage::Invalid
+ }
+ }
+ }
+
+ /// Evaluate a systemLanguage value for conditional processing.
+ pub fn eval(&self, user_language: &UserLanguage) -> bool {
+ match *self {
+ SystemLanguage::Valid(ref tags) => user_language.any_matches(tags),
+ SystemLanguage::Invalid => false,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use locale_config::Locale;
+
+ #[test]
+ fn required_extensions() {
+ assert_eq!(
+ RequiredExtensions::from_attribute("http://test.org/NotExisting/1.0"),
+ RequiredExtensions(false)
+ );
+ }
+
+ #[test]
+ fn required_features() {
+ assert_eq!(
+ RequiredFeatures::from_attribute("http://www.w3.org/TR/SVG11/feature#NotExisting"),
+ RequiredFeatures(false)
+ );
+
+ assert_eq!(
+ RequiredFeatures::from_attribute("http://www.w3.org/TR/SVG11/feature#BasicFilter"),
+ RequiredFeatures(true)
+ );
+
+ assert_eq!(
+ RequiredFeatures::from_attribute(
+ "http://www.w3.org/TR/SVG11/feature#BasicFilter \
+ http://www.w3.org/TR/SVG11/feature#NotExisting",
+ ),
+ RequiredFeatures(false)
+ );
+
+ assert_eq!(
+ RequiredFeatures::from_attribute(
+ "http://www.w3.org/TR/SVG11/feature#BasicFilter \
+ http://www.w3.org/TR/SVG11/feature#BasicText",
+ ),
+ RequiredFeatures(true)
+ );
+ }
+
+ #[test]
+ fn system_language() {
+ let session = Session::new_for_test_suite();
+
+ let locale = Locale::new("de,en-US").unwrap();
+ let user_language = UserLanguage::LanguageTags(LanguageTags::from_locale(&locale).unwrap());
+
+ assert!(matches!(
+ SystemLanguage::from_attribute("", &session),
+ SystemLanguage::Invalid
+ ));
+
+ assert!(matches!(
+ SystemLanguage::from_attribute("12345", &session),
+ SystemLanguage::Invalid
+ ));
+
+ assert_eq!(
+ SystemLanguage::from_attribute("fr", &session).eval(&user_language),
+ false
+ );
+
+ assert_eq!(
+ SystemLanguage::from_attribute("en", &session).eval(&user_language),
+ false
+ );
+
+ assert_eq!(
+ SystemLanguage::from_attribute("de", &session).eval(&user_language),
+ true
+ );
+
+ assert_eq!(
+ SystemLanguage::from_attribute("en-US", &session).eval(&user_language),
+ true
+ );
+
+ assert_eq!(
+ SystemLanguage::from_attribute("en-GB", &session).eval(&user_language),
+ false
+ );
+
+ assert_eq!(
+ SystemLanguage::from_attribute("DE", &session).eval(&user_language),
+ true
+ );
+
+ assert_eq!(
+ SystemLanguage::from_attribute("de-LU", &session).eval(&user_language),
+ true
+ );
+
+ assert_eq!(
+ SystemLanguage::from_attribute("fr, de", &session).eval(&user_language),
+ true
+ );
+ }
+}