<commit_before>//! SVG Elements.

use locale_config::{LanguageRange, Locale};
use markup5ever::{expanded_name, local_name, namespace_url, ns, QualName};
use matches::matches;
use once_cell::sync::Lazy;
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::ops::Deref;

use crate::bbox::BoundingBox;
use crate::cond::{RequiredExtensions, RequiredFeatures, SystemLanguage};
use crate::css::{Declaration, Origin};
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::error::*;
use crate::filter::Filter;
use crate::filters::{
    blend::FeBlend,
    color_matrix::FeColorMatrix,
    component_transfer::{FeComponentTransfer, FeFuncA, FeFuncB, FeFuncG, FeFuncR},
    composite::FeComposite,
    convolve_matrix::FeConvolveMatrix,
    displacement_map::FeDisplacementMap,
    flood::FeFlood,
    gaussian_blur::FeGaussianBlur,
    image::FeImage,
    lighting::{FeDiffuseLighting, FeDistantLight, FePointLight, FeSpecularLighting, FeSpotLight},
    merge::{FeMerge, FeMergeNode},
    morphology::FeMorphology,
    offset::FeOffset,
    tile::FeTile,
    turbulence::FeTurbulence,
    FilterEffect,
};
use crate::gradient::{LinearGradient, RadialGradient, Stop};
use crate::image::Image;
use crate::marker::Marker;
use crate::node::*;
use crate::parsers::Parse;
use crate::pattern::Pattern;
use crate::properties::{ComputedValues, SpecifiedValues};
use crate::property_bag::PropertyBag;
use crate::shapes::{Circle, Ellipse, Line, Path, Polygon, Polyline, Rect};
use crate::structure::{ClipPath, Group, Link, Mask, NonRendering, Svg, Switch, Symbol, Use};
use crate::style::Style;
use crate::text::{TRef, TSpan, Text};
use crate::transform::Transform;

// After creating/parsing a Element, it will be in a success or an error state.
// We represent this with a Result, aliased as a ElementResult.  There is no
// extra information for the Ok case; all the interesting stuff is in the
// Err case.
//
// https://www.w3.org/TR/SVG/implnote.html#ErrorProcessing
//
// When an element has an error during parsing, the SVG spec calls the element
// to be "in error".  We skip rendering of elements that are in error.
//
// When we parse an element's attributes, we stop as soon as we
// encounter the first error:  a parse error, or an invalid value,
// etc.  No further attributes will be processed, although note that
// the order in which an element's attributes are processed is not
// defined.
//
// Alternatively, we could try to parse/validate all the attributes
// that come in an element and build up a Vec<ElementError>.  However, we
// don't do this now.  Doing that may be more useful for an SVG
// validator, not a renderer like librsvg is.
pub type ElementResult = Result<(), ElementError>;

pub trait SetAttributes {
    /// Sets per-element attributes from the `pbag`
    ///
    /// Each element is supposed to iterate the `pbag`, and parse any attributes it needs.
    fn set_attributes(&mut self, _pbag: &PropertyBag<'_>) -> ElementResult {
        Ok(())
    }
}

pub trait Draw {
    /// Draw an element
    ///
    /// Each element is supposed to draw itself as needed.
    fn draw(
        &self,
        _node: &Node,
        _acquired_nodes: &mut AcquiredNodes,
        _cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        _clipping: bool,
    ) -> Result<BoundingBox, RenderingError> {
        // by default elements don't draw themselves
        Ok(draw_ctx.empty_bbox())
    }
}

pub struct ElementInner<T: SetAttributes + Draw> {
    element_name: QualName,
    id: Option<String>,    // id attribute from XML element
    class: Option<String>, // class attribute from XML element
    specified_values: SpecifiedValues,
    important_styles: HashSet<QualName>,
    result: ElementResult,
    transform: Transform,
    values: ComputedValues,
    cond: bool,
    style_attr: String,
    pub element_impl: T,
}

impl<T: SetAttributes + Draw> ElementInner<T> {
    fn element_name(&self) -> &QualName {
        &self.element_name
    }

    fn get_id(&self) -> Option<&str> {
        self.id.as_ref().map(String::as_str)
    }

    fn get_class(&self) -> Option<&str> {
        self.class.as_ref().map(String::as_str)
    }

    fn get_specified_values(&self) -> &SpecifiedValues {
        &self.specified_values
    }

    fn get_computed_values(&self) -> &ComputedValues {
        &self.values
    }

    fn set_computed_values(&mut self, values: &ComputedValues) {
        self.values = values.clone();
    }

    fn get_cond(&self) -> bool {
        self.cond
    }

    fn get_transform(&self) -> Transform {
        self.transform
    }

    fn save_style_attribute(&mut self, pbag: &PropertyBag<'_>) {
        for (attr, value) in pbag.iter() {
            match attr.expanded() {
                expanded_name!("", "style") => self.style_attr.push_str(value),
                _ => (),
            }
        }
    }

