mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-28 12:16:53 -06:00
Merge #304: IDML: text in a paragraph-level cross-reference source keeps its formatting
This commit is contained in:
+17
-10
@@ -1823,6 +1823,15 @@ impl<'r> Importer<'r> {
|
||||
t
|
||||
}
|
||||
|
||||
/// The format of the text in a `CharacterStyleRange`: its style, and its local attributes over
|
||||
/// the enclosing paragraph range's.
|
||||
fn range_format(&mut self, c: &El, pchars: &CharAttrs) -> CharFormat {
|
||||
let style = c.get("AppliedCharacterStyle").map(|r| self.char_style_ref(r)).unwrap_or_else(|| st::NO_CHAR_STYLE.into());
|
||||
let mut over = pchars.clone();
|
||||
over.merge(&self.char_attrs(c));
|
||||
CharFormat { style, over }
|
||||
}
|
||||
|
||||
fn walk_story(&mut self, e: &El, b: &mut StoryBuilder, pf: &ParaFormat, pchars: &CharAttrs, cf: &CharFormat, brk: Option<&str>) {
|
||||
for n in &e.children {
|
||||
match n {
|
||||
@@ -1837,13 +1846,13 @@ impl<'r> Importer<'r> {
|
||||
{
|
||||
*last = npf.clone();
|
||||
}
|
||||
self.walk_story(c, b, &npf, &chars, cf, None);
|
||||
// Text and markers outside any character range (a destination or a
|
||||
// cross-reference source at paragraph level) take the paragraph's format.
|
||||
let pcf = CharFormat { style: st::NO_CHAR_STYLE.into(), over: chars.clone() };
|
||||
self.walk_story(c, b, &npf, &chars, &pcf, None);
|
||||
}
|
||||
"CharacterStyleRange" => {
|
||||
let style = c.get("AppliedCharacterStyle").map(|r| self.char_style_ref(r)).unwrap_or_else(|| st::NO_CHAR_STYLE.into());
|
||||
let mut over = pchars.clone();
|
||||
over.merge(&self.char_attrs(c));
|
||||
let ncf = CharFormat { style, over };
|
||||
let ncf = self.range_format(c, pchars);
|
||||
let bt = c.get("ParagraphBreakType").filter(|t| *t != "Anywhere");
|
||||
b.last = ncf.clone();
|
||||
self.walk_story(c, b, pf, pchars, &ncf, bt);
|
||||
@@ -1907,11 +1916,9 @@ impl<'r> Importer<'r> {
|
||||
"CrossReferenceSource" => {
|
||||
// The generated text is regenerated from the format.
|
||||
b.xrefs.push((c.get("Self").unwrap_or("").to_string(), c.get("AppliedFormat").unwrap_or("").to_string()));
|
||||
let inner = c.find("CharacterStyleRange");
|
||||
let f = match inner.and_then(|r| r.get("AppliedCharacterStyle")) {
|
||||
Some(r) => CharFormat { style: self.char_style_ref(r), over: pchars.clone() },
|
||||
None => cf.clone(),
|
||||
};
|
||||
// A source at paragraph level wraps a range; inside a range it holds the
|
||||
// text directly and takes that range's format.
|
||||
let f = c.find("CharacterStyleRange").map_or_else(|| cf.clone(), |r| self.range_format(r, pchars));
|
||||
b.push(&designcraft_doc::XREF_MARK.to_string(), &f);
|
||||
}
|
||||
"Footnote" => {
|
||||
|
||||
@@ -1039,6 +1039,44 @@ fn round_trips_cross_references() {
|
||||
assert_eq!(back.xref_formats.len(), d.xref_formats.len());
|
||||
}
|
||||
|
||||
/// A cross-reference source sits either inside a character range (holding the text directly) or at
|
||||
/// paragraph level around a range. Both give the generated text the paragraph's and the range's
|
||||
/// attributes, so both set the same text at the same width.
|
||||
#[test]
|
||||
fn cross_reference_source_nesting_keeps_formatting() {
|
||||
let story = |body: &str| {
|
||||
format!(
|
||||
r#"<?xml version="1.0" encoding="UTF-8"?>
|
||||
<idPkg:Story xmlns:idPkg="http://ns.adobe.com/AdobeInDesign/idml/1.0/packaging" DOMVersion="16.0">
|
||||
<Story Self="s1"><ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/$ID/NormalParagraphStyle" PointSize="11">{body}</ParagraphStyleRange></Story></idPkg:Story>"#
|
||||
)
|
||||
};
|
||||
const CSR: &str = r#"<CharacterStyleRange AppliedCharacterStyle="CharacterStyle/$ID/[No character style]" Tracking="200">"#;
|
||||
let inside = story(&format!(
|
||||
r#"{CSR}<Content>See section </Content><CrossReferenceSource Self="x1" AppliedFormat="n"><Content>11.</Content></CrossReferenceSource><Content>, then a few more words to wrap.</Content></CharacterStyleRange>"#
|
||||
));
|
||||
let around = story(&format!(
|
||||
r#"{CSR}<Content>See section </Content></CharacterStyleRange><CrossReferenceSource Self="x1" AppliedFormat="n">{CSR}<Content>11.</Content></CharacterStyleRange></CrossReferenceSource>{CSR}<Content>, then a few more words to wrap.</Content></CharacterStyleRange>"#
|
||||
));
|
||||
let compose = |xml: &str| {
|
||||
let d = import_idml(&fixture_with_story(xml)).unwrap();
|
||||
d.check().unwrap();
|
||||
let s = d.stories.values().find(|s| !s.xrefs.is_empty()).expect("story with a cross-reference").clone();
|
||||
let cs = designcraft_compose::compose_story(&d, s.id, &Default::default());
|
||||
let glyphs: Vec<_> = cs.frames.iter().flat_map(|f| &f.lines).flat_map(|l| l.glyphs.iter().map(move |g| (g.byte, g.x, l.baseline))).collect();
|
||||
(s, glyphs)
|
||||
};
|
||||
let (a, ga) = compose(&inside);
|
||||
let (b, gb) = compose(&around);
|
||||
assert!(ga.len() > a.text.chars().count() / 2, "the story composes");
|
||||
assert_eq!(a.text, b.text);
|
||||
assert_eq!(a.chars, b.chars);
|
||||
assert_eq!(a.chars.len(), 1, "the generated text shares its neighbours' format");
|
||||
assert_eq!(a.chars[0].format.over.tracking, Some(200.0));
|
||||
assert_eq!(a.chars[0].format.over.size, Some(11.0));
|
||||
assert_eq!(ga, gb);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trips_index_references() {
|
||||
use designcraft_doc::index::IndexRange;
|
||||
|
||||
Reference in New Issue
Block a user