Skip to content

Commit

Permalink
Merge with main (text path still needs to be implemented)
Browse files Browse the repository at this point in the history
  • Loading branch information
LaurenzV committed Dec 5, 2023
1 parent 7ad3388 commit cc98d68
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 153 deletions.
133 changes: 1 addition & 132 deletions crates/usvg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,26 +53,13 @@ and can focus just on the rendering part.

mod writer;

use std::collections::HashSet;
pub use usvg_parser::*;
#[cfg(feature = "text")]
pub use usvg_text_layout::*;
pub use usvg_tree::*;

pub use writer::XmlOptions;

/// A struct to keep track of data necessary during writing the SVG file.
pub(crate) struct WriterContext<'a> {
/// The tree that is to be written.
pub tree: &'a Tree,
/// Options for the XML output.
pub opt: &'a XmlOptions,
/// A vector of IDs of the corresponding path of textPaths, in tree order.
pub text_path_map: Vec<String>,
/// A generator for new IDs.
pub id_generator: &'a mut IdGenerator,
}

/// A trait to write `usvg::Tree` back to SVG.
pub trait TreeWriting {
/// Writes `usvg::Tree` back to SVG.
Expand All @@ -81,125 +68,7 @@ pub trait TreeWriting {

impl TreeWriting for usvg_tree::Tree {
fn to_string(&self, opt: &XmlOptions) -> String {
let used_ids = collect_ids(self);
let mut id_generator = IdGenerator::new(used_ids);

let mut writer_context = WriterContext {
tree: &self,
opt,
text_path_map: Vec::new(),
id_generator: &mut id_generator,
};

writer::convert(&mut writer_context)
writer::convert(self, opt)
}
}

/// A generator for new IDs.
#[derive(Default)]
pub(crate) struct IdGenerator {
/// A set of IDs that should never be generated by the generator.
ids: HashSet<String>,
/// The counter for the current path ID.
path: u64,
}

impl IdGenerator {
pub fn new(ids: HashSet<String>) -> Self {
Self {
ids,
..Self::default()
}
}

pub fn bump_path(&mut self) -> String {
IdGenerator::bump_impl(&self.ids, &mut self.path, "p")
}

fn bump_impl(ids: &HashSet<String>, field: &mut u64, format_str: &str) -> String {
let mut bump = || {
*field += 1;
let result = format!("{}{}", format_str, field);
result
};

let mut id = bump();

while ids.contains(&id) {
id = bump();
}

id
}
}

fn collect_ids(tree: &Tree) -> HashSet<String> {
let mut ids = HashSet::new();
collect_ids_impl(&tree.root, &mut ids);
ids.remove("");
ids
}

fn collect_ids_impl(node: &Node, ids: &mut HashSet<String>) {
let get_paint_id = |paint: &Paint| match paint {
Paint::Color(_) => None,
Paint::LinearGradient(lg) => Some(lg.id.clone()),
Paint::RadialGradient(rg) => Some(rg.id.clone()),
Paint::Pattern(pattern) => Some(pattern.id.clone()),
};

for node in node.descendants() {
match *node.borrow() {
NodeKind::Path(ref path) => {
ids.insert(path.id.clone());
if let Some(ref fill) = path.fill {
ids.insert(get_paint_id(&fill.paint).unwrap_or_default());
}
if let Some(ref stroke) = path.stroke {
ids.insert(get_paint_id(&stroke.paint).unwrap_or_default());
}
}
NodeKind::Image(ref image) => {
ids.insert(image.id.clone());
}
NodeKind::Text(ref text) => {
ids.insert(text.id.clone());
for chunk in &text.chunks {
for span in &chunk.spans {
if let Some(ref fill) = span.fill {
ids.insert(get_paint_id(&fill.paint).unwrap_or_default());
}
if let Some(ref stroke) = span.stroke {
ids.insert(get_paint_id(&stroke.paint).unwrap_or_default());
}
}
}
}
NodeKind::Group(ref group) => {
ids.insert(group.id.clone());

for filter in &group.filters {
ids.insert(filter.id.clone());
}

if let Some(mask) = &group.mask {
ids.insert(mask.id.clone());

if let Some(mask) = &mask.mask {
ids.insert(mask.id.clone());
}
}

if let Some(clip_path) = &group.clip_path {
ids.insert(clip_path.id.clone());

if let Some(clip_path) = &clip_path.clip_path {
ids.insert(clip_path.id.clone());
}
}
}
}

node.subroots(|node| collect_ids_impl(&node, ids));
}
}
36 changes: 15 additions & 21 deletions crates/usvg/src/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,7 @@ fn conv_element(node: &Node, is_clip_path: bool, ctx: &mut WriterContext, xml: &
xml.start_svg_element(EId::Text);

if !text.id.is_empty() {
xml.write_id_attribute(&text.id, writer_context.opt);
xml.write_id_attribute(&text.id, ctx);
}

xml.write_attribute("xml:space", "preserve");
Expand All @@ -892,25 +892,19 @@ fn conv_element(node: &Node, is_clip_path: bool, ctx: &mut WriterContext, xml: &
xml.write_numbers(AId::Rotate, &text.rotate);
}

if !text.positions.is_empty() && text.positions.iter().any(|c| c.dx.is_some()) {
if !text.dx.is_empty() && text.dx.iter().any(|dx| *dx != 0.0) {
xml.write_numbers(
AId::Dx,
&text
.positions
.iter()
.map(|c| c.dx.unwrap_or_default())
.collect::<Vec<_>>(),
.dx
);
}

if !text.positions.is_empty() && text.positions.iter().any(|c| c.dy.is_some()) {
if !text.dy.is_empty() && text.dy.iter().any(|dy| *dy != 0.0) {
xml.write_numbers(
AId::Dy,
&text
.positions
.iter()
.map(|c| c.dy.unwrap_or_default())
.collect::<Vec<_>>(),
.dy
);
}

Expand All @@ -921,10 +915,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 = 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());
});

if text_path.start_offset != 0.0 {
Expand Down Expand Up @@ -979,12 +973,12 @@ fn conv_element(node: &Node, is_clip_path: bool, ctx: &mut WriterContext, xml: &
for (deco_name, deco) in &decorations {
xml.start_svg_element(EId::Tspan);
xml.write_svg_attribute(AId::TextDecoration, deco_name);
write_fill(&deco.fill, false, writer_context.opt, xml);
write_stroke(&deco.stroke, writer_context.opt, xml);
write_fill(&deco.fill, false, ctx, xml);
write_stroke(&deco.stroke, ctx, xml);
}

// Writes the remaining attributes of a span
write_span(is_clip_path, writer_context, xml, chunk, span);
write_span(is_clip_path, ctx, xml, chunk, span);

// End for each tspan we needed to create for decorations
for _ in &decorations {
Expand Down Expand Up @@ -1594,7 +1588,7 @@ fn write_num(num: f32, buf: &mut Vec<u8>, precision: u8) {
/// Write all of the tspan attributes except for baseline_shift and decorations.
fn write_span(
is_clip_path: bool,
writer_context: &mut WriterContext,
ctx: &mut WriterContext,
xml: &mut XmlWriter,
chunk: &TextChunk,
span: &TextSpan,
Expand Down Expand Up @@ -1685,8 +1679,8 @@ fn write_span(
xml.write_svg_attribute(AId::AlignmentBaseline, name);
}

write_fill(&span.fill, is_clip_path, writer_context.opt, xml);
write_stroke(&span.stroke, writer_context.opt, xml);
write_fill(&span.fill, is_clip_path, ctx, xml);
write_stroke(&span.stroke, ctx, xml);

let cur_text = &chunk.text[span.start..span.end];

Expand Down

0 comments on commit cc98d68

Please sign in to comment.