    fn set_transform_attribute(&mut self, pbag: &PropertyBag<'_>) -> Result<(), ElementError> {
        for (attr, value) in pbag.iter() {
            match attr.expanded() {
                expanded_name!("", "transform") => {
                    return Transform::parse_str(value)
                        .attribute(attr)
                        .and_then(|affine| {
                            self.transform = affine;
                            Ok(())
                        });
                }
                _ => (),
            }
        }

        Ok(())
    }

    fn set_conditional_processing_attributes(
        &mut self,
        pbag: &PropertyBag<'_>,
    ) -> Result<(), ElementError> {
        let mut cond = self.cond;
        let locale = locale_from_environment();

        for (attr, value) in pbag.iter() {
            let mut parse = || -> Result<_, ValueErrorKind> {
                match attr.expanded() {
                    expanded_name!("", "requiredExtensions") if cond => {
                        cond = RequiredExtensions::from_attribute(value)
                            .map(|RequiredExtensions(res)| res)?;
                    }

                    expanded_name!("", "requiredFeatures") if cond => {
                        cond = RequiredFeatures::from_attribute(value)
                            .map(|RequiredFeatures(res)| res)?;
                    }

                    expanded_name!("", "systemLanguage") if cond => {
                        cond = SystemLanguage::from_attribute(value, &locale)
                            .map(|SystemLanguage(res)| res)?;
                    }

                    _ => {}
                }

                Ok(cond)
            };

            parse().map(|c| self.cond = c).attribute(attr)?;
        }

        Ok(())
    }

