diff --git a/crates/resvg/tests/integration/main.rs b/crates/resvg/tests/integration/main.rs index 602686c5a..8dcf40363 100644 --- a/crates/resvg/tests/integration/main.rs +++ b/crates/resvg/tests/integration/main.rs @@ -35,14 +35,17 @@ pub fn render(name: &str) -> usize { let tree = { let svg_data = std::fs::read(&svg_path).unwrap(); // TODO: Revert this + let db = GLOBAL_FONTDB.lock().unwrap(); + let mut original_tree = usvg::Tree::from_data(&svg_data, &opt) + .unwrap(); + original_tree.convert_text(&db); + let mut tree = usvg::Tree::from_str( - &usvg::Tree::from_data(&svg_data, &opt) - .unwrap() + &original_tree .to_string(&XmlOptions::default()), &opt, ) .unwrap(); - let db = GLOBAL_FONTDB.lock().unwrap(); tree.convert_text(&db); tree }; diff --git a/crates/usvg-parser/src/text.rs b/crates/usvg-parser/src/text.rs index 6494a5b47..7e060f8a5 100644 --- a/crates/usvg-parser/src/text.rs +++ b/crates/usvg-parser/src/text.rs @@ -367,7 +367,11 @@ fn resolve_text_flow(node: SvgNode, state: &converter::State) -> Option Font { diff --git a/crates/usvg-tree/src/lib.rs b/crates/usvg-tree/src/lib.rs index a88bcaf78..56c15169e 100644 --- a/crates/usvg-tree/src/lib.rs +++ b/crates/usvg-tree/src/lib.rs @@ -1297,7 +1297,11 @@ impl NodeExt for Node { fn calc_node_bbox(node: &Node, ts: Transform) -> Option { match *node.borrow() { - NodeKind::Path(ref path) => path.data.compute_tight_bounds()?.transform(ts).map(BBox::from), + NodeKind::Path(ref path) => path + .data + .compute_tight_bounds()? + .transform(ts) + .map(BBox::from), NodeKind::Image(ref img) => img.view_box.rect.transform(ts).map(BBox::from), NodeKind::Group(_) => { let mut bbox = BBox::default(); diff --git a/crates/usvg/src/lib.rs b/crates/usvg/src/lib.rs index 25e0e62a9..9f753daf9 100644 --- a/crates/usvg/src/lib.rs +++ b/crates/usvg/src/lib.rs @@ -71,4 +71,3 @@ impl TreeWriting for usvg_tree::Tree { writer::convert(self, opt) } } - diff --git a/crates/usvg/src/writer.rs b/crates/usvg/src/writer.rs index 667b22f66..b83243c72 100644 --- a/crates/usvg/src/writer.rs +++ b/crates/usvg/src/writer.rs @@ -71,6 +71,9 @@ struct WriterContext<'a> { next_linear_gradient_index: usize, next_radial_gradient_index: usize, next_pattern_index: usize, + next_path_index: usize, + + text_path_map: HashMap, } impl WriterContext<'_> { @@ -129,6 +132,12 @@ impl WriterContext<'_> { id } + fn gen_path_id(&mut self) -> String { + let (new_index, id) = self.gen_id("path", self.next_path_index); + self.next_path_index = new_index; + id + } + fn push_defs_id(&mut self, node: &Rc, id: String) { let key = Rc::as_ptr(node) as usize; if !self.id_map.contains_key(&key) { @@ -180,6 +189,8 @@ pub(crate) fn convert(tree: &Tree, opt: &XmlOptions) -> String { next_linear_gradient_index: 0, next_radial_gradient_index: 0, next_pattern_index: 0, + next_path_index: 0, + text_path_map: HashMap::new(), }; collect_ids(tree, &mut ctx); @@ -722,6 +733,30 @@ fn conv_defs(tree: &Tree, ctx: &mut WriterContext, xml: &mut XmlWriter) { xml.end_element(); } + + if tree.has_text_nodes() { + // TODO: doesn't check for text in patterns and masks... + for node in tree.root.descendants() { + if let NodeKind::Text(ref text) = *node.borrow() { + for chunk in &text.chunks { + if let TextFlow::Path(ref text_path) = chunk.text_flow { + let path = Path { + id: ctx.gen_path_id(), + data: text_path.path.clone(), + visibility: Visibility::default(), + fill: None, + stroke: None, + rendering_mode: ShapeRendering::default(), + paint_order: PaintOrder::default(), + }; + write_path(&path, false, Transform::default(), None, ctx, xml); + ctx.text_path_map + .insert(text_path.id.clone(), path.id.clone()); + } + } + } + } + } } fn conv_elements(parent: &Node, is_clip_path: bool, ctx: &mut WriterContext, xml: &mut XmlWriter) { @@ -893,19 +928,11 @@ fn conv_element(node: &Node, is_clip_path: bool, ctx: &mut WriterContext, xml: & } if !text.dx.is_empty() && text.dx.iter().any(|dx| *dx != 0.0) { - xml.write_numbers( - AId::Dx, - &text - .dx - ); + xml.write_numbers(AId::Dx, &text.dx); } if !text.dy.is_empty() && text.dy.iter().any(|dy| *dy != 0.0) { - xml.write_numbers( - AId::Dy, - &text - .dy - ); + xml.write_numbers(AId::Dy, &text.dy); } xml.set_preserve_whitespaces(true); @@ -915,10 +942,10 @@ fn conv_element(node: &Node, is_clip_path: bool, ctx: &mut WriterContext, xml: & xml.start_svg_element(EId::TextPath); xml.write_attribute_raw("xlink:href", |buf| { - // let ref_path = writer_context.text_path_map.pop().unwrap(); - // let prefix = writer_context.opt.id_prefix.as_deref().unwrap_or_default(); - // let url = format!("#{}{}", prefix, ref_path); - // buf.extend_from_slice(url.as_bytes()); + let ref_path = ctx.text_path_map.get(&text_path.id).unwrap(); + let prefix = ctx.opt.id_prefix.as_deref().unwrap_or_default(); + let url = format!("#{}{}", prefix, ref_path); + buf.extend_from_slice(url.as_bytes()); }); if text_path.start_offset != 0.0 { @@ -964,11 +991,11 @@ fn conv_element(node: &Node, is_clip_path: bool, ctx: &mut WriterContext, xml: & ("line-through", &span.decoration.line_through), ("overline", &span.decoration.overline), ] - .iter() - .filter_map(|&(key, option_value)| { - option_value.as_ref().map(|value| (key, value)) - }) - .collect(); + .iter() + .filter_map(|&(key, option_value)| { + option_value.as_ref().map(|value| (key, value)) + }) + .collect(); for (deco_name, deco) in &decorations { xml.start_svg_element(EId::Tspan); @@ -1656,25 +1683,25 @@ fn write_span( DominantBaseline::Alphabetic => "alphabetic", DominantBaseline::Hanging => "hanging", DominantBaseline::Mathematical => "mathematical", - DominantBaseline::Auto => unreachable!() + DominantBaseline::Auto => unreachable!(), }; xml.write_svg_attribute(AId::DominantBaseline, name); } if span.alignment_baseline != AlignmentBaseline::Auto { let name = match span.alignment_baseline { - AlignmentBaseline::Baseline => "baseline", + AlignmentBaseline::Baseline => "baseline", AlignmentBaseline::BeforeEdge => "before-edge", - AlignmentBaseline::TextBeforeEdge => "text-before-edge", - AlignmentBaseline::Middle => "middle", - AlignmentBaseline::Central => "central", + AlignmentBaseline::TextBeforeEdge => "text-before-edge", + AlignmentBaseline::Middle => "middle", + AlignmentBaseline::Central => "central", AlignmentBaseline::AfterEdge => "after-edge", AlignmentBaseline::TextAfterEdge => "text-after-edge", AlignmentBaseline::Ideographic => "ideographic", AlignmentBaseline::Alphabetic => "alphabetic", - AlignmentBaseline::Hanging => "hanging", + AlignmentBaseline::Hanging => "hanging", AlignmentBaseline::Mathematical => "mathematical", - AlignmentBaseline::Auto => unreachable!() + AlignmentBaseline::Auto => unreachable!(), }; xml.write_svg_attribute(AId::AlignmentBaseline, name); }