use super::{
colors::Color,
simpletypes::{
BlendMode, BlipCompression, Coordinate, EffectContainerType, FixedAngle, FixedPercentage, LineEndLength,
LineEndType, LineEndWidth, PathShadeType, Percentage, PositiveCoordinate, PositiveFixedAngle,
PositiveFixedPercentage, PositivePercentage, PresetLineDashVal, PresetPatternVal, PresetShadowVal,
RectAlignment, TileFlipMode,
},
};
use crate::{
error::{LimitViolationError, MaxOccurs, MissingAttributeError, MissingChildNodeError, NotGroupMemberError},
shared::relationship::RelationshipId,
xml::{parse_xml_bool, XmlNode},
xsdtypes::{XsdChoice, XsdType},
};
use log::trace;
use std::error::Error;
pub type Result<T> = ::std::result::Result<T, Box<dyn Error>>;
#[derive(Default, Debug, Clone, PartialEq)]
pub struct RelativeRect {
pub left: Option<Percentage>,
pub top: Option<Percentage>,
pub right: Option<Percentage>,
pub bottom: Option<Percentage>,
}
impl RelativeRect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<RelativeRect> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_str() {
"l" => instance.left = Some(value.parse()?),
"t" => instance.top = Some(value.parse()?),
"r" => instance.right = Some(value.parse()?),
"b" => instance.bottom = Some(value.parse()?),
_ => (),
}
Ok(instance)
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AlphaBiLevelEffect {
pub threshold: PositiveFixedPercentage,
}
impl AlphaBiLevelEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<AlphaBiLevelEffect> {
let threshold = xml_node
.attributes
.get("thresh")
.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "thresh"))?
.parse()?;
Ok(Self { threshold })
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct AlphaInverseEffect {
pub color: Option<Color>,
}
impl AlphaInverseEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<AlphaInverseEffect> {
let color = xml_node
.child_nodes
.iter()
.find_map(Color::try_from_xml_element)
.transpose()?;
Ok(Self { color })
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AlphaModulateEffect {
pub container: EffectContainer,
}
impl AlphaModulateEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<AlphaModulateEffect> {
let container = xml_node
.child_nodes
.iter()
.find(|child_node| child_node.local_name() == "cont")
.ok_or_else(|| Box::<dyn Error>::from(MissingChildNodeError::new(xml_node.name.clone(), "container")))
.and_then(EffectContainer::from_xml_element)?;
Ok(Self { container })
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct AlphaModulateFixedEffect {
pub amount: Option<PositivePercentage>,
}
impl AlphaModulateFixedEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let amount = xml_node.attributes.get("amt").map(|value| value.parse()).transpose()?;
Ok(Self { amount })
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct AlphaOutsetEffect {
pub radius: Option<Coordinate>,
}
impl AlphaOutsetEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let radius = xml_node.attributes.get("rad").map(|value| value.parse()).transpose()?;
Ok(Self { radius })
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AlphaReplaceEffect {
pub alpha: PositiveFixedPercentage,
}
impl AlphaReplaceEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let alpha = xml_node
.attributes
.get("a")
.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "a"))?
.parse()?;
Ok(Self { alpha })
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BiLevelEffect {
pub threshold: PositiveFixedPercentage,
}
impl BiLevelEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let threshold = xml_node
.attributes
.get("thresh")
.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "thresh"))?
.parse()?;
Ok(Self { threshold })
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BlendEffect {
pub blend: BlendMode,
pub container: EffectContainer,
}
impl BlendEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let blend = xml_node
.attributes
.get("blend")
.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "blend"))?
.parse()?;
let container = xml_node
.child_nodes
.iter()
.find(|child_node| child_node.local_name() == "cont")
.ok_or_else(|| Box::<dyn Error>::from(MissingChildNodeError::new(xml_node.name.clone(), "cont")))
.and_then(EffectContainer::from_xml_element)?;
Ok(Self { blend, container })
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct BlurEffect {
pub radius: Option<PositiveCoordinate>,
pub grow: Option<bool>,
}
impl BlurEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_ref() {
"rad" => instance.radius = Some(value.parse()?),
"grow" => instance.grow = Some(parse_xml_bool(value)?),
_ => (),
}
Ok(instance)
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ColorChangeEffect {
pub use_alpha: Option<bool>,
pub color_from: Color,
pub color_to: Color,
}
impl ColorChangeEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let use_alpha = xml_node.attributes.get("useA").map(|value| value.parse()).transpose()?;
let mut color_from = None;
let mut color_to = None;
for child_node in &xml_node.child_nodes {
match child_node.local_name() {
"clrFrom" => {
color_from = child_node
.child_nodes
.iter()
.find_map(Color::try_from_xml_element)
.transpose()?
}
"clrTo" => {
color_to = child_node
.child_nodes
.iter()
.find_map(Color::try_from_xml_element)
.transpose()?
}
_ => (),
}
}
let color_from = color_from.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "clrFrom"))?;
let color_to = color_to.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "clrTo"))?;
Ok(Self {
use_alpha,
color_from,
color_to,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ColorReplaceEffect {
pub color: Color,
}
impl ColorReplaceEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let color = xml_node
.child_nodes
.iter()
.find_map(Color::try_from_xml_element)
.transpose()?
.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "EG_Color"))?;
Ok(Self { color })
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct LuminanceEffect {
pub brightness: Option<FixedPercentage>,
pub contrast: Option<FixedPercentage>,
}
impl LuminanceEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_ref() {
"bright" => instance.brightness = Some(value.parse()?),
"contrast" => instance.contrast = Some(value.parse()?),
_ => (),
}
Ok(instance)
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DuotoneEffect {
pub colors: [Color; 2],
}
impl DuotoneEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let mut iterator = xml_node.child_nodes.iter().filter_map(Color::try_from_xml_element);
let color1 = iterator
.next()
.transpose()?
.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "EG_Color"))?;
let color2 = iterator
.next()
.transpose()?
.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "EG_Color"))?;
Ok(Self {
colors: [color1, color2],
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct FillEffect {
pub fill_properties: FillProperties,
}
impl FillEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let fill_properties = xml_node
.child_nodes
.iter()
.find_map(FillProperties::try_from_xml_element)
.transpose()?
.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "EG_FillProperties"))?;
Ok(Self { fill_properties })
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct FillOverlayEffect {
pub blend_mode: BlendMode,
pub fill: FillProperties,
}
impl FillOverlayEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let blend_mode = xml_node
.attributes
.get("blend")
.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "blend"))?
.parse()?;
let fill = xml_node
.child_nodes
.iter()
.find_map(FillProperties::try_from_xml_element)
.transpose()?
.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "EG_FillProperties"))?;
Ok(Self { blend_mode, fill })
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct GlowEffect {
pub radius: Option<PositiveCoordinate>,
pub color: Color,
}
impl GlowEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let radius = xml_node.attributes.get("rad").map(|value| value.parse()).transpose()?;
let color = xml_node
.child_nodes
.iter()
.find_map(Color::try_from_xml_element)
.transpose()?
.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "EG_ColorChoice"))?;
Ok(Self { radius, color })
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct HslEffect {
pub hue: Option<PositiveFixedAngle>,
pub saturation: Option<FixedPercentage>,
pub luminance: Option<FixedPercentage>,
}
impl HslEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_ref() {
"hue" => instance.hue = Some(value.parse()?),
"sat" => instance.saturation = Some(value.parse()?),
"lum" => instance.luminance = Some(value.parse()?),
_ => (),
}
Ok(instance)
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct InnerShadowEffect {
pub blur_radius: Option<PositiveCoordinate>,
pub distance: Option<PositiveCoordinate>,
pub direction: Option<PositiveFixedAngle>,
pub color: Color,
}
impl InnerShadowEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let color = xml_node
.child_nodes
.iter()
.find_map(Color::try_from_xml_element)
.transpose()?
.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "EG_ColorChoice"))?;
let mut blur_radius = None;
let mut distance = None;
let mut direction = None;
for (attr, value) in &xml_node.attributes {
match attr.as_str() {
"blurRad" => blur_radius = Some(value.parse()?),
"dist" => distance = Some(value.parse()?),
"dir" => direction = Some(value.parse()?),
_ => (),
}
}
Ok(Self {
blur_radius,
distance,
direction,
color,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct OuterShadowEffect {
pub blur_radius: Option<PositiveCoordinate>,
pub distance: Option<PositiveCoordinate>,
pub direction: Option<PositiveFixedAngle>,
pub scale_x: Option<Percentage>,
pub scale_y: Option<Percentage>,
pub skew_x: Option<FixedAngle>,
pub skew_y: Option<FixedAngle>,
pub alignment: Option<RectAlignment>,
pub rotate_with_shape: Option<bool>,
pub color: Color,
}
impl OuterShadowEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let color = xml_node
.child_nodes
.iter()
.find_map(Color::try_from_xml_element)
.transpose()?
.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "EG_ColorChoice"))?;
let mut blur_radius = None;
let mut distance = None;
let mut direction = None;
let mut scale_x = None;
let mut scale_y = None;
let mut skew_x = None;
let mut skew_y = None;
let mut alignment = None;
let mut rotate_with_shape = None;
for (attr, value) in &xml_node.attributes {
match attr.as_str() {
"blurRad" => blur_radius = Some(value.parse()?),
"dist" => distance = Some(value.parse()?),
"dir" => direction = Some(value.parse()?),
"sx" => scale_x = Some(value.parse()?),
"sy" => scale_y = Some(value.parse()?),
"kx" => skew_x = Some(value.parse()?),
"ky" => skew_y = Some(value.parse()?),
"algn" => alignment = Some(value.parse()?),
"rotWithShape" => rotate_with_shape = Some(parse_xml_bool(value)?),
_ => (),
}
}
Ok(Self {
blur_radius,
distance,
direction,
scale_x,
scale_y,
skew_x,
skew_y,
alignment,
rotate_with_shape,
color,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PresetShadowEffect {
pub preset: PresetShadowVal,
pub distance: Option<PositiveCoordinate>,
pub direction: Option<PositiveFixedAngle>,
pub color: Color,
}
impl PresetShadowEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let color = xml_node
.child_nodes
.iter()
.find_map(Color::try_from_xml_element)
.transpose()?
.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "EG_ColorChoice"))?;
let mut preset = None;
let mut distance = None;
let mut direction = None;
for (attr, value) in &xml_node.attributes {
match attr.as_str() {
"prst" => preset = Some(value.parse()?),
"dist" => distance = Some(value.parse()?),
"dir" => direction = Some(value.parse()?),
_ => (),
}
}
let preset = preset.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "prst"))?;
Ok(Self {
preset,
distance,
direction,
color,
})
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct ReflectionEffect {
pub blur_radius: Option<PositiveCoordinate>,
pub start_opacity: Option<PositiveFixedPercentage>,
pub start_position: Option<PositiveFixedPercentage>,
pub end_opacity: Option<PositiveFixedPercentage>,
pub end_position: Option<PositiveFixedPercentage>,
pub distance: Option<PositiveCoordinate>,
pub direction: Option<PositiveFixedAngle>,
pub fade_direction: Option<PositiveFixedAngle>,
pub scale_x: Option<Percentage>,
pub scale_y: Option<Percentage>,
pub skew_x: Option<FixedAngle>,
pub skew_y: Option<FixedAngle>,
pub alignment: Option<RectAlignment>,
pub rotate_with_shape: Option<bool>,
}
impl ReflectionEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_ref() {
"blurRad" => instance.blur_radius = Some(value.parse()?),
"stA" => instance.start_opacity = Some(value.parse()?),
"stPos" => instance.start_position = Some(value.parse()?),
"endA" => instance.end_opacity = Some(value.parse()?),
"endPos" => instance.end_position = Some(value.parse()?),
"dist" => instance.distance = Some(value.parse()?),
"dir" => instance.direction = Some(value.parse()?),
"fadeDir" => instance.fade_direction = Some(value.parse()?),
"sx" => instance.scale_x = Some(value.parse()?),
"sy" => instance.scale_y = Some(value.parse()?),
"kx" => instance.skew_x = Some(value.parse()?),
"ky" => instance.skew_y = Some(value.parse()?),
"algn" => instance.alignment = Some(value.parse()?),
"rotWithShape" => instance.rotate_with_shape = Some(value.parse()?),
_ => (),
}
Ok(instance)
})
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct RelativeOffsetEffect {
pub translate_x: Option<Percentage>,
pub translate_y: Option<Percentage>,
}
impl RelativeOffsetEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_str() {
"tx" => instance.translate_x = Some(value.parse()?),
"ty" => instance.translate_y = Some(value.parse()?),
_ => (),
}
Ok(instance)
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SoftEdgesEffect {
pub radius: PositiveCoordinate,
}
impl SoftEdgesEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let radius = xml_node
.attributes
.get("rad")
.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "rad"))?
.parse()?;
Ok(Self { radius })
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct TintEffect {
pub hue: Option<PositiveFixedAngle>,
pub amount: Option<FixedPercentage>,
}
impl TintEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_str() {
"hue" => instance.hue = Some(value.parse()?),
"amt" => instance.amount = Some(value.parse()?),
_ => (),
}
Ok(instance)
})
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct TransformEffect {
pub scale_x: Option<Percentage>,
pub scale_y: Option<Percentage>,
pub translate_x: Option<Coordinate>,
pub translate_y: Option<Coordinate>,
pub skew_x: Option<FixedAngle>,
pub skew_y: Option<FixedAngle>,
}
impl TransformEffect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_ref() {
"sx" => instance.scale_x = Some(value.parse()?),
"sy" => instance.scale_y = Some(value.parse()?),
"kx" => instance.skew_x = Some(value.parse()?),
"ky" => instance.skew_y = Some(value.parse()?),
"tx" => instance.translate_x = Some(value.parse()?),
"ty" => instance.translate_y = Some(value.parse()?),
_ => (),
}
Ok(instance)
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Effect {
Container(EffectContainer),
EffectReference(String),
AlphaBiLevel(AlphaBiLevelEffect),
AlphaCeiling,
AlphaFloor,
AlphaInverse(AlphaInverseEffect),
AlphaModulate(AlphaModulateEffect),
AlphaModulateFixed(AlphaModulateFixedEffect),
AlphaOutset(AlphaOutsetEffect),
AlphaReplace(AlphaReplaceEffect),
BiLevel(BiLevelEffect),
Blend(BlendEffect),
Blur(BlurEffect),
ColorChange(ColorChangeEffect),
ColorReplace(ColorReplaceEffect),
Duotone(DuotoneEffect),
Fill(FillEffect),
FillOverlay(FillOverlayEffect),
Glow(GlowEffect),
Grayscale,
Hsl(HslEffect),
InnerShadow(InnerShadowEffect),
Luminance(LuminanceEffect),
OuterShadow(OuterShadowEffect),
PresetShadow(PresetShadowEffect),
Reflection(ReflectionEffect),
RelativeOffset(RelativeOffsetEffect),
SoftEdges(SoftEdgesEffect),
Tint(TintEffect),
Transform(TransformEffect),
}
impl XsdType for Effect {
fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
match xml_node.local_name() {
"cont" => Ok(Effect::Container(EffectContainer::from_xml_element(xml_node)?)),
"effect" => {
let reference = xml_node
.attributes
.get("ref")
.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "ref"))?
.clone();
Ok(Effect::EffectReference(reference))
}
"alphaBiLevel" => Ok(Effect::AlphaBiLevel(AlphaBiLevelEffect::from_xml_element(xml_node)?)),
"alphaCeiling" => Ok(Effect::AlphaCeiling),
"alphaFloor" => Ok(Effect::AlphaFloor),
"alphaInv" => Ok(Effect::AlphaInverse(AlphaInverseEffect::from_xml_element(xml_node)?)),
"alphaMod" => Ok(Effect::AlphaModulate(AlphaModulateEffect::from_xml_element(xml_node)?)),
"alphaModFix" => Ok(Effect::AlphaModulateFixed(AlphaModulateFixedEffect::from_xml_element(
xml_node,
)?)),
"alphaOutset" => Ok(Effect::AlphaOutset(AlphaOutsetEffect::from_xml_element(xml_node)?)),
"alphaRepl" => Ok(Effect::AlphaReplace(AlphaReplaceEffect::from_xml_element(xml_node)?)),
"biLevel" => Ok(Effect::BiLevel(BiLevelEffect::from_xml_element(xml_node)?)),
"blend" => Ok(Effect::Blend(BlendEffect::from_xml_element(xml_node)?)),
"blur" => Ok(Effect::Blur(BlurEffect::from_xml_element(xml_node)?)),
"clrChange" => Ok(Effect::ColorChange(ColorChangeEffect::from_xml_element(xml_node)?)),
"clrRepl" => Ok(Effect::ColorReplace(ColorReplaceEffect::from_xml_element(xml_node)?)),
"duotone" => Ok(Effect::Duotone(DuotoneEffect::from_xml_element(xml_node)?)),
"fill" => Ok(Effect::Fill(FillEffect::from_xml_element(xml_node)?)),
"fillOverlay" => Ok(Effect::FillOverlay(FillOverlayEffect::from_xml_element(xml_node)?)),
"glow" => Ok(Effect::Glow(GlowEffect::from_xml_element(xml_node)?)),
"grayscl" => Ok(Effect::Grayscale),
"hsl" => Ok(Effect::Hsl(HslEffect::from_xml_element(xml_node)?)),
"innerShdw" => Ok(Effect::InnerShadow(InnerShadowEffect::from_xml_element(xml_node)?)),
"lum" => Ok(Effect::Luminance(LuminanceEffect::from_xml_element(xml_node)?)),
"outerShdw" => Ok(Effect::OuterShadow(OuterShadowEffect::from_xml_element(xml_node)?)),
"prstShdw" => Ok(Effect::PresetShadow(PresetShadowEffect::from_xml_element(xml_node)?)),
"reflection" => Ok(Effect::Reflection(ReflectionEffect::from_xml_element(xml_node)?)),
"relOff" => Ok(Effect::RelativeOffset(RelativeOffsetEffect::from_xml_element(
xml_node,
)?)),
"softEdge" => Ok(Effect::SoftEdges(SoftEdgesEffect::from_xml_element(xml_node)?)),
"tint" => Ok(Effect::Tint(TintEffect::from_xml_element(xml_node)?)),
"xfrm" => Ok(Effect::Transform(TransformEffect::from_xml_element(xml_node)?)),
_ => Err(Box::new(NotGroupMemberError::new(xml_node.name.clone(), "EG_Effect"))),
}
}
}
impl XsdChoice for Effect {
fn is_choice_member<T>(name: T) -> bool
where
T: AsRef<str>,
{
match name.as_ref() {
"cont" | "effect" | "alphaBiLevel" | "alphaCeiling" | "alphaFloor" | "alphaInv" | "alphaMod"
| "alphaModFix" | "alphaOutset" | "alphaRepl" | "biLevel" | "blend" | "blur" | "clrChange" | "clrRepl"
| "duotone" | "fill" | "fillOverlay" | "glow" | "grayscl" | "hsl" | "innerShdw" | "lum" | "outerShdw"
| "prstShdw" | "reflection" | "relOff" | "softEdge" | "tint" | "xfrm" => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum BlipEffect {
AlphaBiLevel(AlphaBiLevelEffect),
AlphaCeiling,
AlphaFloor,
AlphaInverse(AlphaInverseEffect),
AlphaModulate(AlphaModulateEffect),
AlphaModulateFixed(AlphaModulateFixedEffect),
AlphaReplace(AlphaReplaceEffect),
BiLevel(BiLevelEffect),
Blur(BlurEffect),
ColorChange(ColorChangeEffect),
ColorReplace(ColorReplaceEffect),
Duotone(DuotoneEffect),
FillOverlay(FillOverlayEffect),
Grayscale,
Hsl(HslEffect),
Luminance(LuminanceEffect),
Tint(TintEffect),
}
impl XsdType for BlipEffect {
fn from_xml_element(xml_node: &XmlNode) -> Result<BlipEffect> {
match xml_node.local_name() {
"alphaBiLevel" => Ok(BlipEffect::AlphaBiLevel(AlphaBiLevelEffect::from_xml_element(
xml_node,
)?)),
"alphaCeiling" => Ok(BlipEffect::AlphaCeiling),
"alphaFloor" => Ok(BlipEffect::AlphaFloor),
"alphaInv" => Ok(BlipEffect::AlphaInverse(AlphaInverseEffect::from_xml_element(
xml_node,
)?)),
"alphaMod" => Ok(BlipEffect::AlphaModulate(AlphaModulateEffect::from_xml_element(
xml_node,
)?)),
"alphaModFixed" => Ok(BlipEffect::AlphaModulateFixed(
AlphaModulateFixedEffect::from_xml_element(xml_node)?,
)),
"alphaRepl" => Ok(BlipEffect::AlphaReplace(AlphaReplaceEffect::from_xml_element(
xml_node,
)?)),
"biLevel" => Ok(BlipEffect::BiLevel(BiLevelEffect::from_xml_element(xml_node)?)),
"blur" => Ok(BlipEffect::Blur(BlurEffect::from_xml_element(xml_node)?)),
"clrChange" => Ok(BlipEffect::ColorChange(ColorChangeEffect::from_xml_element(xml_node)?)),
"clrRepl" => Ok(BlipEffect::ColorReplace(ColorReplaceEffect::from_xml_element(
xml_node,
)?)),
"duotone" => Ok(BlipEffect::Duotone(DuotoneEffect::from_xml_element(xml_node)?)),
"fillOverlay" => Ok(BlipEffect::FillOverlay(FillOverlayEffect::from_xml_element(xml_node)?)),
"grayscl" => Ok(BlipEffect::Grayscale),
"hsl" => Ok(BlipEffect::Hsl(HslEffect::from_xml_element(xml_node)?)),
"lum" => Ok(BlipEffect::Luminance(LuminanceEffect::from_xml_element(xml_node)?)),
"tint" => Ok(BlipEffect::Tint(TintEffect::from_xml_element(xml_node)?)),
_ => Err(NotGroupMemberError::new(xml_node.name.clone(), "EG_BlipEffect").into()),
}
}
}
impl XsdChoice for BlipEffect {
fn is_choice_member<T: AsRef<str>>(name: T) -> bool {
match name.as_ref() {
"alphaBiLevel" | "alphaCeiling" | "alphaFloor" | "alphaInv" | "alphaMod" | "alphaModFixed"
| "alphaRepl" | "biLevel" | "blur" | "clrChange" | "clrRepl" | "duotone" | "fillOverlay" | "grayscl"
| "hsl" | "lum" | "tint" => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum EffectProperties {
EffectList(Box<EffectList>),
EffectContainer(Box<EffectContainer>),
}
impl XsdType for EffectProperties {
fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
match xml_node.local_name() {
"effectLst" => Ok(EffectProperties::EffectList(Box::new(EffectList::from_xml_element(
xml_node,
)?))),
"effectDag" => Ok(EffectProperties::EffectContainer(Box::new(
EffectContainer::from_xml_element(xml_node)?,
))),
_ => Err(Box::new(NotGroupMemberError::new(
xml_node.name.clone(),
"EG_EffectProperties",
))),
}
}
}
impl XsdChoice for EffectProperties {
fn is_choice_member<T>(name: T) -> bool
where
T: AsRef<str>,
{
match name.as_ref() {
"effectLst" | "effectDag" => true,
_ => false,
}
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct EffectList {
pub blur: Option<BlurEffect>,
pub fill_overlay: Option<FillOverlayEffect>,
pub glow: Option<GlowEffect>,
pub inner_shadow: Option<InnerShadowEffect>,
pub outer_shadow: Option<OuterShadowEffect>,
pub preset_shadow: Option<PresetShadowEffect>,
pub reflection: Option<ReflectionEffect>,
pub soft_edges: Option<SoftEdgesEffect>,
}
impl EffectList {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
trace!("parsing EffectList '{}'", xml_node.name);
xml_node
.child_nodes
.iter()
.try_fold(Default::default(), |mut instance: Self, child_node| {
match child_node.local_name() {
"blur" => instance.blur = Some(BlurEffect::from_xml_element(child_node)?),
"fillOverlay" => instance.fill_overlay = Some(FillOverlayEffect::from_xml_element(child_node)?),
"glow" => instance.glow = Some(GlowEffect::from_xml_element(child_node)?),
"innerShdw" => instance.inner_shadow = Some(InnerShadowEffect::from_xml_element(child_node)?),
"outerShdw" => instance.outer_shadow = Some(OuterShadowEffect::from_xml_element(child_node)?),
"prstShdw" => instance.preset_shadow = Some(PresetShadowEffect::from_xml_element(child_node)?),
"reflection" => instance.reflection = Some(ReflectionEffect::from_xml_element(child_node)?),
"softEdge" => instance.soft_edges = Some(SoftEdgesEffect::from_xml_element(child_node)?),
_ => (),
}
Ok(instance)
})
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct EffectContainer {
pub container_type: Option<EffectContainerType>,
pub name: Option<String>,
pub effects: Vec<Effect>,
}
impl EffectContainer {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<EffectContainer> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_str() {
"type" => instance.container_type = Some(value.parse::<EffectContainerType>()?),
"name" => instance.name = Some(value.clone()),
_ => (),
}
Ok(instance)
})
.and_then(|mut instance| {
instance.effects = xml_node
.child_nodes
.iter()
.filter_map(Effect::try_from_xml_element)
.collect::<Result<Vec<_>>>()?;
Ok(instance)
})
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct GradientFillProperties {
pub flip: Option<TileFlipMode>,
pub rotate_with_shape: Option<bool>,
pub gradient_stop_list: Option<Vec<GradientStop>>,
pub shade_properties: Option<ShadeProperties>,
pub tile_rect: Option<RelativeRect>,
}
impl GradientFillProperties {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_str() {
"flip" => instance.flip = Some(value.parse()?),
"rotWithShape" => instance.rotate_with_shape = Some(parse_xml_bool(value)?),
_ => (),
}
Ok(instance)
})
.and_then(|instance| {
xml_node
.child_nodes
.iter()
.try_fold(instance, |mut instance, child_node| {
match child_node.local_name() {
"gsLst" => {
let gradient_stop_list = child_node
.child_nodes
.iter()
.filter(|gs_node| gs_node.local_name() == "gs")
.map(GradientStop::from_xml_element)
.collect::<Result<Vec<_>>>()?;
match gradient_stop_list.len() {
len if len >= 2 => instance.gradient_stop_list = Some(gradient_stop_list),
len => {
return Err(Box::<dyn Error>::from(LimitViolationError::new(
xml_node.name.clone(),
"gsLst",
2,
MaxOccurs::Unbounded,
len as u32,
)))
}
}
}
"tileRect" => instance.tile_rect = Some(RelativeRect::from_xml_element(child_node)?),
local_name if ShadeProperties::is_choice_member(local_name) => {
instance.shade_properties = Some(ShadeProperties::from_xml_element(child_node)?)
}
_ => (),
}
Ok(instance)
})
})
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct Blip {
pub embed_rel_id: Option<RelationshipId>,
pub linked_rel_id: Option<RelationshipId>,
pub compression: Option<BlipCompression>,
pub effects: Vec<BlipEffect>,
}
impl Blip {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_ref() {
"r:embed" => instance.embed_rel_id = Some(value.clone()),
"r:link" => instance.linked_rel_id = Some(value.clone()),
"cstate" => instance.compression = Some(value.parse::<BlipCompression>()?),
_ => (),
}
Ok(instance)
})
.and_then(|instance| {
let effects = xml_node
.child_nodes
.iter()
.filter_map(BlipEffect::try_from_xml_element)
.collect::<Result<Vec<_>>>()?;
Ok(Self { effects, ..instance })
})
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct BlipFillProperties {
pub dpi: Option<u32>,
pub rotate_with_shape: Option<bool>,
pub blip: Option<Box<Blip>>,
pub source_rect: Option<RelativeRect>,
pub fill_mode_properties: Option<FillModeProperties>,
}
impl BlipFillProperties {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_ref() {
"dpi" => instance.dpi = Some(value.parse()?),
"rotWithShape" => instance.rotate_with_shape = Some(parse_xml_bool(value)?),
_ => (),
}
Ok(instance)
})
.and_then(|instance| {
xml_node
.child_nodes
.iter()
.try_fold(instance, |mut instance, child_node| {
match child_node.local_name() {
"blip" => instance.blip = Some(Box::new(Blip::from_xml_element(child_node)?)),
"srcRect" => instance.source_rect = Some(RelativeRect::from_xml_element(child_node)?),
local_name if FillModeProperties::is_choice_member(local_name) => {
instance.fill_mode_properties = Some(FillModeProperties::from_xml_element(child_node)?)
}
_ => (),
}
Ok(instance)
})
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DashStop {
pub dash_length: PositivePercentage,
pub space_length: PositivePercentage,
}
impl DashStop {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<DashStop> {
let mut opt_dash_length = None;
let mut opt_space_length = None;
for (attr, value) in &xml_node.attributes {
match attr.as_ref() {
"d" => opt_dash_length = Some(value.parse::<PositivePercentage>()?),
"sp" => opt_space_length = Some(value.parse::<PositivePercentage>()?),
_ => (),
}
}
let dash_length = opt_dash_length.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "d"))?;
let space_length = opt_space_length.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "sp"))?;
Ok(Self {
dash_length,
space_length,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct GradientStop {
pub position: PositiveFixedPercentage,
pub color: Color,
}
impl GradientStop {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let position = xml_node
.attributes
.get("pos")
.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "pos"))?
.parse()?;
let color = xml_node
.child_nodes
.iter()
.find_map(Color::try_from_xml_element)
.transpose()?
.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "color"))?;
Ok(Self { position, color })
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct LineEndProperties {
pub end_type: Option<LineEndType>,
pub width: Option<LineEndWidth>,
pub length: Option<LineEndLength>,
}
impl LineEndProperties {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<LineEndProperties> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_ref() {
"type" => instance.end_type = Some(value.parse::<LineEndType>()?),
"width" => instance.width = Some(value.parse::<LineEndWidth>()?),
"length" => instance.length = Some(value.parse::<LineEndLength>()?),
_ => (),
}
Ok(instance)
})
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct LinearShadeProperties {
pub angle: Option<PositiveFixedAngle>,
pub scaled: Option<bool>,
}
impl LinearShadeProperties {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_ref() {
"ang" => instance.angle = Some(value.parse::<PositiveFixedAngle>()?),
"scaled" => instance.scaled = Some(parse_xml_bool(value)?),
_ => (),
}
Ok(instance)
})
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct PathShadeProperties {
pub path: Option<PathShadeType>,
pub fill_to_rect: Option<RelativeRect>,
}
impl PathShadeProperties {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let path = xml_node.attributes.get("path").map(|value| value.parse()).transpose()?;
let fill_to_rect = xml_node
.child_nodes
.iter()
.find(|child_node| child_node.local_name() == "fillToRect")
.map(RelativeRect::from_xml_element)
.transpose()?;
Ok(Self { path, fill_to_rect })
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ShadeProperties {
Linear(LinearShadeProperties),
Path(PathShadeProperties),
}
impl XsdType for ShadeProperties {
fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
match xml_node.local_name() {
"lin" => Ok(ShadeProperties::Linear(LinearShadeProperties::from_xml_element(
xml_node,
)?)),
"path" => Ok(ShadeProperties::Path(PathShadeProperties::from_xml_element(xml_node)?)),
_ => Err(NotGroupMemberError::new(xml_node.name.clone(), "EG_ShadeProperties").into()),
}
}
}
impl XsdChoice for ShadeProperties {
fn is_choice_member<T: AsRef<str>>(name: T) -> bool {
match name.as_ref() {
"lin" | "path" => true,
_ => false,
}
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct PatternFillProperties {
pub preset: Option<PresetPatternVal>,
pub fg_color: Option<Color>,
pub bg_color: Option<Color>,
}
impl PatternFillProperties {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let preset = xml_node.attributes.get("prst").map(|value| value.parse()).transpose()?;
xml_node.child_nodes.iter().try_fold(
Self {
preset,
..Default::default()
},
|mut instance, child_node| {
match child_node.local_name() {
"fgClr" => {
let fg_color = child_node
.child_nodes
.iter()
.find_map(Color::try_from_xml_element)
.transpose()?
.ok_or_else(|| MissingChildNodeError::new(child_node.name.clone(), "EG_Color"))?;
instance.fg_color = Some(fg_color);
}
"bgClr" => {
let bg_color = child_node
.child_nodes
.iter()
.find_map(Color::try_from_xml_element)
.transpose()?
.ok_or_else(|| MissingChildNodeError::new(child_node.name.clone(), "EG_Color"))?;
instance.bg_color = Some(bg_color);
}
_ => (),
}
Ok(instance)
},
)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum FillProperties {
NoFill,
SolidFill(Color),
GradientFill(Box<GradientFillProperties>),
BlipFill(Box<BlipFillProperties>),
PatternFill(Box<PatternFillProperties>),
GroupFill,
}
impl XsdType for FillProperties {
fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
match xml_node.local_name() {
"noFill" => Ok(FillProperties::NoFill),
"solidFill" => {
let color = xml_node
.child_nodes
.iter()
.find_map(Color::try_from_xml_element)
.transpose()?
.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "color"))?;
Ok(FillProperties::SolidFill(color))
}
"gradFill" => Ok(FillProperties::GradientFill(Box::new(
GradientFillProperties::from_xml_element(xml_node)?,
))),
"blipFill" => Ok(FillProperties::BlipFill(Box::new(
BlipFillProperties::from_xml_element(xml_node)?,
))),
"pattFill" => Ok(FillProperties::PatternFill(Box::new(
PatternFillProperties::from_xml_element(xml_node)?,
))),
"grpFill" => Ok(FillProperties::GroupFill),
_ => Err(NotGroupMemberError::new(xml_node.name.clone(), "EG_FillProperties").into()),
}
}
}
impl XsdChoice for FillProperties {
fn is_choice_member<T: AsRef<str>>(name: T) -> bool {
match name.as_ref() {
"noFill" | "solidFill" | "gradFill" | "blipFill" | "pattFill" | "grpFill" => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum LineJoinProperties {
Round,
Bevel,
Miter(Option<PositivePercentage>),
}
impl XsdType for LineJoinProperties {
fn from_xml_element(xml_node: &XmlNode) -> Result<LineJoinProperties> {
match xml_node.local_name() {
"round" => Ok(LineJoinProperties::Round),
"bevel" => Ok(LineJoinProperties::Bevel),
"miter" => {
let lim = xml_node.attributes.get("lim").map(|value| value.parse()).transpose()?;
Ok(LineJoinProperties::Miter(lim))
}
_ => Err(NotGroupMemberError::new(xml_node.name.clone(), "EG_LineJoinProperties").into()),
}
}
}
impl XsdChoice for LineJoinProperties {
fn is_choice_member<T: AsRef<str>>(name: T) -> bool {
match name.as_ref() {
"round" | "bevel" | "miter" => true,
_ => false,
}
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct StretchInfoProperties {
pub fill_rect: Option<RelativeRect>,
}
impl StretchInfoProperties {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let fill_rect = xml_node
.child_nodes
.iter()
.find(|child_node| child_node.local_name() == "fillRect")
.map(RelativeRect::from_xml_element)
.transpose()?;
Ok(Self { fill_rect })
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct TileInfoProperties {
pub translate_x: Option<Coordinate>,
pub translate_y: Option<Coordinate>,
pub scale_x: Option<Percentage>,
pub scale_y: Option<Percentage>,
pub flip_mode: Option<TileFlipMode>,
pub alignment: Option<RectAlignment>,
}
impl TileInfoProperties {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_ref() {
"tx" => instance.translate_x = Some(value.parse()?),
"ty" => instance.translate_y = Some(value.parse()?),
"sx" => instance.scale_x = Some(value.parse()?),
"sy" => instance.scale_y = Some(value.parse()?),
"flip" => instance.flip_mode = Some(value.parse()?),
"algn" => instance.alignment = Some(value.parse()?),
_ => (),
}
Ok(instance)
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum FillModeProperties {
Tile(Box<TileInfoProperties>),
Stretch(Box<StretchInfoProperties>),
}
impl XsdType for FillModeProperties {
fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
match xml_node.local_name() {
"tile" => Ok(FillModeProperties::Tile(Box::new(
TileInfoProperties::from_xml_element(xml_node)?,
))),
"stretch" => Ok(FillModeProperties::Stretch(Box::new(
StretchInfoProperties::from_xml_element(xml_node)?,
))),
_ => Err(NotGroupMemberError::new(xml_node.name.clone(), "EG_FillModeProperties").into()),
}
}
}
impl XsdChoice for FillModeProperties {
fn is_choice_member<T: AsRef<str>>(name: T) -> bool {
match name.as_ref() {
"tile" | "stretch" => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum LineFillProperties {
NoFill,
SolidFill(Color),
GradientFill(Box<GradientFillProperties>),
PatternFill(Box<PatternFillProperties>),
}
impl XsdType for LineFillProperties {
fn from_xml_element(xml_node: &XmlNode) -> Result<LineFillProperties> {
match xml_node.local_name() {
"noFill" => Ok(LineFillProperties::NoFill),
"solidFill" => {
let color = xml_node
.child_nodes
.iter()
.find_map(Color::try_from_xml_element)
.transpose()?
.ok_or_else(|| MissingChildNodeError::new(xml_node.name.clone(), "color"))?;
Ok(LineFillProperties::SolidFill(color))
}
"gradFill" => Ok(LineFillProperties::GradientFill(Box::new(
GradientFillProperties::from_xml_element(xml_node)?,
))),
"pattFill" => Ok(LineFillProperties::PatternFill(Box::new(
PatternFillProperties::from_xml_element(xml_node)?,
))),
_ => Err(NotGroupMemberError::new(xml_node.name.clone(), "EG_LineFillProperties").into()),
}
}
}
impl XsdChoice for LineFillProperties {
fn is_choice_member<T: AsRef<str>>(name: T) -> bool {
match name.as_ref() {
"noFill" | "solidFill" | "gradFill" | "pattFill" => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum LineDashProperties {
PresetDash(PresetLineDashVal),
CustomDash(Vec<DashStop>),
}
impl XsdChoice for LineDashProperties {
fn is_choice_member<T: AsRef<str>>(name: T) -> bool {
match name.as_ref() {
"prstDash" | "custDash" => true,
_ => false,
}
}
}
impl XsdType for LineDashProperties {
fn from_xml_element(xml_node: &XmlNode) -> Result<LineDashProperties> {
match xml_node.local_name() {
"prstDash" => {
let val = xml_node
.attributes
.get("val")
.map(|value| value.parse())
.transpose()?
.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "val"))?;
Ok(LineDashProperties::PresetDash(val))
}
"custDash" => {
let dash_vec = xml_node
.child_nodes
.iter()
.filter(|child_node| child_node.local_name() == "ds")
.map(DashStop::from_xml_element)
.collect::<Result<Vec<_>>>()?;
Ok(LineDashProperties::CustomDash(dash_vec))
}
_ => Err(NotGroupMemberError::new(xml_node.name.clone(), "EG_LineDashProperties").into()),
}
}
}