diff --git a/Cargo.lock b/Cargo.lock index ba7cd7460..9653958e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1242,10 +1242,10 @@ dependencies = [ [[package]] name = "craft-idml" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=8345921af#8345921aff971504d55cc202b93d2ca6aa5932de" +source = "git+https://github.com/storytold/craft-libs?rev=3d82c75d5d0ffcbb7bb39aff687f69ce8c4a713c#3d82c75d5d0ffcbb7bb39aff687f69ce8c4a713c" dependencies = [ - "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=8345921af)", - "craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=8345921af)", + "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=3d82c75d5d0ffcbb7bb39aff687f69ce8c4a713c)", + "craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=3d82c75d5d0ffcbb7bb39aff687f69ce8c4a713c)", "thiserror 2.0.21", ] @@ -1647,7 +1647,7 @@ dependencies = [ [[package]] name = "craft-xml" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=40525913633942f84b06d4837a8d90534a55d63f#40525913633942f84b06d4837a8d90534a55d63f" +source = "git+https://github.com/storytold/craft-libs?rev=3d82c75d5d0ffcbb7bb39aff687f69ce8c4a713c#3d82c75d5d0ffcbb7bb39aff687f69ce8c4a713c" dependencies = [ "quick-xml 0.41.0", "thiserror 2.0.21", @@ -1656,7 +1656,7 @@ dependencies = [ [[package]] name = "craft-xml" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=8345921af#8345921aff971504d55cc202b93d2ca6aa5932de" +source = "git+https://github.com/storytold/craft-libs?rev=40525913633942f84b06d4837a8d90534a55d63f#40525913633942f84b06d4837a8d90534a55d63f" dependencies = [ "quick-xml 0.41.0", "thiserror 2.0.21", @@ -1730,7 +1730,7 @@ dependencies = [ [[package]] name = "craft-zip" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=8345921af#8345921aff971504d55cc202b93d2ca6aa5932de" +source = "git+https://github.com/storytold/craft-libs?rev=3d82c75d5d0ffcbb7bb39aff687f69ce8c4a713c#3d82c75d5d0ffcbb7bb39aff687f69ce8c4a713c" dependencies = [ "flate2", "thiserror 2.0.21", @@ -2021,7 +2021,7 @@ dependencies = [ "craft-prepress", "craft-print", "craft-svg 0.1.0 (git+https://github.com/storytold/craft-libs?rev=cc23ffd4d26f3074808e2187d332564817624f8f)", - "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=8345921af)", + "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=3d82c75d5d0ffcbb7bb39aff687f69ce8c4a713c)", "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=f23b0f33f0d00b157476533a43cbbcfd901aa1f1)", "designcraft-color", "designcraft-compose", @@ -2103,9 +2103,9 @@ name = "designcraft-idml" version = "0.20.1" dependencies = [ "craft-file-formats", - "craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=8345921af)", + "craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=3d82c75d5d0ffcbb7bb39aff687f69ce8c4a713c)", "craft-svg 0.1.0 (git+https://github.com/storytold/craft-libs?rev=cc23ffd4d26f3074808e2187d332564817624f8f)", - "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=8345921af)", + "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=3d82c75d5d0ffcbb7bb39aff687f69ce8c4a713c)", "designcraft-color", "designcraft-compose", "designcraft-doc", @@ -2222,7 +2222,7 @@ version = "0.20.1" dependencies = [ "craft-docx", "craft-file-formats", - "craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=8345921af)", + "craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=3d82c75d5d0ffcbb7bb39aff687f69ce8c4a713c)", "craft-rtf", "craft-wordmodel", "craft-xlsx", diff --git a/Cargo.toml b/Cargo.toml index 83bbe7ebe..70e4c9a2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,10 +16,10 @@ craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "4052591363 craft-designcraft-format = { git = "https://github.com/storytold/craft-libs", rev = "19692b6190465671e41967c75d0cd4c439d0c84c", version = "=0.1.0" } craft-eps = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" } craft-epub = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" } -craft-idml = { git = "https://github.com/storytold/craft-libs", rev = "8345921af", version = "=0.1.0" } +craft-idml = { git = "https://github.com/storytold/craft-libs", rev = "3d82c75d5d0ffcbb7bb39aff687f69ce8c4a713c", version = "=0.1.0" } craft-indd = { git = "https://github.com/storytold/craft-libs", rev = "47bf5650b0d503b97bfb5e841746c4957897d11f", version = "=0.1.0" } craft-svg = { git = "https://github.com/storytold/craft-libs", rev = "cc23ffd4d26f3074808e2187d332564817624f8f", version = "=0.1.0", features = ["tree-render", "tree-text"] } -craft-xml = { git = "https://github.com/storytold/craft-libs", rev = "8345921af", version = "=0.1.0" } +craft-xml = { git = "https://github.com/storytold/craft-libs", rev = "3d82c75d5d0ffcbb7bb39aff687f69ce8c4a713c", version = "=0.1.0" } craft-xml-dtd = { package = "craft-xml", git = "https://github.com/storytold/craft-libs", rev = "f23b0f33f0d00b157476533a43cbbcfd901aa1f1", version = "=0.1.0" } craft-rtf = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" } craft-xlsx = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" } diff --git a/crates/idml/src/export/item.rs b/crates/idml/src/export/item.rs index 4bab2248b..4fde416a4 100644 --- a/crates/idml/src/export/item.rs +++ b/crates/idml/src/export/item.rs @@ -1,7 +1,6 @@ use super::common::{affine_s, bool_s, p, pct, uid, with_props}; use super::{ - Affine, ColumnsKind, Content, El, Ex, HashSet, Item, Node, OBJECT_BUILTINS, PathData, Shape, TextFrameOptions, base64_encode, escape_id, names, - num, pt, + Affine, ColumnsKind, Content, El, Ex, HashSet, Item, Node, OBJECT_BUILTINS, PathData, Shape, TextFrameOptions, base64_encode, names, num, pt, }; impl<'a> Ex<'a> { @@ -324,49 +323,6 @@ impl<'a> Ex<'a> { } } -/// Most nested style folders one style name can create. -const MAX_STYLE_GROUP_DEPTH: usize = 32; - -/// Style folder tree (names `A/B/C` → groups A, B). -#[derive(Default)] -pub(super) struct Group { - styles: Vec, - groups: Vec<(String, Group)>, -} - -impl Group { - pub(super) fn insert(&mut self, name: &str, el: El) { - let builtin = name.starts_with('['); - let mut segs: Vec<&str> = if builtin { vec![name] } else { name.split('/').collect() }; - let _leaf = segs.pop(); - let mut g = self; - // Deeper folders than this are not made (the style lands in the deepest one), so the recursive `emit` is bounded. - for s in segs.into_iter().take(MAX_STYLE_GROUP_DEPTH) { - let i = match g.groups.iter().position(|(n, _)| n == s) { - Some(i) => i, - None => { - g.groups.push((s.to_string(), Group::default())); - g.groups.len() - 1 - } - }; - g = &mut g.groups[i].1; - } - g.styles.push(el); - } - - pub(super) fn emit(self, parent: &mut El, kind: &str, path: &str) { - for s in self.styles { - parent.push(s); - } - for (name, g) in self.groups { - let full = if path.is_empty() { name.clone() } else { format!("{path}:{name}") }; - let mut ge = El::new(kind).attr("Self", format!("{kind}/{}", escape_id(&full))).attr("Name", &name); - g.emit(&mut ge, kind, &full); - parent.push(ge); - } - } -} - /// `rule_color` is the IDML reference of the column rule's swatch. /// `TextWrapPreference` of a page item or a placed graphic. fn text_wrap_pref(w: &designcraft_doc::TextWrap) -> El { @@ -522,20 +478,20 @@ pub(crate) fn path_to_uri(path: &str) -> String { #[cfg(test)] mod group_depth_tests { - use super::*; + use craft_idml::model::{StyleGroupWriter, StyleKind}; #[test] fn a_style_name_with_many_separators_makes_bounded_folders() { - let mut g = Group::default(); - g.insert(&"a/".repeat(10_000), El::new("S")); - let (mut depth, mut cur) = (0, &g); - while let Some((_, next)) = cur.groups.first() { + let mut group = StyleGroupWriter::new(StyleKind::Character, "root"); + let style = craft_idml::El::new("CharacterStyle"); + group.insert(&"a/".repeat(10_000), style); + let root = group.to_el(); + let mut depth = 0; + let mut current = root.elements().next(); + while let Some(el) = current.filter(|el| el.local() == "CharacterStyleGroup") { depth += 1; - cur = next; + current = el.elements().next(); } - assert_eq!(depth, MAX_STYLE_GROUP_DEPTH); - assert_eq!(cur.styles.len(), 1); - let mut root = El::new("Root"); - g.emit(&mut root, "K", ""); + assert_eq!(depth, 32); } } diff --git a/crates/idml/src/export/styles.rs b/crates/idml/src/export/styles.rs index 0965a351f..ff7c55872 100644 --- a/crates/idml/src/export/styles.rs +++ b/crates/idml/src/export/styles.rs @@ -1,5 +1,5 @@ use super::common::{bool_s, p, pct, pkg_root, with_props}; -use super::item::{Group, text_frame_pref}; +use super::item::text_frame_pref; use super::story::with_list_tab; use super::{ CHAR_BUILTINS, El, Ex, NO_PARA_ID, OBJECT_BUILTINS, PARA_BUILTINS, Rule, TOC_STYLE_ID, TOC_STYLE_NAME, document, escape_id, names, num, st, @@ -14,7 +14,7 @@ impl<'a> Ex<'a> { let mut root = pkg_root("Styles"); // Character styles. - let mut cg = Group::default(); + let mut cg = craft_idml::model::StyleGroupWriter::new(craft_idml::model::StyleKind::Character, self.fresh()); for cs in &styles.character { let id = names::style_self("CharacterStyle", CHAR_BUILTINS, &cs.name); let mut el = @@ -32,14 +32,12 @@ impl<'a> Ex<'a> { if let Some(t) = styles.export_tag(&cs.name, true) { el.push(self.export_tag_map(t)); } - cg.insert(&cs.name, el); + cg.insert(&cs.name, crate::xml::to_craft(&el)); } - let mut rc = El::new("RootCharacterStyleGroup").attr("Self", self.fresh()); - cg.emit(&mut rc, "CharacterStyleGroup", ""); - root.push(rc); + root.push(crate::xml::from_craft(cg.to_el())); // Paragraph styles. - let mut pg = Group::default(); + let mut pg = craft_idml::model::StyleGroupWriter::new(craft_idml::model::StyleKind::Paragraph, self.fresh()); for ps in &styles.paragraph { let id = names::style_self("ParagraphStyle", PARA_BUILTINS, &ps.name); let mut el = @@ -77,11 +75,9 @@ impl<'a> Ex<'a> { if let Some(t) = tag { el.push(self.export_tag_map(t)); } - pg.insert(&ps.name, el); + pg.insert(&ps.name, crate::xml::to_craft(&el)); } - let mut rp = El::new("RootParagraphStyleGroup").attr("Self", self.fresh()); - pg.emit(&mut rp, "ParagraphStyleGroup", ""); - root.push(rp); + root.push(crate::xml::from_craft(pg.to_el())); self.append_cell_styles(&mut root, &styles); self.append_table_styles(&mut root, &styles); @@ -97,7 +93,7 @@ impl<'a> Ex<'a> { } // Object styles. - let mut og = Group::default(); + let mut og = craft_idml::model::StyleGroupWriter::new(craft_idml::model::StyleKind::Object, self.fresh()); let mut have: Vec<&str> = styles.object.iter().map(|o| o.name.as_str()).collect(); let defaults = designcraft_doc::Styles::default(); let mut all: Vec = styles.object.clone(); @@ -157,11 +153,9 @@ impl<'a> Ex<'a> { .attr("EnableDirectionalFeather", "false") .attr("EnableGradientFeather", "false"), ); - og.insert(&os.name, el); + og.insert(&os.name, crate::xml::to_craft(&el)); } - let mut ro = El::new("RootObjectStyleGroup").attr("Self", self.fresh()); - og.emit(&mut ro, "ObjectStyleGroup", ""); - root.push(ro); + root.push(crate::xml::from_craft(og.to_el())); if let Some(toc) = d.toc.as_ref().filter(|t| d.story(t.story).is_some()) { root.push(self.toc_style_el(toc)); }