// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. use super::prelude::*; // Emulate a new viewport via clipPath. // // From: // // // // To: // // // // // // // // pub fn clip_element(doc: &mut Document, target_node: &mut Node) -> Option { // No need to clip elements with overflow:visible. { let attrs = target_node.attributes(); let overflow = attrs.get_str_or(AId::Overflow, "hidden"); if overflow != "hidden" && overflow != "scroll" { return None; } } if let Some(clip_rect) = get_clip_rect(doc, target_node) { // We can't set `clip-path` on the element itself, // because it will be affected by a possible transform. // So we have to create an additional group. let mut g_node = doc.create_element(EId::G); target_node.insert_before(g_node.clone()); target_node.detach(); g_node.append(target_node.clone()); let mut clip_node = doc.create_element(EId::ClipPath); clip_node.set_id(gen_clip_path_id(doc)); clip_node.set_attribute((AId::ClipPathUnits, "userSpaceOnUse")); g_node.insert_before(clip_node.clone()); let mut rect_node = doc.create_element(EId::Rect); rect_node.set_attribute((AId::X, clip_rect.x)); rect_node.set_attribute((AId::Y, clip_rect.y)); rect_node.set_attribute((AId::Width, clip_rect.width)); rect_node.set_attribute((AId::Height, clip_rect.height)); clip_node.append(rect_node); g_node.set_attribute((AId::ClipPath, clip_node.clone())); Some(g_node) } else { None } } fn get_clip_rect(doc: &Document, node: &Node) -> Option { let (x, y, w, h) = { let attrs = node.attributes(); let x = attrs.get_number(AId::X)?; let y = attrs.get_number(AId::Y)?; let w = attrs.get_number(AId::Width)?; let h = attrs.get_number(AId::Height)?; (x, y, w, h) }; let svg = doc.svg_element()?; let svg_w = svg.attributes().get_number(AId::Width)?; let svg_h = svg.attributes().get_number(AId::Height)?; // Clip rect is not needed when it has the same size as a whole image. if w.fuzzy_eq(&svg_w) && h.fuzzy_eq(&svg_h) { return None; } Some((x, y, w, h).into()) } /// Creates a free id for `clipPath`. fn gen_clip_path_id(doc: &Document) -> String { // TODO: speedup let mut idx = 1; let mut id = format!("clipPath{}", idx); while doc.root().descendants().any(|n| *n.id() == id) { idx += 1; id = format!("clipPath{}", idx); } id }