#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![warn(missing_debug_implementations)]
#![warn(missing_copy_implementations)]
#![allow(clippy::collapsible_else_if)]
#![allow(clippy::neg_cmp_op_on_partial_ord)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::derivable_impls)]
pub mod filter;
mod geom;
mod text;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
pub use strict_num::{self, ApproxEqUlps, NonZeroPositiveF32, NormalizedF32, PositiveF32};
pub use svgtypes::{Align, AspectRatio};
pub use tiny_skia_path;
pub use crate::geom::*;
pub use crate::text::*;
pub type Opacity = NormalizedF32;
#[derive(Clone, Copy, Debug)]
pub struct NonZeroF32(f32);
impl NonZeroF32 {
#[inline]
pub fn new(n: f32) -> Option<Self> {
if n.approx_eq_ulps(&0.0, 4) {
None
} else {
Some(NonZeroF32(n))
}
}
#[inline]
pub fn get(&self) -> f32 {
self.0
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Units {
UserSpaceOnUse,
ObjectBoundingBox,
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Visibility {
Visible,
Hidden,
Collapse,
}
impl Default for Visibility {
fn default() -> Self {
Self::Visible
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[allow(missing_docs)]
pub enum ShapeRendering {
OptimizeSpeed,
CrispEdges,
GeometricPrecision,
}
impl ShapeRendering {
pub fn use_shape_antialiasing(self) -> bool {
match self {
ShapeRendering::OptimizeSpeed => false,
ShapeRendering::CrispEdges => false,
ShapeRendering::GeometricPrecision => true,
}
}
}
impl Default for ShapeRendering {
fn default() -> Self {
Self::GeometricPrecision
}
}
impl std::str::FromStr for ShapeRendering {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"optimizeSpeed" => Ok(ShapeRendering::OptimizeSpeed),
"crispEdges" => Ok(ShapeRendering::CrispEdges),
"geometricPrecision" => Ok(ShapeRendering::GeometricPrecision),
_ => Err("invalid"),
}
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum TextRendering {
OptimizeSpeed,
OptimizeLegibility,
GeometricPrecision,
}
impl Default for TextRendering {
fn default() -> Self {
Self::OptimizeLegibility
}
}
impl std::str::FromStr for TextRendering {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"optimizeSpeed" => Ok(TextRendering::OptimizeSpeed),
"optimizeLegibility" => Ok(TextRendering::OptimizeLegibility),
"geometricPrecision" => Ok(TextRendering::GeometricPrecision),
_ => Err("invalid"),
}
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ImageRendering {
OptimizeQuality,
OptimizeSpeed,
}
impl Default for ImageRendering {
fn default() -> Self {
Self::OptimizeQuality
}
}
impl std::str::FromStr for ImageRendering {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"optimizeQuality" => Ok(ImageRendering::OptimizeQuality),
"optimizeSpeed" => Ok(ImageRendering::OptimizeSpeed),
_ => Err("invalid"),
}
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum BlendMode {
Normal,
Multiply,
Screen,
Overlay,
Darken,
Lighten,
ColorDodge,
ColorBurn,
HardLight,
SoftLight,
Difference,
Exclusion,
Hue,
Saturation,
Color,
Luminosity,
}
impl Default for BlendMode {
fn default() -> Self {
Self::Normal
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum SpreadMethod {
Pad,
Reflect,
Repeat,
}
impl Default for SpreadMethod {
fn default() -> Self {
Self::Pad
}
}
#[derive(Clone, Debug)]
pub struct BaseGradient {
pub id: String,
pub units: Units,
pub transform: Transform,
pub spread_method: SpreadMethod,
pub stops: Vec<Stop>,
}
#[allow(missing_docs)]
#[derive(Clone, Debug)]
pub struct LinearGradient {
pub base: BaseGradient,
pub x1: f32,
pub y1: f32,
pub x2: f32,
pub y2: f32,
}
impl std::ops::Deref for LinearGradient {
type Target = BaseGradient;
fn deref(&self) -> &Self::Target {
&self.base
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug)]
pub struct RadialGradient {
pub base: BaseGradient,
pub cx: f32,
pub cy: f32,
pub r: PositiveF32,
pub fx: f32,
pub fy: f32,
}
impl std::ops::Deref for RadialGradient {
type Target = BaseGradient;
fn deref(&self) -> &Self::Target {
&self.base
}
}
pub type StopOffset = NormalizedF32;
#[derive(Clone, Copy, Debug)]
pub struct Stop {
pub offset: StopOffset,
pub color: Color,
pub opacity: Opacity,
}
#[derive(Clone, Debug)]
pub struct Pattern {
pub id: String,
pub units: Units,
pub content_units: Units,
pub transform: Transform,
pub rect: NonZeroRect,
pub view_box: Option<ViewBox>,
pub root: Group,
}
pub type StrokeWidth = NonZeroPositiveF32;
#[derive(Clone, Copy, Debug)]
pub struct StrokeMiterlimit(f32);
impl StrokeMiterlimit {
#[inline]
pub fn new(n: f32) -> Self {
debug_assert!(n.is_finite());
debug_assert!(n >= 1.0);
let n = if !(n >= 1.0) { 1.0 } else { n };
StrokeMiterlimit(n)
}
#[inline]
pub fn get(&self) -> f32 {
self.0
}
}
impl Default for StrokeMiterlimit {
#[inline]
fn default() -> Self {
StrokeMiterlimit::new(4.0)
}
}
impl From<f32> for StrokeMiterlimit {
#[inline]
fn from(n: f32) -> Self {
Self::new(n)
}
}
impl PartialEq for StrokeMiterlimit {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0.approx_eq_ulps(&other.0, 4)
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum LineCap {
Butt,
Round,
Square,
}
impl Default for LineCap {
fn default() -> Self {
Self::Butt
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum LineJoin {
Miter,
MiterClip,
Round,
Bevel,
}
impl Default for LineJoin {
fn default() -> Self {
Self::Miter
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug)]
pub struct Stroke {
pub paint: Paint,
pub dasharray: Option<Vec<f32>>,
pub dashoffset: f32,
pub miterlimit: StrokeMiterlimit,
pub opacity: Opacity,
pub width: StrokeWidth,
pub linecap: LineCap,
pub linejoin: LineJoin,
}
impl Default for Stroke {
fn default() -> Self {
Stroke {
paint: Paint::Color(Color::black()),
dasharray: None,
dashoffset: 0.0,
miterlimit: StrokeMiterlimit::default(),
opacity: Opacity::ONE,
width: StrokeWidth::new(1.0).unwrap(),
linecap: LineCap::default(),
linejoin: LineJoin::default(),
}
}
}
impl Stroke {
pub fn to_tiny_skia(&self) -> tiny_skia_path::Stroke {
let mut stroke = tiny_skia_path::Stroke {
width: self.width.get(),
miter_limit: self.miterlimit.get(),
line_cap: match self.linecap {
LineCap::Butt => tiny_skia_path::LineCap::Butt,
LineCap::Round => tiny_skia_path::LineCap::Round,
LineCap::Square => tiny_skia_path::LineCap::Square,
},
line_join: match self.linejoin {
LineJoin::Miter => tiny_skia_path::LineJoin::Miter,
LineJoin::MiterClip => tiny_skia_path::LineJoin::MiterClip,
LineJoin::Round => tiny_skia_path::LineJoin::Round,
LineJoin::Bevel => tiny_skia_path::LineJoin::Bevel,
},
dash: None,
};
if let Some(ref list) = self.dasharray {
stroke.dash = tiny_skia_path::StrokeDash::new(list.clone(), self.dashoffset);
}
stroke
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum FillRule {
NonZero,
EvenOdd,
}
impl Default for FillRule {
fn default() -> Self {
Self::NonZero
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug)]
pub struct Fill {
pub paint: Paint,
pub opacity: Opacity,
pub rule: FillRule,
}
impl Fill {
pub fn from_paint(paint: Paint) -> Self {
Fill {
paint,
..Fill::default()
}
}
}
impl Default for Fill {
fn default() -> Self {
Fill {
paint: Paint::Color(Color::black()),
opacity: Opacity::ONE,
rule: FillRule::default(),
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[allow(missing_docs)]
pub struct Color {
pub red: u8,
pub green: u8,
pub blue: u8,
}
impl Color {
#[inline]
pub fn new_rgb(red: u8, green: u8, blue: u8) -> Color {
Color { red, green, blue }
}
#[inline]
pub fn black() -> Color {
Color::new_rgb(0, 0, 0)
}
#[inline]
pub fn white() -> Color {
Color::new_rgb(255, 255, 255)
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug)]
pub enum Paint {
Color(Color),
LinearGradient(Rc<LinearGradient>),
RadialGradient(Rc<RadialGradient>),
Pattern(Rc<RefCell<Pattern>>),
}
impl Paint {
#[inline]
pub fn units(&self) -> Option<Units> {
match self {
Self::Color(_) => None,
Self::LinearGradient(ref lg) => Some(lg.units),
Self::RadialGradient(ref rg) => Some(rg.units),
Self::Pattern(ref patt) => Some(patt.borrow().units),
}
}
}
impl PartialEq for Paint {
#[inline]
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Color(lc), Self::Color(rc)) => lc == rc,
(Self::LinearGradient(ref lg1), Self::LinearGradient(ref lg2)) => Rc::ptr_eq(lg1, lg2),
(Self::RadialGradient(ref rg1), Self::RadialGradient(ref rg2)) => Rc::ptr_eq(rg1, rg2),
(Self::Pattern(ref p1), Self::Pattern(ref p2)) => Rc::ptr_eq(p1, p2),
_ => false,
}
}
}
#[derive(Clone, Debug)]
pub struct ClipPath {
pub id: String,
pub units: Units,
pub transform: Transform,
pub clip_path: Option<SharedClipPath>,
pub root: Group,
}
pub type SharedClipPath = Rc<RefCell<ClipPath>>;
impl Default for ClipPath {
fn default() -> Self {
ClipPath {
id: String::new(),
units: Units::UserSpaceOnUse,
transform: Transform::default(),
clip_path: None,
root: Group::default(),
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum MaskType {
Luminance,
Alpha,
}
impl Default for MaskType {
fn default() -> Self {
Self::Luminance
}
}
#[derive(Clone, Debug)]
pub struct Mask {
pub id: String,
pub units: Units,
pub content_units: Units,
pub rect: NonZeroRect,
pub kind: MaskType,
pub mask: Option<SharedMask>,
pub root: Group,
}
pub type SharedMask = Rc<RefCell<Mask>>;
#[allow(missing_docs)]
#[derive(Clone, Debug)]
pub enum Node {
Group(Box<Group>),
Path(Box<Path>),
Image(Box<Image>),
Text(Box<Text>),
}
impl Node {
pub fn id(&self) -> &str {
match self {
Node::Group(ref e) => e.id.as_str(),
Node::Path(ref e) => e.id.as_str(),
Node::Image(ref e) => e.id.as_str(),
Node::Text(ref e) => e.id.as_str(),
}
}
pub fn abs_transform(&self) -> Transform {
match self {
Node::Group(ref group) => group.abs_transform,
Node::Path(ref path) => path.abs_transform,
Node::Image(ref image) => image.abs_transform,
Node::Text(ref text) => text.abs_transform,
}
}
pub fn bounding_box(&self) -> Option<Rect> {
match self {
Node::Group(ref group) => group.bounding_box,
Node::Path(ref path) => path.bounding_box,
Node::Image(ref image) => image.bounding_box.map(|r| r.to_rect()),
Node::Text(ref text) => text.bounding_box.map(|r| r.to_rect()),
}
}
pub fn abs_bounding_box(&self) -> Option<Rect> {
self.bounding_box()?.transform(self.abs_transform())
}
pub fn stroke_bounding_box(&self) -> Option<NonZeroRect> {
match self {
Node::Group(ref group) => group.stroke_bounding_box,
Node::Path(ref path) => path.stroke_bounding_box,
Node::Image(ref image) => image.bounding_box,
Node::Text(ref text) => text.stroke_bounding_box,
}
}
pub fn abs_stroke_bounding_box(&self) -> Option<NonZeroRect> {
self.stroke_bounding_box()?.transform(self.abs_transform())
}
pub fn subroots<F: FnMut(&Group)>(&self, mut f: F) {
match self {
Node::Group(ref group) => group.subroots(&mut f),
Node::Path(ref path) => path.subroots(&mut f),
Node::Image(ref image) => image.subroots(&mut f),
Node::Text(ref text) => text.subroots(&mut f),
}
}
pub fn subroots_mut<F: FnMut(&mut Group)>(&mut self, mut f: F) {
match self {
Node::Group(ref mut group) => group.subroots_mut(&mut f),
Node::Path(ref mut path) => path.subroots_mut(&mut f),
Node::Image(ref mut image) => image.subroots_mut(&mut f),
Node::Text(ref mut text) => text.subroots_mut(&mut f),
}
}
}
#[derive(Clone, Debug)]
pub struct Group {
pub id: String,
pub transform: Transform,
pub abs_transform: Transform,
pub opacity: Opacity,
pub blend_mode: BlendMode,
pub isolate: bool,
pub clip_path: Option<SharedClipPath>,
pub mask: Option<SharedMask>,
pub filters: Vec<filter::SharedFilter>,
pub bounding_box: Option<Rect>,
pub stroke_bounding_box: Option<NonZeroRect>,
pub layer_bounding_box: Option<NonZeroRect>,
pub children: Vec<Node>,
}
impl Default for Group {
fn default() -> Self {
Group {
id: String::new(),
transform: Transform::default(),
abs_transform: Transform::default(),
opacity: Opacity::ONE,
blend_mode: BlendMode::Normal,
isolate: false,
clip_path: None,
mask: None,
filters: Vec::new(),
bounding_box: None,
stroke_bounding_box: None,
layer_bounding_box: None,
children: Vec::new(),
}
}
}
impl Group {
pub fn should_isolate(&self) -> bool {
self.isolate
|| self.opacity != Opacity::ONE
|| self.clip_path.is_some()
|| self.mask.is_some()
|| !self.filters.is_empty()
|| self.blend_mode != BlendMode::Normal }
pub fn has_children(&self) -> bool {
!self.children.is_empty()
}
pub fn abs_bounding_box(&self) -> Option<Rect> {
self.bounding_box?.transform(self.abs_transform)
}
pub fn filters_bounding_box(&self) -> Option<NonZeroRect> {
let mut full_region = BBox::default();
for filter in &self.filters {
let mut region = filter.borrow().rect;
if filter.borrow().units == Units::ObjectBoundingBox {
let object_bbox = self.bounding_box.and_then(|bbox| bbox.to_non_zero_rect());
if let Some(object_bbox) = object_bbox {
region = region.bbox_transform(object_bbox);
} else {
continue;
}
}
full_region = full_region.expand(BBox::from(region));
}
full_region.to_non_zero_rect()
}
pub fn abs_filters_bounding_box(&self) -> Option<NonZeroRect> {
self.filters_bounding_box()?.transform(self.abs_transform)
}
fn subroots(&self, f: &mut dyn FnMut(&Group)) {
if let Some(ref clip) = self.clip_path {
f(&clip.borrow().root);
if let Some(ref sub_clip) = clip.borrow().clip_path {
f(&sub_clip.borrow().root);
}
}
if let Some(ref mask) = self.mask {
f(&mask.borrow().root);
if let Some(ref sub_mask) = mask.borrow().mask {
f(&sub_mask.borrow().root);
}
}
for filter in &self.filters {
for primitive in &filter.borrow().primitives {
if let filter::Kind::Image(ref image) = primitive.kind {
if let filter::ImageKind::Use(ref use_node) = image.data {
f(use_node);
}
}
}
}
}
fn subroots_mut(&mut self, f: &mut dyn FnMut(&mut Group)) {
if let Some(ref clip) = self.clip_path {
f(&mut clip.borrow_mut().root);
if let Some(ref sub_clip) = clip.borrow().clip_path {
f(&mut sub_clip.borrow_mut().root);
}
}
if let Some(ref mask) = self.mask {
f(&mut mask.borrow_mut().root);
if let Some(ref sub_mask) = mask.borrow_mut().mask {
f(&mut sub_mask.borrow_mut().root);
}
}
for filter in &mut self.filters {
for primitive in &mut filter.borrow_mut().primitives {
if let filter::Kind::Image(ref mut image) = primitive.kind {
if let filter::ImageKind::Use(ref mut use_node) = image.data {
f(use_node);
}
}
}
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[allow(missing_docs)]
pub enum PaintOrder {
FillAndStroke,
StrokeAndFill,
}
impl Default for PaintOrder {
fn default() -> Self {
Self::FillAndStroke
}
}
#[derive(Clone, Debug)]
pub struct Path {
pub id: String,
pub visibility: Visibility,
pub fill: Option<Fill>,
pub stroke: Option<Stroke>,
pub paint_order: PaintOrder,
pub rendering_mode: ShapeRendering,
pub data: Rc<tiny_skia_path::Path>,
pub abs_transform: Transform,
pub bounding_box: Option<Rect>,
pub stroke_bounding_box: Option<NonZeroRect>,
}
impl Path {
pub fn new(data: Rc<tiny_skia_path::Path>) -> Self {
Path {
id: String::new(),
visibility: Visibility::Visible,
fill: None,
stroke: None,
paint_order: PaintOrder::default(),
rendering_mode: ShapeRendering::default(),
data,
abs_transform: Transform::default(),
bounding_box: None,
stroke_bounding_box: None,
}
}
pub fn calculate_stroke_bounding_box(&self) -> Option<NonZeroRect> {
let stroke = self.stroke.as_ref()?;
let mut stroke = stroke.to_tiny_skia();
stroke.dash = None;
if let Some(stroked_path) = self.data.stroke(&stroke, 1.0) {
return stroked_path
.compute_tight_bounds()
.and_then(|r| r.to_non_zero_rect());
}
None
}
fn subroots(&self, f: &mut dyn FnMut(&Group)) {
if let Some(Paint::Pattern(ref patt)) = self.fill.as_ref().map(|f| &f.paint) {
f(&patt.borrow().root)
}
if let Some(Paint::Pattern(ref patt)) = self.stroke.as_ref().map(|f| &f.paint) {
f(&patt.borrow().root)
}
}
fn subroots_mut(&mut self, f: &mut dyn FnMut(&mut Group)) {
if let Some(Paint::Pattern(ref mut patt)) = self.fill.as_mut().map(|f| &mut f.paint) {
f(&mut patt.borrow_mut().root)
}
if let Some(Paint::Pattern(ref mut patt)) = self.stroke.as_mut().map(|f| &mut f.paint) {
f(&mut patt.borrow_mut().root)
}
}
}
#[derive(Clone)]
pub enum ImageKind {
JPEG(Arc<Vec<u8>>),
PNG(Arc<Vec<u8>>),
GIF(Arc<Vec<u8>>),
SVG(Tree),
}
impl std::fmt::Debug for ImageKind {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
ImageKind::JPEG(_) => f.write_str("ImageKind::JPEG(..)"),
ImageKind::PNG(_) => f.write_str("ImageKind::PNG(..)"),
ImageKind::GIF(_) => f.write_str("ImageKind::GIF(..)"),
ImageKind::SVG(_) => f.write_str("ImageKind::SVG(..)"),
}
}
}
#[derive(Clone, Debug)]
pub struct Image {
pub id: String,
pub visibility: Visibility,
pub view_box: ViewBox,
pub rendering_mode: ImageRendering,
pub kind: ImageKind,
pub abs_transform: Transform,
pub bounding_box: Option<NonZeroRect>,
}
impl Image {
fn subroots(&self, f: &mut dyn FnMut(&Group)) {
if let ImageKind::SVG(ref tree) = self.kind {
f(&tree.root)
}
}
fn subroots_mut(&mut self, f: &mut dyn FnMut(&mut Group)) {
if let ImageKind::SVG(ref mut tree) = self.kind {
f(&mut tree.root)
}
}
}
#[allow(missing_debug_implementations)]
#[derive(Clone, Debug)]
pub struct Tree {
pub size: Size,
pub view_box: ViewBox,
pub root: Group,
}
impl Tree {
pub fn node_by_id(&self, id: &str) -> Option<&Node> {
if id.is_empty() {
return None;
}
node_by_id(&self.root, id)
}
pub fn has_text_nodes(&self) -> bool {
has_text_nodes(&self.root)
}
pub fn paint_servers<F: FnMut(&Paint)>(&self, mut f: F) {
loop_over_paint_servers(&self.root, &mut f)
}
pub fn clip_paths<F: FnMut(SharedClipPath)>(&self, mut f: F) {
loop_over_clip_paths(&self.root, &mut f)
}
pub fn masks<F: FnMut(SharedMask)>(&self, mut f: F) {
loop_over_masks(&self.root, &mut f)
}
pub fn filters<F: FnMut(filter::SharedFilter)>(&self, mut f: F) {
loop_over_filters(&self.root, &mut f)
}
pub fn calculate_abs_transforms(&mut self) {
self.root.calculate_abs_transforms(Transform::identity());
}
pub fn calculate_bounding_boxes(&mut self) {
self.root.calculate_bounding_boxes();
}
}
fn node_by_id<'a>(parent: &'a Group, id: &str) -> Option<&'a Node> {
for child in &parent.children {
if child.id() == id {
return Some(child);
}
if let Node::Group(ref g) = child {
if let Some(n) = node_by_id(g, id) {
return Some(n);
}
}
}
None
}
fn has_text_nodes(root: &Group) -> bool {
for node in &root.children {
if let Node::Text(_) = node {
return true;
}
let mut has_text = false;
if let Node::Image(ref image) = node {
if let ImageKind::SVG(ref tree) = image.kind {
if has_text_nodes(&tree.root) {
has_text = true;
}
}
}
node.subroots(|subroot| has_text |= has_text_nodes(subroot));
if has_text {
return true;
}
}
true
}
fn loop_over_paint_servers(parent: &Group, f: &mut dyn FnMut(&Paint)) {
fn push(paint: Option<&Paint>, f: &mut dyn FnMut(&Paint)) {
if let Some(paint) = paint {
f(paint);
}
}
for node in &parent.children {
match node {
Node::Group(ref group) => loop_over_paint_servers(group, f),
Node::Path(ref path) => {
push(path.fill.as_ref().map(|f| &f.paint), f);
push(path.stroke.as_ref().map(|f| &f.paint), f);
}
Node::Image(_) => {}
Node::Text(ref text) => {
if text.flattened.is_none() {
for chunk in &text.chunks {
for span in &chunk.spans {
push(span.fill.as_ref().map(|f| &f.paint), f);
push(span.stroke.as_ref().map(|f| &f.paint), f);
if let Some(ref underline) = span.decoration.underline {
push(underline.fill.as_ref().map(|f| &f.paint), f);
push(underline.stroke.as_ref().map(|f| &f.paint), f);
}
if let Some(ref overline) = span.decoration.overline {
push(overline.fill.as_ref().map(|f| &f.paint), f);
push(overline.stroke.as_ref().map(|f| &f.paint), f);
}
if let Some(ref line_through) = span.decoration.line_through {
push(line_through.fill.as_ref().map(|f| &f.paint), f);
push(line_through.stroke.as_ref().map(|f| &f.paint), f);
}
}
}
}
}
}
node.subroots(|subroot| loop_over_paint_servers(subroot, f));
}
}
fn loop_over_clip_paths(parent: &Group, f: &mut dyn FnMut(SharedClipPath)) {
for node in &parent.children {
if let Node::Group(ref g) = node {
if let Some(ref clip) = g.clip_path {
f(clip.clone());
if let Some(ref sub_clip) = clip.borrow().clip_path {
f(sub_clip.clone());
}
}
}
node.subroots(|subroot| loop_over_clip_paths(subroot, f));
if let Node::Group(ref g) = node {
loop_over_clip_paths(g, f);
}
}
}
fn loop_over_masks(parent: &Group, f: &mut dyn FnMut(SharedMask)) {
for node in &parent.children {
if let Node::Group(ref g) = node {
if let Some(ref mask) = g.mask {
f(mask.clone());
if let Some(ref sub_mask) = mask.borrow().mask {
f(sub_mask.clone());
}
}
loop_over_masks(g, f);
}
node.subroots(|subroot| loop_over_masks(subroot, f));
if let Node::Group(ref g) = node {
loop_over_masks(g, f);
}
}
}
fn loop_over_filters(parent: &Group, f: &mut dyn FnMut(filter::SharedFilter)) {
for node in &parent.children {
if let Node::Group(ref g) = node {
for filter in &g.filters {
f(filter.clone());
}
}
node.subroots(|subroot| loop_over_filters(subroot, f));
if let Node::Group(ref g) = node {
loop_over_filters(g, f);
}
}
}
impl Group {
pub fn calculate_abs_transforms(&mut self, transform: Transform) {
for node in &mut self.children {
match node {
Node::Group(ref mut group) => {
let abs_ts = transform.pre_concat(group.transform);
group.abs_transform = abs_ts;
group.calculate_abs_transforms(abs_ts);
}
Node::Path(ref mut path) => path.abs_transform = transform,
Node::Image(ref mut image) => image.abs_transform = transform,
Node::Text(ref mut text) => text.abs_transform = transform,
}
node.subroots_mut(|root| root.calculate_abs_transforms(Transform::identity()));
}
}
pub fn calculate_bounding_boxes(&mut self) {
for node in &mut self.children {
match node {
Node::Path(ref mut path) => {
path.bounding_box = path.data.compute_tight_bounds();
path.stroke_bounding_box = path.calculate_stroke_bounding_box();
if path.stroke_bounding_box.is_none() {
path.stroke_bounding_box =
path.bounding_box.and_then(|r| r.to_non_zero_rect());
}
}
Node::Image(ref mut image) => image.bounding_box = Some(image.view_box.rect),
Node::Group(ref mut group) => {
group.calculate_bounding_boxes();
}
Node::Text(_) => {}
}
node.subroots_mut(|root| root.calculate_bounding_boxes());
}
let mut bbox = BBox::default();
let mut stroke_bbox = BBox::default();
let mut layer_bbox = BBox::default();
for child in &self.children {
if let Some(mut c_bbox) = child.bounding_box() {
if let Node::Group(ref group) = child {
if let Some(r) = c_bbox.transform(group.transform) {
c_bbox = r;
}
}
bbox = bbox.expand(c_bbox);
}
if let Some(mut c_bbox) = child.stroke_bounding_box() {
if let Node::Group(ref group) = child {
if let Some(r) = c_bbox.transform(group.transform) {
c_bbox = r;
}
}
stroke_bbox = stroke_bbox.expand(c_bbox);
}
if let Node::Group(ref group) = child {
if let Some(r) = group.layer_bounding_box {
if let Some(r) = r.transform(group.transform) {
layer_bbox = layer_bbox.expand(r);
}
}
} else if let Some(c_bbox) = child.stroke_bounding_box() {
layer_bbox = layer_bbox.expand(c_bbox);
}
}
self.bounding_box = bbox.to_rect();
self.stroke_bounding_box = stroke_bbox.to_non_zero_rect();
if let Some(filter_bbox) = self.filters_bounding_box() {
self.layer_bounding_box = Some(filter_bbox);
} else {
self.layer_bounding_box = layer_bbox.to_non_zero_rect();
}
}
}