rust-pptx 0.1.7

Parse PowerPoint PPTX and legacy PPT presentations into a structured model
Documentation
use std::{collections::HashMap, rc::Rc};

use super::{BTreeMap, NodeParseContext, OfficeArtColors, ThemeDataInternal};
use rust_office_art::RenderedPicture;

#[derive(Eq, PartialEq, Hash)]
pub(super) struct ColorKey {
    theme: Option<String>,
    map: BTreeMap<String, String>,
}

impl ColorKey {
    pub(super) fn new(
        theme: Option<&ThemeDataInternal>,
        map: &BTreeMap<String, String>,
    ) -> Rc<Self> {
        Rc::new(Self {
            theme: theme.map(|theme| theme.id.clone()),
            map: map.clone(),
        })
    }

    fn bytes(&self) -> usize {
        self.theme.as_ref().map_or(0, String::len)
            + self
                .map
                .iter()
                .map(|(key, value)| key.len() + value.len() + 64)
                .sum::<usize>()
    }
}

#[derive(Eq, PartialEq, Hash)]
struct ShapeKey {
    properties: String,
    style: String,
    width: u64,
    height: u64,
    colors: Option<Rc<ColorKey>>,
    relationships: Vec<(String, String)>,
    assets: usize,
}

impl ShapeKey {
    fn bytes(&self) -> usize {
        self.properties.len()
            + self.style.len()
            + self.colors.as_ref().map_or(0, |colors| colors.bytes())
            + self
                .relationships
                .iter()
                .map(|(key, value)| key.len() + value.len() + 64)
                .sum::<usize>()
            + 256
    }
}

#[derive(Default)]
pub(super) struct ShapeCache {
    entries: HashMap<ShapeKey, RenderedPicture>,
    bytes: usize,
}

pub(super) fn render_shape(
    properties: String,
    style: String,
    width: f64,
    height: f64,
    context: &NodeParseContext<'_, '_>,
) -> Option<RenderedPicture> {
    let mut relationships = context
        .rels
        .iter()
        .map(|(key, value)| (key.clone(), value.clone()))
        .collect::<Vec<_>>();
    relationships.sort_unstable();
    let key = ShapeKey {
        properties,
        style,
        width: width.to_bits(),
        height: height.to_bits(),
        colors: context.colors.map(|colors| Rc::clone(&colors.paint_key)),
        relationships,
        assets: context.assets.len(),
    };
    if let Some(paint) = context.shape_cache.borrow().entries.get(&key) {
        return Some(paint.clone());
    }
    let warning_count = context.diagnostics.warnings.borrow().len();
    let color_warning_count = context
        .colors
        .map(|colors| colors.diagnostics.warnings.borrow().len());
    let paint = rust_office_art::render_shape(
        &key.properties,
        &key.style,
        width,
        height,
        &OfficeArtColors {
            colors: context.colors,
            limits: context.limits,
            assets: context.assets,
            relationships: context.rels,
        },
    )?;
    if context.diagnostics.warnings.borrow().len() == warning_count
        && context
            .colors
            .map(|colors| colors.diagnostics.warnings.borrow().len())
            == color_warning_count
    {
        let bytes = key.bytes().saturating_add(paint.svg.len());
        let mut cache = context.shape_cache.borrow_mut();
        if bytes <= 1024 * 1024
            && cache.entries.len() < 4096
            && cache.bytes.saturating_add(bytes) <= 8 * 1024 * 1024
        {
            cache.entries.insert(key, paint.clone());
            cache.bytes += bytes;
        }
    }
    Some(paint)
}