From 5de168955bd285ac3b5370cb1fbbfa6009e45247 Mon Sep 17 00:00:00 2001 From: Grzegorz Martens Date: Tue, 10 Nov 2026 06:59:28 +0100 Subject: [PATCH] M7: the docs say which failed reads the allowance doesn't count `Metrics::past_allowance`, `Reads::decode` and the Type 3 glyph-box read said a stream that fails on its own data isn't counted as refused even when it spends the last of the allowance. A stream past its own limit fails with any allowance, yet with no more left than its stages' total it is counted, as `Reads::decode` itself says just above. They now name the case that isn't: a read that fails without the allowance refusing it, such as an empty stream behind a filter nothing decodes. The program-list test's doc no longer says more than it checks. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY --- crates/fonts/src/pdf.rs | 12 +++++++----- crates/fonts/src/pdf/tests.rs | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/crates/fonts/src/pdf.rs b/crates/fonts/src/pdf.rs index 00ace3bf44..37bbc52819 100644 --- a/crates/fonts/src/pdf.rs +++ b/crates/fonts/src/pdf.rs @@ -72,7 +72,8 @@ pub struct Metrics { /// was given was spent: what it would have decided (code lengths, CIDs, widths, meanings, a /// Type 3 font's height) is a guess. It is set exactly when the allowance counted a refusal /// during the read ([`DecodeBudget::refused`]), which callers that share it may refuse on; a - /// stream that fails on its own data doesn't set it, with or without anything left. + /// read that fails without one, such as an empty stream behind a filter nothing decodes, + /// doesn't set it, with or without anything left. pub past_allowance: bool, } @@ -423,9 +424,9 @@ impl<'a> Reads<'a> { /// made and `max`, it is charged those two and isn't refused; otherwise, with more left than /// the stages' total (behind one filter, `max`), it is charged that total and isn't refused; /// with no more left than that total, it spends all of it and counts as refused. Only what - /// the allowance counts as refused ([`DecodeBudget::refused`]) is: a stream that fails on its - /// own data fails the same with more left, even when it spends the last of it or nothing was - /// left, and callers that refuse on that count must agree with [`Metrics::past_allowance`]. + /// the allowance counts as refused ([`DecodeBudget::refused`]) is noted: a read that fails + /// without one, such as an empty stream behind a filter nothing decodes, fails the same with + /// more left, and callers that refuse on that count must agree with [`Metrics::past_allowance`]. fn decode(&mut self, stream: &Stream, max: usize) -> Option> { let refused = self.budget.refused(); match self.budget.decode_stages_within(self.doc, stream, max) { @@ -502,7 +503,8 @@ fn type3_glyph_box(doc: &Document, font: &Dict, reads: &mut Reads<'_>) -> Option left = left.saturating_sub(before.saturating_sub(reads.budget.remaining())); let Ok(data) = read else { // As in `Reads::decode`, only a refusal the allowance counts: a description that fails - // on its own data, spending the last of it, leaves the rest to be refused above. + // without one (an empty one nothing decodes, once the allowance is spent) leaves the + // rest to be read, or refused above. if reads.budget.refused() > refused { // The rest go unread too, for want of the reading's allowance. reads.refused = true; diff --git a/crates/fonts/src/pdf/tests.rs b/crates/fonts/src/pdf/tests.rs index 7c1d5ca0c0..4c5f90044f 100644 --- a/crates/fonts/src/pdf/tests.rs +++ b/crates/fonts/src/pdf/tests.rs @@ -437,7 +437,7 @@ fn each_stage_of_a_metrics_stream_is_held_to_its_limit() { /// The editor's program list reads a legacy Thai font's metrics after its program. With the program /// spending the allowance to the byte, a ToUnicode that is an empty stream behind a filter nothing /// decodes, unread with any allowance, doesn't put the metrics past the allowance: the program is -/// listed, as with plenty. A ToUnicode with data is refused by the allowance, and the program isn't. +/// listed. A ToUnicode with data is refused by the allowance, and the program isn't. #[test] fn an_editors_program_is_listed_past_a_stream_unread_on_its_own() { let bytes = include_bytes!("../../../../assets/fonts/Inter-Regular.ttf");