mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-14 14:22:39 +00:00
Use shared codec for placed graphic options
This commit is contained in:
5 files changed
+27
-100
No files matched your search
Generated
+9
-9
@@ -1242,10 +1242,10 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-idml"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=7435724d60a321fdee6d87b1aea6e0c7cc3dea2a#7435724d60a321fdee6d87b1aea6e0c7cc3dea2a"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=76d9d83769d2a53cf801b659e361c5d35d928e71#76d9d83769d2a53cf801b659e361c5d35d928e71"
|
||||
dependencies = [
|
||||
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=7435724d60a321fdee6d87b1aea6e0c7cc3dea2a)",
|
||||
"craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=7435724d60a321fdee6d87b1aea6e0c7cc3dea2a)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=76d9d83769d2a53cf801b659e361c5d35d928e71)",
|
||||
"craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=76d9d83769d2a53cf801b659e361c5d35d928e71)",
|
||||
"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=7435724d60a321fdee6d87b1aea6e0c7cc3dea2a#7435724d60a321fdee6d87b1aea6e0c7cc3dea2a"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=76d9d83769d2a53cf801b659e361c5d35d928e71#76d9d83769d2a53cf801b659e361c5d35d928e71"
|
||||
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=7435724d60a321fdee6d87b1aea6e0c7cc3dea2a#7435724d60a321fdee6d87b1aea6e0c7cc3dea2a"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=76d9d83769d2a53cf801b659e361c5d35d928e71#76d9d83769d2a53cf801b659e361c5d35d928e71"
|
||||
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=7435724d60a321fdee6d87b1aea6e0c7cc3dea2a)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=76d9d83769d2a53cf801b659e361c5d35d928e71)",
|
||||
"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=7435724d60a321fdee6d87b1aea6e0c7cc3dea2a)",
|
||||
"craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=76d9d83769d2a53cf801b659e361c5d35d928e71)",
|
||||
"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=7435724d60a321fdee6d87b1aea6e0c7cc3dea2a)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=76d9d83769d2a53cf801b659e361c5d35d928e71)",
|
||||
"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=7435724d60a321fdee6d87b1aea6e0c7cc3dea2a)",
|
||||
"craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=76d9d83769d2a53cf801b659e361c5d35d928e71)",
|
||||
"craft-rtf",
|
||||
"craft-wordmodel",
|
||||
"craft-xlsx",
|
||||
|
||||
+2
-2
@@ -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 = "7435724d60a321fdee6d87b1aea6e0c7cc3dea2a", version = "=0.1.0" }
|
||||
craft-idml = { git = "https://github.com/storytold/craft-libs", rev = "76d9d83769d2a53cf801b659e361c5d35d928e71", 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 = "7435724d60a321fdee6d87b1aea6e0c7cc3dea2a", version = "=0.1.0" }
|
||||
craft-xml = { git = "https://github.com/storytold/craft-libs", rev = "76d9d83769d2a53cf801b659e361c5d35d928e71", 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" }
|
||||
|
||||
@@ -5,15 +5,6 @@ use craft_idml::model::{
|
||||
StrokeDefinition, TintDefinition,
|
||||
};
|
||||
|
||||
// Retained IDML attributes have unprefixed ASCII names. Native JSON can be
|
||||
// edited independently: never let its map keys become XML syntax or Self IDs.
|
||||
pub(super) fn graphic_layer_attribute_name(key: &str) -> bool {
|
||||
!key.is_empty()
|
||||
&& key != "Self"
|
||||
&& key != "xmlns"
|
||||
&& key.bytes().enumerate().all(|(i, b)| b.is_ascii_alphabetic() || b == b'_' || (i > 0 && (b.is_ascii_digit() || matches!(b, b'.' | b'-'))))
|
||||
}
|
||||
|
||||
impl<'a> Ex<'a> {
|
||||
/// A `Gradient` element; stop colours missing from the swatches become anonymous colours.
|
||||
fn gradient_el(
|
||||
|
||||
@@ -1,58 +1,19 @@
|
||||
use super::common::{anchor_self, bool_s, change_type, topic_self, uid, with_props, xref_format_self};
|
||||
use super::graphics::graphic_layer_attribute_name;
|
||||
use super::{
|
||||
BTreeMap, CHAR_BUILTINS, CharFormat, El, Ex, ListType, NO_CHAR_ID, Node, PARA_BUILTINS, ParaAttrs, ParaFormat, Story, TOC_STYLE_ID, names, num,
|
||||
st,
|
||||
};
|
||||
|
||||
impl<'a> Ex<'a> {
|
||||
/// `<GraphicLayerOption>` of a placed graphic: its layer attributes and the nested layer tree.
|
||||
/// `<GraphicLayerOption>` of a placed graphic: its layer attributes and nested layer tree.
|
||||
pub(super) fn layer_option_el(&mut self, options: &designcraft_doc::GraphicLayerOptions) -> El {
|
||||
let mut option = El::new("GraphicLayerOption");
|
||||
for (key, value) in &options.attributes {
|
||||
if graphic_layer_attribute_name(key) {
|
||||
option.set(key, value);
|
||||
}
|
||||
}
|
||||
// Only an ancestor whose subtree is still open can parent the next layer.
|
||||
// Reopening a closed subtree would reorder native-edited lists on roundtrip.
|
||||
let mut open = Vec::new();
|
||||
let mut parents = Vec::with_capacity(options.layers.len());
|
||||
for i in 0..options.layers.len() {
|
||||
let parent = options.parents.get(i).copied().flatten();
|
||||
if let Some(at) = parent.and_then(|p| open.iter().position(|id| *id == p)).filter(|at| *at < 239) {
|
||||
open.truncate(at + 1);
|
||||
parents.push(parent);
|
||||
} else {
|
||||
open.clear();
|
||||
parents.push(None);
|
||||
}
|
||||
open.push(i);
|
||||
}
|
||||
let mut children: Vec<Vec<El>> = vec![Vec::new(); options.layers.len()];
|
||||
let mut roots = Vec::new();
|
||||
for (i, attributes) in options.layers.iter().enumerate().rev() {
|
||||
let mut layer = El::new("GraphicLayer").attr("Self", self.fresh());
|
||||
for (key, value) in attributes {
|
||||
if graphic_layer_attribute_name(key) {
|
||||
layer.set(key, value);
|
||||
}
|
||||
}
|
||||
if let Some(nested) = children.get_mut(i) {
|
||||
for child in std::mem::take(nested).into_iter().rev() {
|
||||
layer.push(child);
|
||||
}
|
||||
}
|
||||
if let Some(nested) = parents.get(i).copied().flatten().and_then(|parent| children.get_mut(parent)) {
|
||||
nested.push(layer);
|
||||
} else {
|
||||
roots.push(layer);
|
||||
}
|
||||
}
|
||||
for layer in roots.into_iter().rev() {
|
||||
option.push(layer);
|
||||
}
|
||||
option
|
||||
let model = craft_idml::model::GraphicLayerOptions {
|
||||
attributes: options.attributes.iter().map(|(key, value)| (key.clone(), value.clone())).collect(),
|
||||
layers: options.layers.iter().map(|layer| layer.iter().map(|(key, value)| (key.clone(), value.clone())).collect()).collect(),
|
||||
parents: options.parents.clone(),
|
||||
};
|
||||
let self_id = self.fresh();
|
||||
crate::xml::from_craft(model.to_el(&self_id, || self.fresh()))
|
||||
}
|
||||
|
||||
/// `<Index>`: nested topics with their See / See also cross-references.
|
||||
|
||||
@@ -243,22 +243,10 @@ impl<'r> Importer<'r> {
|
||||
}
|
||||
|
||||
fn graphic(&mut self, g: &El, model: &craft_idml::model::Graphic) -> Content {
|
||||
let layer_options = g.find("GraphicLayerOption").map(|options| {
|
||||
let attrs = |e: &El| e.attrs.iter().filter(|(key, _)| key != "Self").cloned().collect();
|
||||
let mut out = designcraft_doc::GraphicLayerOptions { attributes: attrs(options), ..Default::default() };
|
||||
let mut pending: Vec<_> = options.find_all("GraphicLayer").map(|e| (e, None)).collect();
|
||||
pending.reverse();
|
||||
while let Some((layer, parent)) = pending.pop() {
|
||||
let index = out.layers.len();
|
||||
out.layers.push(attrs(layer));
|
||||
out.parents.push(parent);
|
||||
let children: Vec<_> = layer.find_all("GraphicLayer").collect();
|
||||
pending.extend(children.into_iter().rev().map(|e| (e, Some(index))));
|
||||
}
|
||||
if out.parents.iter().all(Option::is_none) {
|
||||
out.parents.clear();
|
||||
}
|
||||
out
|
||||
let layer_options = model.layer_options().map(|options| designcraft_doc::GraphicLayerOptions {
|
||||
attributes: options.attributes.into_iter().collect(),
|
||||
layers: options.layers.into_iter().map(IntoIterator::into_iter).map(Iterator::collect).collect(),
|
||||
parents: options.parents,
|
||||
});
|
||||
if layer_options.as_ref().is_some_and(designcraft_doc::GraphicLayerOptions::has_visibility_overrides) {
|
||||
self.warnings.push(format!("graphic {}: {}", g.get("Self").unwrap_or("(unnamed)"), designcraft_doc::GRAPHIC_LAYER_RENDERING_WARNING));
|
||||
@@ -294,9 +282,11 @@ impl<'r> Importer<'r> {
|
||||
})
|
||||
};
|
||||
let mut data = model.contents().unwrap_or_default();
|
||||
let page = pdf_page_in(g);
|
||||
let pdf_attribute = model.pdf_attribute();
|
||||
let page = pdf_attribute.as_ref().map(craft_idml::model::PdfAttribute::page_index).unwrap_or(0);
|
||||
// Where the box sits on the page needs the PDF parsed: the engine finds it after import.
|
||||
let pdf_crop = pdf_attribute(g).and_then(|a| a.get("PDFCrop")).map_or(designcraft_doc::PdfCrop::Crop, names::pdf_crop_in);
|
||||
let pdf_crop =
|
||||
pdf_attribute.as_ref().and_then(craft_idml::model::PdfAttribute::crop).map_or(designcraft_doc::PdfCrop::Crop, names::pdf_crop_in);
|
||||
let mut read_from = None;
|
||||
if data.is_empty()
|
||||
&& let Some(p) = &uri
|
||||
@@ -404,21 +394,6 @@ fn path_from_model(geometry: craft_idml::model::PathGeometry) -> PathData {
|
||||
PathData::new(subpaths)
|
||||
}
|
||||
|
||||
/// A placed PDF's `PDFAttribute` (Image Import Options), as a child or a property.
|
||||
fn pdf_attribute(g: &El) -> Option<&El> {
|
||||
g.find("PDFAttribute").or_else(|| g.prop_el("PDFAttribute"))
|
||||
}
|
||||
|
||||
/// The page a placed PDF shows (0-based), from `PDFAttribute/@PageNumber` (1-based). A missing or
|
||||
/// malformed value means page 1; a page the PDF lacks draws nothing and is left out of PDF export.
|
||||
fn pdf_page_in(g: &El) -> u32 {
|
||||
pdf_attribute(g)
|
||||
.and_then(|a| a.get("PageNumber"))
|
||||
.and_then(|v| v.trim().parse::<u64>().ok())
|
||||
.and_then(|n| u32::try_from(n.saturating_sub(1)).ok())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Anchors a custom text wrap boundary may have.
|
||||
const MAX_WRAP_PATH_ANCHORS: usize = 4096;
|
||||
|
||||
|
||||
Reference in new issue
Block a user