Edit text: a paragraph's new colour no longer spills into the text after it (#6)
Rewriting a paragraph (text_edit, Format text) draws it in its own text object, then sets the text state back for what follows, but not the fill colour. Changing one paragraph's colour therefore also coloured every paragraph after it in the same content stream (recolour the first paragraph of a page red and the rest of the page turns red). The rewritten paragraph is now wrapped in q … Q, so its colour and other graphics state stay with it. Co-authored-by: storybored379 <storybored379@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
096b515719
commit
24f0a8928e
|
|
@ -449,6 +449,28 @@ fn paragraphs_take_new_formatting() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_new_paragraph_colour_does_not_spill_into_the_text_after_it() {
|
||||
let mut doc = text_page("BT /F1 10 Tf 72 700 Td (First paragraph) Tj 0 -40 Td (Second paragraph) Tj ET");
|
||||
let style = text::BlockStyle { color: Some([1.0, 0.0, 0.0]), ..Default::default() };
|
||||
text::rewrite_block(&mut doc, 0, 0, None, &style).unwrap();
|
||||
let doc = reopen(&doc);
|
||||
// The fill colour in force where the second paragraph is shown (q/Q nest it).
|
||||
let ops = printcraft_content::parse(&page_content_bytes(&doc, 0)).ops;
|
||||
let (mut fill, mut stack) = (String::from("0 g"), Vec::new());
|
||||
for op in &ops {
|
||||
match op.op.as_slice() {
|
||||
b"q" => stack.push(fill.clone()),
|
||||
b"Q" => fill = stack.pop().unwrap_or_default(),
|
||||
b"g" | b"rg" | b"k" => fill = String::from_utf8_lossy(&printcraft_content::serialize_ops(std::slice::from_ref(op))).trim().to_string(),
|
||||
b"Tj" if op.operands.first().and_then(|o| o.as_string()).is_some_and(|s| s.to_text() == "Second paragraph") => break,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
assert_eq!(fill, "0 g", "{ops:?}");
|
||||
assert_eq!(text::text_blocks(&doc, 0).unwrap()[1].text, "Second paragraph");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn justify_underline_and_spacing() {
|
||||
let src = "BT /F1 10 Tf 12 TL 100 700 Td (One two three four five six seven) Tj T* (eight nine ten eleven twelve) Tj T* (end) Tj ET";
|
||||
|
|
|
|||
|
|
@ -667,7 +667,8 @@ pub fn rewrite_block(doc: &mut Document, page: usize, block: usize, text: Option
|
|||
None => size * 1.2,
|
||||
};
|
||||
let n = printcraft_content::num;
|
||||
let mut block_ops = vec![Op::new("BT", vec![])];
|
||||
// In its own graphics state, so a new colour (or anything else) stops at the paragraph.
|
||||
let mut block_ops = vec![Op::new("q", vec![]), Op::new("BT", vec![])];
|
||||
let mut state = o_state.clone();
|
||||
state.word_spacing = 0.0;
|
||||
state.font = Some((show_font, size));
|
||||
|
|
@ -735,6 +736,7 @@ pub fn rewrite_block(doc: &mut Document, page: usize, block: usize, text: Option
|
|||
block_ops.push(Op::new("S", vec![]));
|
||||
block_ops.push(Op::new("Q", vec![]));
|
||||
}
|
||||
block_ops.push(Op::new("Q", vec![]));
|
||||
// What the text after the paragraph expects: the state at its BT.
|
||||
block_ops.extend(o.bt_state.ops().into_iter().filter(|op| !op.is("Tf") || o.bt_state.font.is_some()));
|
||||
let drop: std::collections::HashSet<usize> = members.iter().flat_map(|l| l.ops.iter().copied()).collect();
|
||||
|
|
|
|||
Loading…
Reference in New Issue