mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-14 22:35:26 +00:00
Use shared IDML geometry model
This commit is contained in:
6 files changed
+83
-87
No files matched your search
Generated
+26
-7
@@ -1235,10 +1235,10 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-idml"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=ba24fe2435adb3c81ad6057faf36365fbba736cf#ba24fe2435adb3c81ad6057faf36365fbba736cf"
|
||||
source = "git+https://github.com/dylanmtaylor/craft-libs?rev=bee70057b341b972686a59ecae8fa0063cedf787#bee70057b341b972686a59ecae8fa0063cedf787"
|
||||
dependencies = [
|
||||
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=ba24fe2435adb3c81ad6057faf36365fbba736cf)",
|
||||
"craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=ba24fe2435adb3c81ad6057faf36365fbba736cf)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/dylanmtaylor/craft-libs?rev=bee70057b341b972686a59ecae8fa0063cedf787)",
|
||||
"craft-zip 0.1.0 (git+https://github.com/dylanmtaylor/craft-libs?rev=bee70057b341b972686a59ecae8fa0063cedf787)",
|
||||
"thiserror 2.0.21",
|
||||
]
|
||||
|
||||
@@ -1602,6 +1602,15 @@ dependencies = [
|
||||
"thiserror 2.0.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "craft-xml"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/dylanmtaylor/craft-libs?rev=bee70057b341b972686a59ecae8fa0063cedf787#bee70057b341b972686a59ecae8fa0063cedf787"
|
||||
dependencies = [
|
||||
"quick-xml 0.41.0",
|
||||
"thiserror 2.0.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "craft-xml"
|
||||
version = "0.1.0"
|
||||
@@ -1641,6 +1650,16 @@ dependencies = [
|
||||
"zip 9.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "craft-zip"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/dylanmtaylor/craft-libs?rev=bee70057b341b972686a59ecae8fa0063cedf787#bee70057b341b972686a59ecae8fa0063cedf787"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"thiserror 2.0.21",
|
||||
"zip 9.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "craft-zip"
|
||||
version = "0.1.0"
|
||||
@@ -1895,7 +1914,7 @@ dependencies = [
|
||||
"craft-prepress",
|
||||
"craft-print",
|
||||
"craft-svg 0.1.0 (git+https://github.com/dylanmtaylor/craft-libs?rev=2e670cf9377b523112f3fe4b409399772c745895)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=ba24fe2435adb3c81ad6057faf36365fbba736cf)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/dylanmtaylor/craft-libs?rev=bee70057b341b972686a59ecae8fa0063cedf787)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/dylanmtaylor/craft-libs?rev=f23b0f33f0d00b157476533a43cbbcfd901aa1f1)",
|
||||
"designcraft-color",
|
||||
"designcraft-compose",
|
||||
@@ -1979,8 +1998,8 @@ dependencies = [
|
||||
name = "designcraft-idml"
|
||||
version = "0.20.1"
|
||||
dependencies = [
|
||||
"craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=ba24fe2435adb3c81ad6057faf36365fbba736cf)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=ba24fe2435adb3c81ad6057faf36365fbba736cf)",
|
||||
"craft-idml 0.1.0 (git+https://github.com/dylanmtaylor/craft-libs?rev=bee70057b341b972686a59ecae8fa0063cedf787)",
|
||||
"craft-xml 0.1.0 (git+https://github.com/dylanmtaylor/craft-libs?rev=bee70057b341b972686a59ecae8fa0063cedf787)",
|
||||
"designcraft-color",
|
||||
"designcraft-compose",
|
||||
"designcraft-doc",
|
||||
@@ -2097,7 +2116,7 @@ version = "0.20.1"
|
||||
dependencies = [
|
||||
"craft-docx",
|
||||
"craft-file-formats",
|
||||
"craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=ba24fe2435adb3c81ad6057faf36365fbba736cf)",
|
||||
"craft-idml 0.1.0 (git+https://github.com/dylanmtaylor/craft-libs?rev=bee70057b341b972686a59ecae8fa0063cedf787)",
|
||||
"craft-rtf",
|
||||
"craft-wordmodel",
|
||||
"craft-xlsx",
|
||||
|
||||
+2
-2
@@ -16,10 +16,10 @@ craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "4052591363
|
||||
craft-document-bundle = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", 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 = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" }
|
||||
craft-idml = { git = "https://github.com/dylanmtaylor/craft-libs", rev = "bee70057b341b972686a59ecae8fa0063cedf787", 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/dylanmtaylor/craft-libs", rev = "2e670cf9377b523112f3fe4b409399772c745895", version = "=0.1.0", features = ["tree-render", "tree-text"] }
|
||||
craft-xml = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" }
|
||||
craft-xml = { git = "https://github.com/dylanmtaylor/craft-libs", rev = "bee70057b341b972686a59ecae8fa0063cedf787", version = "=0.1.0" }
|
||||
craft-xml-dtd = { package = "craft-xml", git = "https://github.com/dylanmtaylor/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" }
|
||||
|
||||
@@ -447,20 +447,21 @@ fn clipping_settings(c: &designcraft_doc::ClippingPath) -> El {
|
||||
}
|
||||
|
||||
fn path_geometry(path: &PathData) -> El {
|
||||
let mut pg = El::new("PathGeometry");
|
||||
for sp in &path.subpaths {
|
||||
let mut arr = El::new("PathPointArray");
|
||||
for a in &sp.anchors {
|
||||
arr.push(
|
||||
El::new("PathPointType")
|
||||
.attr("Anchor", pt(a.p.x, a.p.y))
|
||||
.attr("LeftDirection", pt(a.h_in.x, a.h_in.y))
|
||||
.attr("RightDirection", pt(a.h_out.x, a.h_out.y)),
|
||||
);
|
||||
}
|
||||
pg.push(El::new("GeometryPathType").attr("PathOpen", bool_s(!sp.closed)).child(arr));
|
||||
}
|
||||
pg
|
||||
let geometry = craft_idml::model::PathGeometry {
|
||||
paths: path
|
||||
.subpaths
|
||||
.iter()
|
||||
.map(|sp| craft_idml::model::SubPath {
|
||||
open: !sp.closed,
|
||||
points: sp
|
||||
.anchors
|
||||
.iter()
|
||||
.map(|a| craft_idml::model::PathPoint { anchor: [a.p.x, a.p.y], left: [a.h_in.x, a.h_in.y], right: [a.h_out.x, a.h_out.y] })
|
||||
.collect(),
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
crate::xml::from_craft(geometry.to_el())
|
||||
}
|
||||
|
||||
/// IDML corner names for anchors 0..4 of a path (by position on its bounding box).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::frame::{Prefs, frame_grid_relative, text_frame_options, text_wrap};
|
||||
use super::object_style::INDESIGN_CORNER_SIZE;
|
||||
use super::package::uri_to_path;
|
||||
use super::values::{nums, parse_affine, parse_point, tint};
|
||||
use super::values::{nums, parse_affine, tint};
|
||||
use super::{
|
||||
Affine, Anchor, Arc, Asset, AssetId, Content, Corner, CornerOptions, El, Fill, Importer, Item, ItemId, OBJECT_BUILTINS, PathData, Point, Shape,
|
||||
Story, StoryId, Stroke, SubPath, TextFrame, TextFrameOptions, base64_decode, names, sniff_image, swatch, unescape_id,
|
||||
@@ -412,30 +412,30 @@ fn path_of(pg: &El) -> PathData {
|
||||
|
||||
/// The path of a `…Geometry` element, keeping at most `max` anchors.
|
||||
fn path_capped(pg: &El, max: usize) -> PathData {
|
||||
let mut subs = Vec::new();
|
||||
let mut left = max;
|
||||
for g in pg.find_all("GeometryPathType") {
|
||||
let closed = g.get("PathOpen") != Some("true");
|
||||
let mut anchors = Vec::new();
|
||||
if let Some(arr) = g.find("PathPointArray") {
|
||||
// A NaN or infinite point drops its subpath (JSON can't store it).
|
||||
let non_finite = |s: Option<&str>| s.is_some_and(|s| s.split_whitespace().any(|v| v.parse::<f64>().is_ok_and(|x| !x.is_finite())));
|
||||
if arr.find_all("PathPointType").take(left).any(|p| ["Anchor", "LeftDirection", "RightDirection"].iter().any(|k| non_finite(p.get(k)))) {
|
||||
continue;
|
||||
}
|
||||
for p in arr.find_all("PathPointType").take(left) {
|
||||
let Some(a) = parse_point(p.get("Anchor")) else { continue };
|
||||
let hin = parse_point(p.get("LeftDirection")).unwrap_or(a);
|
||||
let hout = parse_point(p.get("RightDirection")).unwrap_or(a);
|
||||
anchors.push(Anchor::with_handles(a, hin, hout));
|
||||
}
|
||||
}
|
||||
left = left.saturating_sub(anchors.len());
|
||||
if !anchors.is_empty() {
|
||||
subs.push(SubPath::new(anchors, closed));
|
||||
}
|
||||
}
|
||||
PathData::new(subs)
|
||||
let geometry = craft_idml::model::PathGeometry::from_el_capped(&crate::xml::to_craft(pg), max);
|
||||
path_from_model(geometry)
|
||||
}
|
||||
|
||||
fn path_from_model(geometry: craft_idml::model::PathGeometry) -> PathData {
|
||||
let subpaths = geometry
|
||||
.paths
|
||||
.into_iter()
|
||||
.filter_map(|path| {
|
||||
let anchors: Vec<_> = path
|
||||
.points
|
||||
.into_iter()
|
||||
.map(|point| {
|
||||
Anchor::with_handles(
|
||||
Point::new(point.anchor[0], point.anchor[1]),
|
||||
Point::new(point.left[0], point.left[1]),
|
||||
Point::new(point.right[0], point.right[1]),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
(!anchors.is_empty()).then(|| SubPath::new(anchors, !path.open))
|
||||
})
|
||||
.collect();
|
||||
PathData::new(subpaths)
|
||||
}
|
||||
|
||||
/// A placed PDF's `PDFAttribute` (Image Import Options), as a child or a property.
|
||||
@@ -460,35 +460,16 @@ const MAX_WRAP_PATH_ANCHORS: usize = 4096;
|
||||
/// space like its own path. Hostile input is an error: too many anchors, a malformed or non-finite
|
||||
/// point, or no points at all.
|
||||
fn wrap_path_of(pg: &El) -> std::result::Result<PathData, &'static str> {
|
||||
let finite = |s: Option<&str>| -> std::result::Result<Option<Point>, &'static str> {
|
||||
let Some(s) = s else { return Ok(None) };
|
||||
let v = nums(s);
|
||||
match (v.first(), v.get(1)) {
|
||||
(Some(&x), Some(&y)) if v.len() == 2 && x.is_finite() && y.is_finite() => Ok(Some(Point::new(x, y))),
|
||||
_ => Err("malformed or non-finite point"),
|
||||
}
|
||||
};
|
||||
let mut subs = Vec::new();
|
||||
let mut n = 0usize;
|
||||
for g in pg.find_all("GeometryPathType") {
|
||||
let closed = g.get("PathOpen") != Some("true");
|
||||
let mut anchors = Vec::new();
|
||||
for p in g.find("PathPointArray").into_iter().flat_map(|arr| arr.find_all("PathPointType")) {
|
||||
n = n.saturating_add(1);
|
||||
if n > MAX_WRAP_PATH_ANCHORS {
|
||||
return Err("too many points");
|
||||
}
|
||||
let a = finite(p.get("Anchor"))?.ok_or("point without an anchor")?;
|
||||
let hin = finite(p.get("LeftDirection"))?.unwrap_or(a);
|
||||
let hout = finite(p.get("RightDirection"))?.unwrap_or(a);
|
||||
anchors.push(Anchor::with_handles(a, hin, hout));
|
||||
}
|
||||
if !anchors.is_empty() {
|
||||
subs.push(SubPath::new(anchors, closed));
|
||||
}
|
||||
}
|
||||
if subs.is_empty() {
|
||||
let geometry =
|
||||
craft_idml::model::PathGeometry::try_from_el_capped(&crate::xml::to_craft(pg), MAX_WRAP_PATH_ANCHORS).map_err(|error| match error {
|
||||
craft_idml::model::PathGeometryError::TooManyPoints => "too many points",
|
||||
craft_idml::model::PathGeometryError::MissingAnchor => "point without an anchor",
|
||||
craft_idml::model::PathGeometryError::MalformedPoint => "malformed or non-finite point",
|
||||
craft_idml::model::PathGeometryError::NonFinitePoint => "malformed or non-finite point",
|
||||
})?;
|
||||
let path = path_from_model(geometry);
|
||||
if path.subpaths.is_empty() {
|
||||
return Err("no points");
|
||||
}
|
||||
Ok(PathData::new(subs))
|
||||
Ok(path)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{Affine, Color, El, Point, lab};
|
||||
use super::{Affine, Color, El, lab};
|
||||
|
||||
/// An XMP date property, written as an element or an attribute.
|
||||
pub(super) fn xmp_date(xmp: &str, key: &str) -> Option<i64> {
|
||||
@@ -32,11 +32,6 @@ pub(super) fn parse_affine(s: Option<&str>) -> Affine {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn parse_point(s: Option<&str>) -> Option<Point> {
|
||||
let v = nums(s?);
|
||||
(v.len() >= 2).then(|| Point::new(v[0], v[1]))
|
||||
}
|
||||
|
||||
/// IDML tint (percent; `-1` = the colour's own tint, 100 %) → 0..1.
|
||||
pub(super) fn tint(v: Option<f64>) -> Option<f32> {
|
||||
v.map(|t| if t < 0.0 || t.is_nan() { 1.0 } else { (t / 100.0).min(1.0) as f32 })
|
||||
|
||||
@@ -182,7 +182,7 @@ pub fn escape_into(out: &mut String, s: &str, attr: bool) {
|
||||
/// Serialize a part: XML declaration + root element.
|
||||
pub fn document(root: &El) -> Vec<u8> {
|
||||
let mut s = String::from("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n");
|
||||
s.push_str(&to_xml(root).to_xml_pretty("\t"));
|
||||
s.push_str(&to_craft(root).to_xml_pretty("\t"));
|
||||
s.into_bytes()
|
||||
}
|
||||
|
||||
@@ -191,11 +191,11 @@ pub fn document_with_pi(root: &El, pi: &str) -> Vec<u8> {
|
||||
let mut s = String::from("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n");
|
||||
s.push_str(pi);
|
||||
s.push('\n');
|
||||
s.push_str(&to_xml(root).to_xml_pretty("\t"));
|
||||
s.push_str(&to_craft(root).to_xml_pretty("\t"));
|
||||
s.into_bytes()
|
||||
}
|
||||
|
||||
fn to_xml(el: &El) -> craft_xml::El {
|
||||
pub(crate) fn to_craft(el: &El) -> craft_xml::El {
|
||||
craft_xml::El {
|
||||
name: el.name.clone(),
|
||||
attrs: el.attrs.clone(),
|
||||
@@ -203,7 +203,7 @@ fn to_xml(el: &El) -> craft_xml::El {
|
||||
.children
|
||||
.iter()
|
||||
.map(|node| match node {
|
||||
Node::El(child) => craft_xml::Node::El(to_xml(child)),
|
||||
Node::El(child) => craft_xml::Node::El(to_craft(child)),
|
||||
Node::Text(text) => craft_xml::Node::Text(text.clone()),
|
||||
Node::CData(text) => craft_xml::Node::CData(text.clone()),
|
||||
Node::Pi(target, data) => craft_xml::Node::Pi { target: target.clone(), data: data.clone() },
|
||||
|
||||
Reference in new issue
Block a user