mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-14 14:22:39 +00:00
IDML: a style without a category flag takes it from its based-on style
An object style that gives neither EnableStrokeAndCornerOptions nor corners of its own takes the category from the style it is based on, and one without EnableTextFrameGeneralOptions does the same for its text frame options. A style based on [None] still sets neither, and [None] keeps the corners it sets itself.
This commit is contained in:
1 parent
737fcfec04
commit
f0e63b333d
2 files changed
+64
-13
No files matched your search
@@ -199,8 +199,9 @@ impl<'r> Importer<'r> {
|
||||
.flatten();
|
||||
let levels = based_on_levels(e, &objects_by_self);
|
||||
// Text Frame General Options: each attribute from the nearest style whose
|
||||
// `TextFramePreference` sets it, among the styles that don't turn the category off.
|
||||
let text_frame = if e.get("EnableTextFrameGeneralOptions") == Some("true") {
|
||||
// `TextFramePreference` sets it, among the styles that don't turn the category off. A
|
||||
// style without the category flag takes it from the style it is based on.
|
||||
let text_frame = if category_on(&levels, "EnableTextFrameGeneralOptions", |_| false) {
|
||||
let prefs: Vec<&El> = levels
|
||||
.iter()
|
||||
.filter(|l| l.get("EnableTextFrameGeneralOptions") != Some("false"))
|
||||
@@ -211,7 +212,7 @@ impl<'r> Importer<'r> {
|
||||
None
|
||||
};
|
||||
let effects = self.style_effects(e);
|
||||
let corners = style_corners(e, &levels);
|
||||
let corners = style_corners(&levels);
|
||||
let apply_next_style = paragraph_style.is_some() && e.get("ApplyNextParagraphStyle") == Some("true");
|
||||
let s = ObjectStyle { name: name.clone(), based_on, fill, stroke, paragraph_style, apply_next_style, text_frame, effects, corners };
|
||||
match self.styles.object.iter_mut().find(|o| o.name == name) {
|
||||
@@ -412,21 +413,34 @@ impl<'r> Importer<'r> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Object style `e`'s corner options (Stroke & Corner Options), by name, when it sets them. A style
|
||||
/// that sets some corners, or turns the category on, takes the others from the styles in `levels`
|
||||
/// (`e` and the styles it is based on) that don't turn the category off.
|
||||
fn style_corners(e: &El, levels: &[&El]) -> Option<CornerOptions> {
|
||||
match e.get("EnableStrokeAndCornerOptions") {
|
||||
Some("false") => return None,
|
||||
Some("true") => {}
|
||||
_ => {
|
||||
corners(e, "")?;
|
||||
}
|
||||
/// The corner options (Stroke & Corner Options) of the object style that starts `levels` (the
|
||||
/// style, then the styles it is based on), by name, when it sets them. A style that sets some
|
||||
/// corners, or turns the category on (itself or through the style it is based on), takes the
|
||||
/// others from the styles in `levels` that don't turn the category off.
|
||||
fn style_corners(levels: &[&El]) -> Option<CornerOptions> {
|
||||
if !category_on(levels, "EnableStrokeAndCornerOptions", |l| corners(l, "").is_some()) {
|
||||
return None;
|
||||
}
|
||||
let on: Vec<&El> = levels.iter().copied().filter(|l| l.get("EnableStrokeAndCornerOptions") != Some("false")).collect();
|
||||
corners_in(&on, "").map(|c| CornerOptions { corners: c })
|
||||
}
|
||||
|
||||
/// Whether the style chain `levels` (a style, then the styles it is based on) turns the category
|
||||
/// whose flag is `flag` on. The nearest style that gives the flag decides; a style without it
|
||||
/// turns the category on when `own` finds settings of its own. A `[None]` after the first style
|
||||
/// ends the chain, as a `BasedOn` of `[None]` names no style.
|
||||
fn category_on(levels: &[&El], flag: &str, own: impl Fn(&El) -> bool) -> bool {
|
||||
let none = |l: &&El| l.get("Self") == Some("ObjectStyle/$ID/[None]");
|
||||
for l in levels.iter().take(1).chain(levels.iter().skip(1).take_while(|l| !none(l))) {
|
||||
match l.get(flag) {
|
||||
Some(v) => return v == "true",
|
||||
None if own(l) => return true,
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Object style `e`, then the styles it is based on, nearest first (cycle-safe, at most 16). A
|
||||
/// `BasedOn` may name a style by its `Self` id or by its bare name.
|
||||
fn based_on_levels<'e>(e: &'e El, objects_by_self: &HashMap<&str, &'e El>) -> Vec<&'e El> {
|
||||
|
||||
@@ -363,6 +363,43 @@ fn a_style_turning_corners_on_inherits_all_four() {
|
||||
assert_eq!((c.map(|c| c.shape), c.map(|c| c.size)), ([CornerShape::Rounded; 4], [17.0; 4]));
|
||||
}
|
||||
|
||||
/// A style with no Stroke & Corner Options or Text Frame General Options flag and no settings of
|
||||
/// its own takes each category from the style it is based on: on, off, or (for a style based on
|
||||
/// `[None]`) not set.
|
||||
#[test]
|
||||
fn a_style_without_category_flags_takes_them_from_its_based_on_style() {
|
||||
let styles = r#"<ObjectStyle Self="ObjectStyle/Framed" Name="Framed" EnableTextFrameGeneralOptions="true">
|
||||
<Properties><BasedOn type="string">$ID/[None]</BasedOn></Properties>
|
||||
<TextFramePreference TextColumnCount="3"/>
|
||||
</ObjectStyle>
|
||||
<ObjectStyle Self="ObjectStyle/Off" Name="Off" EnableStrokeAndCornerOptions="false" EnableTextFrameGeneralOptions="false"
|
||||
TopLeftCornerOption="RoundedCorner">
|
||||
<Properties><BasedOn type="object">ObjectStyle/Round</BasedOn></Properties>
|
||||
<TextFramePreference TextColumnCount="4"/>
|
||||
</ObjectStyle>
|
||||
<ObjectStyle Self="ObjectStyle/RoundKid" Name="RoundKid">
|
||||
<Properties><BasedOn type="object">ObjectStyle/Round</BasedOn></Properties>
|
||||
</ObjectStyle>
|
||||
<ObjectStyle Self="ObjectStyle/FramedKid" Name="FramedKid">
|
||||
<Properties><BasedOn type="object">ObjectStyle/Framed</BasedOn></Properties>
|
||||
</ObjectStyle>
|
||||
<ObjectStyle Self="ObjectStyle/OffKid" Name="OffKid">
|
||||
<Properties><BasedOn type="object">ObjectStyle/Off</BasedOn></Properties>
|
||||
</ObjectStyle>
|
||||
<ObjectStyle Self="ObjectStyle/Bare" Name="Bare">
|
||||
<Properties><BasedOn type="string">$ID/[None]</BasedOn></Properties>
|
||||
</ObjectStyle>"#;
|
||||
let d = fixture(&format!("{ROUND}{styles}"), &[]);
|
||||
let style = |n: &str| d.styles.object_style(n).cloned().unwrap();
|
||||
let c = style("RoundKid").corners.expect("RoundKid inherits Round's corners").corners;
|
||||
assert_eq!((c.map(|c| c.shape), c.map(|c| c.size)), ([CornerShape::Rounded; 4], [17.0; 4]));
|
||||
assert_eq!(style("FramedKid").text_frame.map(|t| t.columns), Some(3), "FramedKid inherits Framed's text frame options");
|
||||
for n in ["OffKid", "Bare"] {
|
||||
assert!(style(n).corners.is_none() && style(n).text_frame.is_none(), "{n}: {:?}", style(n));
|
||||
}
|
||||
assert!(style(designcraft_doc::NO_OBJECT_STYLE).corners.is_some(), "[None] keeps the corners it sets itself");
|
||||
}
|
||||
|
||||
/// An object style giving its corners only with the legacy all-corners `CornerOption` and
|
||||
/// `CornerRadius` keeps them, so applying it to another frame rounds it.
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user