mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-28 20:26:57 -06:00
Refactor compose for strict Clippy checks
This commit is contained in:
@@ -526,7 +526,9 @@ pub fn knuth_plass(glyphs: &[Glyph], hyph_after: &[bool], sp: &Spacing, width: &
|
||||
(items, ig) = self::items(glyphs, hyph_after, &emergency_after, sp);
|
||||
}
|
||||
for tolerance in [2.5, 12.0, f64::INFINITY] {
|
||||
if let Some(b) = kp_pass(&items, &ig, glyphs, sp, width, tolerance, tolerance.is_infinite()) {
|
||||
if let Some(b) =
|
||||
kp_pass(PassInput { items: &items, item_glyphs: &ig, glyphs, spacing: sp, width, tolerance, emergency: tolerance.is_infinite() })
|
||||
{
|
||||
return b;
|
||||
}
|
||||
}
|
||||
@@ -603,22 +605,78 @@ fn overlong_words(items: &[Item], ig: &[usize], glyphs: &[Glyph], measure: f64)
|
||||
/// Upper bound on the hyphen-count states tracked per breakpoint.
|
||||
const MAX_HYPHEN_STATES: usize = 8;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn kp_pass(
|
||||
items: &[Item],
|
||||
ig: &[usize],
|
||||
glyphs: &[Glyph],
|
||||
sp: &Spacing,
|
||||
width: &dyn Fn(usize) -> f64,
|
||||
tol: f64,
|
||||
struct PassInput<'a> {
|
||||
items: &'a [Item],
|
||||
item_glyphs: &'a [usize],
|
||||
glyphs: &'a [Glyph],
|
||||
spacing: &'a Spacing,
|
||||
width: &'a dyn Fn(usize) -> f64,
|
||||
tolerance: f64,
|
||||
emergency: bool,
|
||||
) -> Option<Vec<Break>> {
|
||||
let m = items.len();
|
||||
// Prefix sums (before item i).
|
||||
let mut sw = vec![0.0; m + 1];
|
||||
let mut sy = vec![[0.0; 3]; m + 1];
|
||||
let mut sz = vec![[0.0; 3]; m + 1];
|
||||
let mut so = vec![0.0; m + 1];
|
||||
}
|
||||
|
||||
/// Enforce consecutive-hyphen limits and the unjustified hyphenation zone.
|
||||
fn hyphen_allowed(it: Item, n: Node, sp: &Spacing, widths: &[f64], items: &[Item], target: f64) -> bool {
|
||||
let limit = sp.hyphen_limit as usize;
|
||||
if limit > 0 && n.hyphens as usize >= limit {
|
||||
return false;
|
||||
}
|
||||
if it.auto && !sp.justify && sp.hyph_zone > 0.0 && it.zone_from != NONE && it.zone_from > n.pos {
|
||||
let before = widths[it.zone_from] - n.tw - items[it.zone_from].hang;
|
||||
if target - before < sp.hyph_zone {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Fitness class and cumulative penalty for a candidate line break.
|
||||
fn break_demerits(n: Node, r: f64, pp: f64, flagged: bool, priority: u8) -> (usize, f64) {
|
||||
let preference = match priority {
|
||||
1 if r > 0.0 => 4.0,
|
||||
2 if r < 0.0 => 4.0,
|
||||
_ => 1.0,
|
||||
};
|
||||
let bad = preference * 100.0 * r.abs().powi(3);
|
||||
let lp = 10.0 + bad;
|
||||
let mut d = if pp >= 0.0 {
|
||||
lp * lp + pp * pp
|
||||
} else if pp > -INF {
|
||||
lp * lp - pp * pp
|
||||
} else {
|
||||
lp * lp
|
||||
};
|
||||
if flagged && n.flagged {
|
||||
d += 3000.0;
|
||||
}
|
||||
let fit = if r < -0.5 {
|
||||
0
|
||||
} else if r <= 0.5 {
|
||||
1
|
||||
} else if r <= 1.0 {
|
||||
2
|
||||
} else {
|
||||
3
|
||||
};
|
||||
if (fit as i32 - n.fitness as i32).abs() > 1 {
|
||||
d += 3000.0;
|
||||
}
|
||||
(fit, n.demerits + d)
|
||||
}
|
||||
|
||||
/// Width, stretch, shrink and overhang totals before each item.
|
||||
struct PrefixSums {
|
||||
sw: Vec<f64>,
|
||||
sy: Vec<[f64; 3]>,
|
||||
sz: Vec<[f64; 3]>,
|
||||
so: Vec<f64>,
|
||||
}
|
||||
|
||||
fn prefix_sums(items: &[Item]) -> PrefixSums {
|
||||
let mut sw = vec![0.0; items.len() + 1];
|
||||
let mut sy = vec![[0.0; 3]; items.len() + 1];
|
||||
let mut sz = vec![[0.0; 3]; items.len() + 1];
|
||||
let mut so = vec![0.0; items.len() + 1];
|
||||
for (i, it) in items.iter().enumerate() {
|
||||
let (w, y, z) = match it.kind {
|
||||
Kind::Box | Kind::Glue => (it.w, it.st, it.sh),
|
||||
@@ -631,6 +689,13 @@ fn kp_pass(
|
||||
sz[i + 1][t] = sz[i][t] + z[t];
|
||||
}
|
||||
}
|
||||
PrefixSums { sw, sy, sz, so }
|
||||
}
|
||||
|
||||
fn kp_pass(input: PassInput<'_>) -> Option<Vec<Break>> {
|
||||
let PassInput { items, item_glyphs, glyphs, spacing: sp, width, tolerance: tol, emergency } = input;
|
||||
let m = items.len();
|
||||
let PrefixSums { sw, sy, sz, so } = prefix_sums(items);
|
||||
let first_box = items.iter().find(|i| i.kind == Kind::Box);
|
||||
let first_hang = first_box.map_or(0.0, |b| b.hang);
|
||||
let first_elastic = first_box.map_or([0.0; 2], |b| b.start_elastic);
|
||||
@@ -695,51 +760,11 @@ fn kp_pass(
|
||||
if !feasible || (r > tol && !forced) {
|
||||
continue;
|
||||
}
|
||||
if flagged {
|
||||
// Hyphen limit: a hard constraint.
|
||||
if limit > 0 && n.hyphens as usize >= limit {
|
||||
continue;
|
||||
}
|
||||
// Hyphenation zone (unjustified): break before the word instead when that leaves
|
||||
// less than the zone at the line end.
|
||||
if it.auto && !sp.justify && sp.hyph_zone > 0.0 && it.zone_from != NONE && it.zone_from > n.pos {
|
||||
let before = sw[it.zone_from] - n.tw - items[it.zone_from].hang;
|
||||
if target - before < sp.hyph_zone {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
let preference = match sp.kinsoku_priority {
|
||||
1 if r > 0.0 => 4.0,
|
||||
2 if r < 0.0 => 4.0,
|
||||
_ => 1.0,
|
||||
};
|
||||
let bad = preference * 100.0 * r.abs().powi(3);
|
||||
let lp = 10.0 + bad;
|
||||
let mut d = if pp >= 0.0 {
|
||||
lp * lp + pp * pp
|
||||
} else if pp > -INF {
|
||||
lp * lp - pp * pp
|
||||
} else {
|
||||
lp * lp
|
||||
};
|
||||
if flagged && n.flagged {
|
||||
d += 3000.0;
|
||||
}
|
||||
let fit = if r < -0.5 {
|
||||
0
|
||||
} else if r <= 0.5 {
|
||||
1
|
||||
} else if r <= 1.0 {
|
||||
2
|
||||
} else {
|
||||
3
|
||||
};
|
||||
if (fit as i32 - n.fitness as i32).abs() > 1 {
|
||||
d += 3000.0;
|
||||
if flagged && !hyphen_allowed(it, n, sp, &sw, items, target) {
|
||||
continue;
|
||||
}
|
||||
let (fit, total) = break_demerits(n, r, pp, flagged, sp.kinsoku_priority);
|
||||
let hs = if flagged { (n.hyphens as usize + 1).min(states - 1) } else { 0 };
|
||||
let total = n.demerits + d;
|
||||
let slot = &mut best[fit * states + hs];
|
||||
if slot.is_none_or(|(bd, _)| total < bd) {
|
||||
*slot = Some((total, a));
|
||||
@@ -820,7 +845,7 @@ fn kp_pass(
|
||||
cur = arena[c].prev;
|
||||
}
|
||||
chain.reverse();
|
||||
Some(breaks_from_positions(items, ig, glyphs, &chain))
|
||||
Some(breaks_from_positions(items, item_glyphs, glyphs, &chain))
|
||||
}
|
||||
|
||||
fn breaks_from_positions(items: &[Item], ig: &[usize], glyphs: &[Glyph], chain: &[usize]) -> Vec<Break> {
|
||||
|
||||
+2163
-1113
File diff suppressed because it is too large
Load Diff
@@ -46,6 +46,14 @@ pub(crate) struct NoteMetrics {
|
||||
descent: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct NoteColumn<'a> {
|
||||
pub(crate) frame_index: usize,
|
||||
pub(crate) column_index: usize,
|
||||
pub(crate) width: f64,
|
||||
pub(crate) frame: &'a FrameSpec,
|
||||
}
|
||||
|
||||
/// Height of footnotes stacked baseline to baseline, from the top of the footnote area to the
|
||||
/// last footnote's descent.
|
||||
fn stack_height<'a>(notes: impl IntoIterator<Item = &'a NoteMetrics>, space_between: f64) -> f64 {
|
||||
@@ -194,16 +202,16 @@ impl Notes {
|
||||
}
|
||||
|
||||
/// Height the footnotes of column (fi, col) take, with `extra` footnotes added.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn reserve(&mut self, doc: &Document, fi: usize, col: usize, extra: &[usize], w: f64, f: &FrameSpec, opts: &ComposeOptions) -> f64 {
|
||||
pub fn reserve(&mut self, doc: &Document, column: NoteColumn<'_>, extra: &[usize], opts: &ComposeOptions) -> f64 {
|
||||
let o = &doc.footnote_options;
|
||||
let (space_between, space_before) = (o.space_between, o.space_before);
|
||||
let mut ms: Vec<NoteMetrics> = self.placed.iter().filter(|p| self.same_area(p, fi, col)).map(|p| p.m).collect();
|
||||
let mut ms: Vec<NoteMetrics> =
|
||||
self.placed.iter().filter(|p| self.same_area(p, column.frame_index, column.column_index)).map(|p| p.m).collect();
|
||||
for &k in extra {
|
||||
ms.push(self.compose(doc, k, w, f, opts).1);
|
||||
ms.push(self.compose(doc, k, column.width, column.frame, opts).1);
|
||||
}
|
||||
let r = if ms.is_empty() { 0.0 } else { stack_height(&ms, space_between) + space_before };
|
||||
if self.span { r.max(self.floor.get(&fi).copied().unwrap_or(0.0)) } else { r }
|
||||
if self.span { r.max(self.floor.get(&column.frame_index).copied().unwrap_or(0.0)) } else { r }
|
||||
}
|
||||
|
||||
/// After spanning notes were placed in column `col` of frame `fi`: whether a line of an earlier
|
||||
@@ -221,12 +229,12 @@ impl Notes {
|
||||
hit
|
||||
}
|
||||
|
||||
pub fn place(&mut self, doc: &Document, k: usize, fi: usize, col: usize, w: f64, f: &FrameSpec, opts: &ComposeOptions) {
|
||||
pub fn place(&mut self, doc: &Document, k: usize, column: NoteColumn<'_>, opts: &ComposeOptions) {
|
||||
if self.placed.iter().any(|p| p.k == k) {
|
||||
return;
|
||||
}
|
||||
let (text, m) = self.compose(doc, k, w, f, opts);
|
||||
self.placed.push(Placed { k, fi, col, m, text });
|
||||
let (text, m) = self.compose(doc, k, column.width, column.frame, opts);
|
||||
self.placed.push(Placed { k, fi: column.frame_index, col: column.column_index, m, text });
|
||||
}
|
||||
|
||||
/// Stack each column's footnotes against its bottom, rule above the first.
|
||||
|
||||
+53
-27
@@ -319,30 +319,50 @@ impl StyleTable<'_> {
|
||||
|
||||
/// Resolve and shape one paragraph. Its drop cap ends at byte `drop.0` (`range.start` without
|
||||
/// one) and takes the character style `drop.1` ("" = none).
|
||||
pub(crate) fn shape_para(
|
||||
db: &ScopedFonts<'_>,
|
||||
styles: &Styles,
|
||||
story: &Story,
|
||||
pi: usize,
|
||||
range: std::ops::Range<usize>,
|
||||
para_chars: &CharProps,
|
||||
auto_leading: TypeEnv,
|
||||
sub: &SubstCtx,
|
||||
table: &mut StyleTable<'_>,
|
||||
nested: &[designcraft_doc::NestedStyle],
|
||||
grep: &[designcraft_doc::GrepStyle],
|
||||
lines: &[(std::ops::Range<usize>, String)],
|
||||
drop: (usize, &str),
|
||||
) -> ShapedPara {
|
||||
let _ = pi;
|
||||
let mut glyphs = Vec::with_capacity(range.len());
|
||||
// Nested line styles lie under nested and GREP styles, and the drop cap's style over them all
|
||||
// (later overlays win).
|
||||
pub(crate) struct ShapeInput<'a, 'font> {
|
||||
pub(crate) db: &'a ScopedFonts<'font>,
|
||||
pub(crate) styles: &'a Styles,
|
||||
pub(crate) story: &'a Story,
|
||||
pub(crate) range: std::ops::Range<usize>,
|
||||
pub(crate) para_chars: &'a CharProps,
|
||||
pub(crate) auto_leading: TypeEnv,
|
||||
pub(crate) sub: &'a SubstCtx,
|
||||
pub(crate) nested: &'a [designcraft_doc::NestedStyle],
|
||||
pub(crate) grep: &'a [designcraft_doc::GrepStyle],
|
||||
pub(crate) lines: &'a [(std::ops::Range<usize>, String)],
|
||||
pub(crate) drop_cap: (usize, &'a str),
|
||||
}
|
||||
|
||||
pub(crate) fn shape_para(input: ShapeInput<'_, '_>, table: &mut StyleTable<'_>) -> ShapedPara {
|
||||
let db = input.db;
|
||||
let sub = input.sub;
|
||||
let range = input.range.clone();
|
||||
let auto_leading = input.auto_leading;
|
||||
let (pieces, auto_tcy) = resolve_pieces(&input, table);
|
||||
let mut glyphs = shape_pieces(&input, table, pieces);
|
||||
fit_auto_tcy(&mut glyphs, &auto_tcy);
|
||||
collapse_tcy(&mut glyphs, sub.vertical);
|
||||
crate::ruby::reserve(db, table.styles, &mut glyphs, auto_leading.glyph_fallback, sub.vertical);
|
||||
ShapedPara { glyphs, range }
|
||||
}
|
||||
|
||||
fn resolve_pieces(input: &ShapeInput<'_, '_>, table: &mut StyleTable<'_>) -> (Vec<Piece>, Vec<std::ops::Range<usize>>) {
|
||||
let db = input.db;
|
||||
let styles = input.styles;
|
||||
let story = input.story;
|
||||
let range = input.range.clone();
|
||||
let para_chars = input.para_chars;
|
||||
let auto_leading = input.auto_leading;
|
||||
let sub = input.sub;
|
||||
let nested = input.nested;
|
||||
let grep = input.grep;
|
||||
let lines = input.lines;
|
||||
let drop_cap = input.drop_cap;
|
||||
let mut overlays: Vec<(std::ops::Range<usize>, String)> = lines.to_vec();
|
||||
// The drop cap's style is the nested styles' leading "through 1 Dropcap" entry (the form IDML
|
||||
// stores it in). Set (`[None]` included), it restyles that entry; a real one stands in for a
|
||||
// missing entry. Either way the nested styles after it start after the drop cap.
|
||||
let set = Some(drop.1).filter(|s| !s.is_empty());
|
||||
let set = Some(drop_cap.1).filter(|s| !s.is_empty());
|
||||
let cap_style = set.filter(|s| *s != designcraft_doc::NO_CHAR_STYLE);
|
||||
let with_cap: Vec<designcraft_doc::NestedStyle>;
|
||||
let nested = match set {
|
||||
@@ -357,12 +377,12 @@ pub(crate) fn shape_para(
|
||||
_ => nested,
|
||||
};
|
||||
if !nested.is_empty() || !grep.is_empty() {
|
||||
overlays.extend(crate::overlay::overlays(&story.text, range.clone(), nested, grep, drop.0));
|
||||
overlays.extend(crate::overlay::overlays(&story.text, range.clone(), nested, grep, drop_cap.0));
|
||||
}
|
||||
if let Some(s) = cap_style
|
||||
&& drop.0 > range.start
|
||||
&& drop_cap.0 > range.start
|
||||
{
|
||||
overlays.push((range.start..drop.0, s.to_string()));
|
||||
overlays.push((range.start..drop_cap.0, s.to_string()));
|
||||
}
|
||||
// Each run, cut where nested / GREP styles start and end.
|
||||
let mut segments: Vec<(usize, usize, Option<&str>, &designcraft_doc::CharFormat)> = Vec::new();
|
||||
@@ -474,6 +494,15 @@ pub(crate) fn shape_para(
|
||||
pieces.push(Piece { range: k..b, props, style, env: auto_leading, hidden: false, composite_note: false });
|
||||
}
|
||||
}
|
||||
(pieces, auto_tcy)
|
||||
}
|
||||
|
||||
fn shape_pieces(input: &ShapeInput<'_, '_>, table: &mut StyleTable<'_>, pieces: Vec<Piece>) -> Vec<Glyph> {
|
||||
let db = input.db;
|
||||
let styles = input.styles;
|
||||
let story = input.story;
|
||||
let sub = input.sub;
|
||||
let mut glyphs = Vec::new();
|
||||
let mut i = 0;
|
||||
while i < pieces.len() {
|
||||
let first = &pieces[i];
|
||||
@@ -562,10 +591,7 @@ pub(crate) fn shape_para(
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
fit_auto_tcy(&mut glyphs, &auto_tcy);
|
||||
collapse_tcy(&mut glyphs, sub.vertical);
|
||||
crate::ruby::reserve(db, table.styles, &mut glyphs, auto_leading.glyph_fallback, sub.vertical);
|
||||
ShapedPara { glyphs, range }
|
||||
glyphs
|
||||
}
|
||||
|
||||
/// Combining marks that start a run are shaped with the letter before them when the two runs
|
||||
|
||||
Reference in New Issue
Block a user