fix(format): bound the characters Sketch text runs write in all

This commit is contained in:
Dylan M. Taylor
2026-11-04 04:50:11 +00:00
parent 4bb35d5266
commit deee3d8acf
2 changed files with 19 additions and 0 deletions
+9
View File
@@ -14,6 +14,9 @@ use super::tree::{Cx, color};
const MAX_CHARS: usize = 1 << 20;
/// Most runs read from one text layer.
const MAX_RUNS: usize = 10_000;
/// Most characters that styled runs may write in all, overlaps counted again (a run's range is
/// rewritten in full, even where later runs overwrite it).
const MAX_RUN_WORK: usize = 16 * MAX_CHARS;
/// Fill `node` (a TEXT node) from text layer `l`.
pub(crate) fn node(cx: &mut Cx, l: &Value, node: &mut JsonMap<String, Value>) {
@@ -80,10 +83,16 @@ pub(crate) fn node(cx: &mut Cx, l: &Value, node: &mut JsonMap<String, Value>) {
// Runs that differ from the first: a style id per UTF-16 unit.
let mut ids = vec![0u64; units];
let mut table = JsonMap::new();
let mut work = 0usize;
for (start, end, s) in &styles {
if *s == base {
continue;
}
work = work.saturating_add(end.saturating_sub(*start));
if work > MAX_RUN_WORK {
cx.warn("text whose styled runs overlap a great deal keeps the first runs' styles");
break;
}
let id = table.len() as u64 + 1;
table.insert(id.to_string(), s.clone());
for slot in ids.get_mut(*start..*end).into_iter().flatten() {
+10
View File
@@ -271,3 +271,13 @@ fn later_runs_dont_inherit_the_first_runs_settings() {
assert_eq!(second.line_height, figcraft_doc::LineHeight::Auto, "{second:?}");
assert!(second.variations.is_empty(), "{second:?}");
}
#[test]
fn runs_that_overlap_a_great_deal_stop_being_applied() {
// 17 runs over the whole of a text of 2^20 characters: 17 × 2^20 writes without the limit.
let n = 1 << 20;
let mut runs = vec![(0, 1, "Helvetica", 10.0, BLACK)];
runs.extend((0..17).map(|i| (0, n, "Helvetica", 11.0 + f64::from(i), BLACK)));
let (_, warnings) = open(vec![text("Long", 0.0, 0.0, 200.0, 20.0, &"a".repeat(n), &runs)]);
assert!(warnings.iter().any(|w| w.contains("overlap a great deal")), "{warnings:?}");
}