.filter(|(key, _value)| key != "gifindex" && key != "fill")
.collect::<BTreeMap<_, _>>();
let id = attributes.remove("id").unwrap();
+ let height = attributes.remove("height").unwrap().parse::<f64>().unwrap();
+ let width = attributes.remove("width").unwrap().parse::<f64>().unwrap();
let index = id
.strip_prefix("pattern")
.unwrap()
.parse::<usize>()
.unwrap();
- patterns[index] = Some((attributes, content));
+ patterns[index] = Some((width, height, attributes, content));
//writeln!(&mut output, "{index} {attributes:?} {content:?}").unwrap();
}
}
pub static PATTERNS: [Pattern; 30] = ["
)
.unwrap();
- for (attributes, content) in &patterns {
+ for (width, height, attributes, content) in &patterns {
writeln!(
&mut output,
" Pattern {{
+ width: {width:?},
+ height: {height:?},
attributes: &["
)
.unwrap();
}
/// Color used in [FontStyle].
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Color {
/// Alpha channel.
///
ColorDisplayCss(*self)
}
+ /// Displays opaque colors as `rrggbb` and others as `rrggbbaa`.
+ pub fn display_hex(&self) -> impl Display {
+ ColorDisplayHex(*self)
+ }
+
/// Returns the red, green, and blue channels of this color.
pub fn into_rgb(&self) -> (u8, u8, u8) {
(self.r, self.g, self.b)
impl Display for ColorDisplayCss {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- let Color { alpha, r, g, b } = self.0;
+ let (r, g, b, alpha) = self.0.into_rgba();
match alpha {
255 => write!(f, "#{r:02x}{g:02x}{b:02x}"),
_ => write!(f, "rgb({r}, {g}, {b}, {:.2})", alpha as f64 / 255.0),
}
}
+/// A structure for formatting a [Color] as hex digits.
+///
+/// See [Color::display_hex].
+struct ColorDisplayHex(Color);
+
+impl Display for ColorDisplayHex {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let (r, g, b, a) = self.0.into_rgba();
+ write!(f, "{r:02x}{g:02x}{b:02x}")?;
+ if a != 255 {
+ write!(f, "{a:02x}")?;
+ }
+ Ok(())
+ }
+}
+
/// Style for drawing a border in a pivot table.
#[derive(Copy, Clone, Debug, PartialEq, Deserialize)]
pub struct BorderStyle {
read::graph::{
format::Formatter,
frames::{Component, Frames, Position},
- patterns::Patterns,
+ patterns::{ColoredPattern, Patterns},
style::{Style, StyleCycle},
},
},
let color = style.color.unwrap_or(Color::BLACK);
let color2 = style.color2.unwrap_or(Color::BLACK);
- let pattern = patterns.insert(style.pattern.unwrap_or_default(), color);
+ let pattern = patterns.insert(ColoredPattern {
+ index: style.pattern.unwrap_or_default(),
+ color,
+ color2: style.color2.unwrap_or(Color::WHITE),
+ });
fn points(
rect: &Rect,
-use std::{
- collections::{BTreeMap, BTreeSet},
- fmt::Display,
-};
+use std::{collections::BTreeSet, fmt::Display};
-use svg::{Node, node::Blob};
+use svg::{
+ Node,
+ node::{Blob, element::Rectangle},
+};
use crate::output::pivot::look::Color;
mod data;
pub struct Pattern {
+ width: f64,
+ height: f64,
attributes: &'static [(&'static str, &'static str)],
content: &'static str,
}
data::PATTERNS.get(index)
}
- pub fn to_svg(&self) -> svg::node::element::Pattern {
- let mut group = svg::node::element::Pattern::new().add(Blob::new(self.content));
+ pub fn to_svg(&self, color: Color, color2: Color) -> svg::node::element::Pattern {
+ let mut group = svg::node::element::Pattern::new()
+ .set("fill", color.display_css().to_string())
+ .set("width", self.width)
+ .set("height", self.height)
+ .add(
+ Rectangle::new()
+ .set("x", 0)
+ .set("y", 0)
+ .set("width", self.width)
+ .set("height", self.height)
+ .set("fill", color2.display_css().to_string()),
+ )
+ .add(Blob::new(self.content));
for (name, value) in self.attributes.into_iter().copied() {
group.assign(name, value);
}
}
}
-#[derive(Clone, Debug, Default)]
-pub struct Patterns {
- instances: BTreeMap<usize, BTreeSet<Color>>,
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct ColoredPattern {
+ pub index: usize,
+ pub color: Color,
+ pub color2: Color,
}
-struct PatternName {
- index: usize,
- color: Color,
-}
-impl Display for PatternName {
+impl Display for ColoredPattern {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- let (r, g, b, a) = self.color.into_rgba();
- write!(f, "pattern{}_{r:02x}{g:02x}{b:02x}", self.index)?;
- if a != 255 {
- write!(f, "_{a:02x}")?;
- }
+ write!(
+ f,
+ "pattern{}_{}_{}",
+ self.index,
+ self.color.display_hex(),
+ self.color2.display_hex()
+ )?;
Ok(())
}
}
+#[derive(Clone, Debug, Default)]
+pub struct Patterns {
+ instances: BTreeSet<ColoredPattern>,
+}
+
impl Patterns {
pub fn new() -> Self {
Self::default()
}
- pub fn insert(&mut self, index: usize, color: Color) -> String {
- if index != 0 && Pattern::for_index(index).is_some() {
- self.instances.entry(index).or_default().insert(color);
- format!("url(#{})", PatternName { index, color })
+ pub fn insert(&mut self, p: ColoredPattern) -> String {
+ if p.index != 0 && Pattern::for_index(p.index).is_some() {
+ self.instances.insert(p);
+ format!("url(#{p})")
} else {
- color.display_css().to_string()
+ p.color.display_css().to_string()
}
}
pub fn to_svg(&self) -> impl Iterator<Item = svg::node::element::Pattern> {
- self.instances.iter().flat_map(|(index, colors)| {
- colors.iter().map(move |color| {
- Pattern::for_index(*index)
- .unwrap()
- .to_svg()
- .set(
- "id",
- PatternName {
- index: *index,
- color: *color,
- }
- .to_string(),
- )
- .set("fill", color.display_css().to_string())
- })
+ self.instances.iter().map(|cp| {
+ Pattern::for_index(cp.index)
+ .unwrap()
+ .to_svg(cp.color, cp.color2)
+ .set("id", cp.to_string())
})
}
}
#[cfg(test)]
mod tests {
- use crate::spv::read::graph::patterns::Pattern;
+ use crate::{output::pivot::look::Color, spv::read::graph::patterns::Pattern};
#[test]
fn pattern() {
let pattern = Pattern::for_index(1).unwrap();
- let svg = pattern.to_svg().to_string();
-
+ let svg = pattern.to_svg(Color::BLACK, Color::WHITE).to_string();
+ println!("{svg}");
assert_eq!(
svg,
- r#"<pattern height="4" patternUnits="userSpaceOnUse" width="10">
+ r##"<pattern fill="#000000" height="4" patternUnits="userSpaceOnUse" width="10">
+<rect fill="#ffffff" height="4" width="10" x="0" y="0"/>
+
<circle cx="4.5" cy="3.5" r="0.5" height="1" />
<rect x="9" y="1" width="1" height="1" />
- </pattern>"#,
- "This assertion will need to be updated if pattern 1 is changed in patterns.svg."
+
+</pattern>"##
);
}
}
Percent
</text>
<g id="element_bar_1">
-<rect fill="#d3ce97" height="92.56696428571429" stroke="#000000" width="205.6875" x="104.53125" y="126.93303571428571"/>
-<rect fill="#d3ce97" height="204.2857142857143" stroke="#000000" width="205.6875" x="378.78125" y="15.214285714285701"/>
+<rect fill="url(#pattern12_d3ce97_000000)" height="92.56696428571429" stroke="#000000" width="205.6875" x="104.53125" y="126.93303571428571"/>
+<rect fill="url(#pattern12_d3ce97_000000)" height="204.2857142857143" stroke="#000000" width="205.6875" x="378.78125" y="15.214285714285701"/>
</g>
<g id="element_bar_1">
-<rect fill="#d3ce97" height="121.29464285714289" stroke="#000000" width="205.6875" x="104.53125" y="317.7053571428571"/>
-<rect fill="#d3ce97" height="127.67857142857144" stroke="#000000" width="205.6875" x="378.78125" y="311.32142857142856"/>
+<rect fill="url(#pattern12_d3ce97_000000)" height="121.29464285714289" stroke="#000000" width="205.6875" x="104.53125" y="317.7053571428571"/>
+<rect fill="url(#pattern12_d3ce97_000000)" height="127.67857142857144" stroke="#000000" width="205.6875" x="378.78125" y="311.32142857142856"/>
</g>
+<defs>
+<pattern fill="#d3ce97" height="8" id="pattern12_d3ce97_000000" patternUnits="userSpaceOnUse" width="8">
+<rect fill="#000000" height="8" width="8" x="0" y="0"/>
+
+ <rect x="0" y="0" width="1" height="4"/>
+ <rect x="5" y="0" width="3" height="4"/>
+ <rect x="1" y="4" width="4" height="4"/>
+
+</pattern>
+</defs>
</g>
</svg>
<style id="label_panel_1s"/>
<style id="majorTicks_axis_panel_1markStyle" visible="false"/>
<style id="majorTicks_axis_panel_1tickLabelStyle"/>
- <style color="#D3CE97" color2="black" depth="75%" id="element_bar_1s" pattern="0" size="75%"/>
+ <style color="#D3CE97" color2="black" depth="75%" id="element_bar_1s" pattern="12" size="75%"/>
<style color="black" id="axis_axisx_1s" stroke-width="0.5pt"/>
<style id="axis_axisx_1baselineStyle"/>
<style color="black" id="majorTicks_axis_axisx_1markStyle" stroke-width="1pt"/>