    /// Hands the pbag to the node's state, to apply the presentation attributes
    fn set_presentation_attributes(&mut self, pbag: &PropertyBag<'_>) -> Result<(), ElementError> {
        match self.specified_values.parse_presentation_attributes(pbag) {
            Ok(_) => Ok(()),
            Err(e) => {
                // FIXME: we'll ignore errors here for now.
                //
                // If we set the node to be in error, we expose buggy handling of the
                // enable-background property; we are not parsing it correctly. This<commit_msg>(#594): Only compute locale info for elements that need it.<commit_after>//! SVG Elements.

use locale_config::{LanguageRange, Locale};
use markup5ever::{expanded_name, local_name, namespace_url, ns, QualName};
use matches::matches;
use once_cell::sync::Lazy;
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::ops::Deref;

use crate::bbox::BoundingBox;
use crate::cond::{RequiredExtensions, RequiredFeatures, SystemLanguage};
use crate::css::{Declaration, Origin};
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::error::*;
use crate::filter::Filter;
use crate::filters::{
    blend::FeBlend,
    color_matrix::FeColorMatrix,
    component_transfer::{FeComponentTransfer, FeFuncA, FeFuncB, FeFuncG, FeFuncR},
    composite::FeComposite,
    convolve_matrix::FeConvolveMatrix,
    displacement_map::FeDisplacementMap,
    flood::FeFlood,
    gaussian_blur::FeGaussianBlur,
    image::FeImage,
    lighting::{FeDiffuseLighting, FeDistantLight, FePointLight, FeSpecularLighting, FeSpotLight},
    merge::{FeMerge, FeMergeNode},
    morphology::FeMorphology,
    offset::FeOffset,
    tile::FeTile,
    turbulence::FeTurbulence,
    FilterEffect,
};
use crate::gradient::{LinearGradient, RadialGradient, Stop};
use crate::image::Image;
use crate::marker::Marker;
use crate::node::*;
use crate::parsers::Parse;
use crate::pattern::Pattern;
use crate::properties::{ComputedValues, SpecifiedValues};
use crate::property_bag::PropertyBag;
use crate::shapes::{Circle, Ellipse, Line, Path, Polygon, Polyline, Rect};
use crate::structure::{ClipPath, Group, Link, Mask, NonRendering, Svg, Switch, Symbol, Use};
use crate::style::Style;
use crate::text::{TRef, TSpan, Text};
use crate::transform::Transform;

// After creating/parsing a Element, it will be in a success or an error state.
// We represent this with a Result, aliased as a ElementResult.  There is no
// extra information for the Ok case; all the interesting stuff is in the
// Err case.
//
// https://www.w3.org/TR/SVG/implnote.html#ErrorProcessing
//
// When an element has an error during parsing, the SVG spec calls the element
// to be "in error".  We skip rendering of elements that are in error.
//
// When we parse an element's attributes, we stop as soon as we
// encounter the first error:  a parse error, or an invalid value,
// etc.  No further attributes will be processed, although note that
// the order in which an element's attributes are processed is not
// defined.
//
// Alternatively, we could try to parse/validate all the attributes
// that come in an element and build up a Vec<ElementError>.  However, we
// don't do this now.  Doing that may be more useful for an SVG
// validator, not a renderer like librsvg is.
pub type ElementResult = Result<(), ElementError>;

pub trait SetAttributes {
    /// Sets per-element attributes from the `pbag`
    ///
    /// Each element is supposed to iterate the `pbag`, and parse any attributes it needs.
    fn set_attributes(&mut self, _pbag: &PropertyBag<'_>) -> ElementResult {
        Ok(())
    }
}

pub trait Draw {
    /// Draw an element
    ///
    /// Each element is supposed to draw itself as needed.
    fn draw(
        &self,
        _node: &Node,
        _acquired_nodes: &mut AcquiredNodes,
        _cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        _clipping: bool,
    ) -> Result<BoundingBox, RenderingError> {
        // by default elements don't draw themselves
        Ok(draw_ctx.empty_bbox())
    }
}

pub struct ElementInner<T: SetAttributes + Draw> {
    element_name: QualName,
    id: Option<String>,    // id attribute from XML element
    class: Option<String>, // class attribute from XML element
    specified_values: SpecifiedValues,
    important_styles: HashSet<QualName>,
    result: ElementResult,
    transform: Transform,
    values: ComputedValues,
    cond: bool,
    style_attr: String,
    pub element_impl: T,
}

impl<T: SetAttributes + Draw> ElementInner<T> {
    fn element_name(&self) -> &QualName {
        &self.element_name
    }

    fn get_id(&self) -> Option<&str> {
        self.id.as_ref().map(String::as_str)
    }

    fn get_class(&self) -> Option<&str> {
        self.class.as_ref().map(String::as_str)
    }

    fn get_specified_values(&self) -> &SpecifiedValues {
        &self.specified_values
    }

    fn get_computed_values(&self) -> &ComputedValues {
        &self.values
    }

    fn set_computed_values(&mut self, values: &ComputedValues) {
        self.values = values.clone();
    }

    fn get_cond(&self) -> bool {
        self.cond
    }

    fn get_transform(&self) -> Transform {
        self.transform
    }

    fn save_style_attribute(&mut self, pbag: &PropertyBag<'_>) {
        for (attr, value) in pbag.iter() {
            match attr.expanded() {
                expanded_name!("", "style") => self.style_attr.push_str(value),
                _ => (),
            }
        }
    }

    fn set_transform_attribute(&mut self, pbag: &PropertyBag<'_>) -> Result<(), ElementError> {
        for (attr, value) in pbag.iter() {
            match attr.expanded() {
                expanded_name!("", "transform") => {
                    return Transform::parse_str(value)
                        .attribute(attr)
                        .and_then(|affine| {
                            self.transform = affine;
                            Ok(())
                        });
                }
                _ => (),
            }
        }

        Ok(())
    }

    fn set_conditional_processing_attributes(
        &mut self,
        pbag: &PropertyBag<'_>,
    ) -> Result<(), ElementError> {
        let mut cond = self.cond;

        for (attr, value) in pbag.iter() {
            let mut parse = || -> Result<_, ValueErrorKind> {
                match attr.expanded() {
                    expanded_name!("", "requiredExtensions") if cond => {
                        cond = RequiredExtensions::from_attribute(value)
                            .map(|RequiredExtensions(res)| res)?;
                    }

                    expanded_name!("", "requiredFeatures") if cond => {
                        cond = RequiredFeatures::from_attribute(value)
                            .map(|RequiredFeatures(res)| res)?;
                    }

                    expanded_name!("", "systemLanguage") if cond => {
                        // In reality this could be computed only once, at the beginning
                        // of loading... maybe if we end up having a LoadContext again.
                        //
                        // (This call has a lot of memory churn internally with many
                        // short-lived allocations.)
                        let locale = locale_from_environment();

                        cond = SystemLanguage::from_attribute(value, &locale)
                            .map(|SystemLanguage(res)| res)?;
                    }

                    _ => {}
                }

                Ok(cond)
            };

            parse().map(|c| self.cond = c).attribute(attr)?;
        }

        Ok(())
    }

    /// Hands the pbag to the node's state, to apply the presentation attributes
    fn set_presentation_attributes(&mut self, pbag: &PropertyBag<'_>) -> Result<(), ElementError> {
        match self.specified_values.parse_presentation_attributes(pbag) {
            Ok(_) => Ok(()),
            Err(e) => {
                // FIXME: we'll ignore errors here for now.
                //
                // If we set the node to be in error, we expose buggy handling of the
                // enable-background property; we are not parsing it correctly. This
{'fraction_non_alphanumeric': 0.12096935709993992, 'fraction_numerical': 0.00046732091594899527, 'mean_word_length': 2.3557347670250897, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 91, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7195008', 'n_tokens_mistral': 4285, 'n_tokens_neox': 4188, 'n_words': 1183}