@@ -220,6 +220,57 @@ void main() {
220220 baselineCache.dispose ();
221221 });
222222
223+ test ('B2 末行補償:近滿末行不得把末字擠到下一行' , () async {
224+ final store = MeasurementStore ();
225+ final cache = ParagraphCache ();
226+ final fingerprint = _fingerprint (lastLineSpacingCompensation: true );
227+ final pump = LayoutPump (
228+ paragraphCache: cache,
229+ measurementStore: store,
230+ namespace: MeasurementNamespace (
231+ epoch: LayoutEpoch .initial,
232+ fingerprint: fingerprint,
233+ ),
234+ );
235+ const key = BlockKey (chapterIndex: 0 , blockIndex: 0 );
236+ const fontSize = 20.0 ;
237+ // 27 個無標點字元 + 寬 9 字 + 1px 殘餘 → 9/9/9 三行;末行 headroom
238+ // 僅 1px。補償上限若誤用間隙數(8)作分母,總增量 9×0.125 會超寬
239+ // 而把末字擠成第四行孤行。
240+ final text = '夜' * 27 ;
241+ pump.submit (
242+ LayoutTask (
243+ block: ChapterBlock (
244+ key: key,
245+ text: text,
246+ charRange: const HybridTextRange (0 , 27 ),
247+ sourceParagraphIndex: 0 ,
248+ ),
249+ epoch: LayoutEpoch .initial,
250+ fingerprint: fingerprint,
251+ textStyle: const HybridBlockTextStyle (
252+ fontSize: fontSize,
253+ lineHeight: 1.5 ,
254+ letterSpacing: 0 ,
255+ textAlign: ui.TextAlign .justify,
256+ ),
257+ contentWidth: fontSize * 9 + 1 ,
258+ ),
259+ );
260+
261+ expect (await pump.pumpPending (), 1 );
262+ final paragraph = cache.acquire (key, LayoutEpoch .initial)! ;
263+ final lines = paragraph.computeLineMetrics ();
264+ expect (lines.length, 3 , reason: '補償不得改變斷行(末字回捲即為 off-by-one)' );
265+ expect (
266+ lines.last.width,
267+ lessThanOrEqualTo (fontSize * 9 + 1 + 0.01 ),
268+ reason: '末行寬不得超出內容寬' ,
269+ );
270+ pump.dispose ();
271+ cache.dispose ();
272+ });
273+
223274 test ('justify 下段首縮排以 placeholder 保留原寬,字距不吸收縮排寬度' , () async {
224275 final store = MeasurementStore ();
225276 final cache = ParagraphCache ();
0 commit comments