From bb96f1455f20c52bfd4fe0ec0910a642d25a7cd1 Mon Sep 17 00:00:00 2001 From: moppeeer Date: Wed, 14 Oct 2026 21:32:57 +0200 Subject: [PATCH 1/2] Fill with Placeholder Text in paragraphs, as InDesign does InDesign's placeholder text comes in paragraphs; Zero2Graphic's magazine tutorial styles its second paragraph as an 18 pt lead (2:16). DesignCraft filled the frame with one paragraph, and the last filler sentence ran into the first ("interit.Ovid"). Paragraphs now hold 2 to 5 sentences, and an overset fill is trimmed to the last whole sentence. Co-Authored-By: Claude Opus 5.5 --- ROADMAP.md | 1 + crates/engine/src/cmd/text.rs | 45 +++++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 9f8168e50..aadbad502 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -132,6 +132,7 @@ list) · [roadmap.md](docs/roadmap.md) (milestones, plan) · [parity-checklist.m Newest first. +- **2026-10-14** · Type › Fill with Placeholder Text fills a frame with paragraphs of a few sentences, as InDesign does, so one paragraph can be styled on its own (it was one long paragraph, and the filler ran sentences together, "interit.Ovid"). No readiness percentage change. - **2026-10-13** · Object › Fitting has InDesign's shortcuts: Fill Frame Proportionally ⌘⌥⇧C, Fit Content Proportionally ⌘⌥⇧E, Fit Frame to Content ⌘⌥C, Fit Content to Frame ⌘⌥E, Center Content ⌘⇧E. No readiness percentage change. - **2026-10-13** · Gradients between process colours blend as inks, as InDesign shows them: magenta to cyan passes through C 50 M 50 (a light lilac) on screen instead of the darker RGB mix, and a midpoint between CMYK or grey stops no longer turns the PDF shading RGB. No readiness percentage change. - **2026-10-14** · The Swatches panel has InDesign's header: a Fill/Stroke proxy, Formatting Affects Container/Text and the Tint of the colour in front (a tint alone keeps each object's own swatch). Clicking a swatch applies it to the fill or stroke in front (of the text, when formatting affects text), and the bottom row has New Color Group, New Swatch and Delete Swatch. No readiness percentage change. diff --git a/crates/engine/src/cmd/text.rs b/crates/engine/src/cmd/text.rs index add94aca9..f6ed4eca5 100644 --- a/crates/engine/src/cmd/text.rs +++ b/crates/engine/src/cmd/text.rs @@ -1398,20 +1398,30 @@ fn fill_placeholder(s: &mut Session, p: &Value) -> Result { story_of(s, p).ok_or_else(|| bad("type.fillWithPlaceholder", "select a text frame"))? } }; - // Fill until overset: repeat the filler sentences. + // Fill until overset with paragraphs of a few sentences each, as InDesign's placeholder text + // comes (so a paragraph can be styled on its own). + const SENTENCES_PER_PARAGRAPH: [usize; 6] = [3, 4, 2, 5, 3, 4]; let mut text = String::new(); - let sentences: Vec<&str> = PLACEHOLDER.split_inclusive(". ").collect(); + let sentences: Vec<&str> = PLACEHOLDER.split_inclusive(". ").map(str::trim_end).collect(); let doc = s.doc()?.doc.clone(); let mut d2 = (*doc).clone(); + let (mut paragraph, mut in_paragraph) = (0, 0); for i in 0..400 { text.push_str(sentences[i % sentences.len()]); + in_paragraph += 1; + if in_paragraph >= SENTENCES_PER_PARAGRAPH[paragraph % SENTENCES_PER_PARAGRAPH.len()] { + text.push('\n'); + (paragraph, in_paragraph) = (paragraph + 1, 0); + } else { + text.push(' '); + } if i % 3 == 2 { d2.set_story_text(sid, &text)?; let cs = compose::compose_story(&d2, sid, &compose::ComposeOptions::default()); if cs.is_overset() { // Trim to the last sentence that fits. - let cut = cs.overset_at.unwrap_or(text.len()); - let keep = text[..cut.min(text.len())].rfind(". ").map(|k| k + 1).unwrap_or(cut); + let cut = cs.overset_at.unwrap_or(text.len()).min(text.len()); + let keep = text.get(..cut).and_then(|t| t.rfind('.')).map(|k| k + 1).unwrap_or(cut); text.truncate(keep); break; } @@ -2559,3 +2569,30 @@ mod sentence_selection_tests { assert_eq!(para_bounds(s, s.len()), (s.len(), s.len())); } } + +#[cfg(test)] +mod placeholder_tests { + use serde_json::json; + + use crate::Session; + + /// Fill with Placeholder Text gives paragraphs of a few sentences, as InDesign's does (it was one + /// paragraph, and the filler ran sentences together: "interit.Ovid"). + #[test] + fn placeholder_text_comes_in_paragraphs() { + let mut s = Session::new(); + s.execute("file.new", &json!({})).unwrap(); + let r = s.execute("frame.create", &json!({"rect": [36, 36, 336, 736], "content": "text", "text": ""})).unwrap(); + s.execute("type.fillWithPlaceholder", &json!({"frame": r["id"]})).unwrap(); + let st = s.execute("story.get", &json!({"story": r["story"]})).unwrap(); + let text = st["text"].as_str().unwrap(); + let paragraphs: Vec<&str> = text.split('\n').collect(); + assert!(paragraphs.len() >= 4, "{} paragraphs", paragraphs.len()); + for p in ¶graphs { + assert!(p.ends_with('.') && !p.starts_with(' '), "a paragraph of whole sentences: {p:?}"); + } + assert!(!text.contains(".O") && !text.contains(".L"), "sentences are spaced: {text}"); + assert!(st["overset"].is_null(), "nothing overset: {}", st["overset"]); + assert!(text.len() > 1500, "the frame is filled: {}", text.len()); + } +} From 017b925189565c8397ca294812011e9840836e0f Mon Sep 17 00:00:00 2001 From: moppeeer Date: Wed, 14 Oct 2026 22:31:32 +0200 Subject: [PATCH 2/2] Placeholder paragraphs as short as InDesign's (2 to 5 sentences) Measured in Zero2Graphic's video: InDesign's placeholder paragraphs run 5, 10 (at 18 pt), 6 and 8 lines in a 5.9 cm column, about 2, 2, 3 and 3 of DesignCraft's filler sentences. The rhythm started 3, 4, 2, 5. Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/cmd/text.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/engine/src/cmd/text.rs b/crates/engine/src/cmd/text.rs index f6ed4eca5..52c3588c5 100644 --- a/crates/engine/src/cmd/text.rs +++ b/crates/engine/src/cmd/text.rs @@ -1398,9 +1398,9 @@ fn fill_placeholder(s: &mut Session, p: &Value) -> Result { story_of(s, p).ok_or_else(|| bad("type.fillWithPlaceholder", "select a text frame"))? } }; - // Fill until overset with paragraphs of a few sentences each, as InDesign's placeholder text + // Fill until overset with paragraphs of two to five sentences, as InDesign's placeholder text // comes (so a paragraph can be styled on its own). - const SENTENCES_PER_PARAGRAPH: [usize; 6] = [3, 4, 2, 5, 3, 4]; + const SENTENCES_PER_PARAGRAPH: [usize; 7] = [2, 2, 3, 3, 5, 2, 4]; let mut text = String::new(); let sentences: Vec<&str> = PLACEHOLDER.split_inclusive(". ").map(str::trim_end).collect(); let doc = s.doc()?.doc.clone();