@@ -54,6 +54,8 @@ struct DistributionSnapshot<'a, 'b> {
5454
5555/// A laid out item in a distribution.
5656enum Item < ' a , ' b > {
57+ /// An introspection tag.
58+ Tag ( & ' a Tag ) ,
5759 /// Absolute spacing and its weakness level.
5860 Abs ( Abs , u8 ) ,
5961 /// Fractional spacing or a fractional block.
@@ -69,6 +71,7 @@ impl Item<'_, '_> {
6971 /// consists solely of such items.
7072 fn migratable ( & self ) -> bool {
7173 match self {
74+ Self :: Tag ( _) => true ,
7275 Self :: Frame ( frame, _) => {
7376 frame. size ( ) . is_zero ( )
7477 && frame. items ( ) . all ( |( _, item) | {
@@ -126,6 +129,15 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
126129 self . composer . work . tags . push ( tag) ;
127130 }
128131
132+ /// Generate items for pending tags.
133+ fn flush_tags ( & mut self ) {
134+ if !self . composer . work . tags . is_empty ( ) {
135+ let tags = & mut self . composer . work . tags ;
136+ self . items . extend ( tags. iter ( ) . copied ( ) . map ( Item :: Tag ) ) ;
137+ tags. clear ( ) ;
138+ }
139+ }
140+
129141 /// Processes relative spacing.
130142 fn rel ( & mut self , amount : Rel < Abs > , weakness : u8 ) {
131143 let amount = amount. relative_to ( self . regions . base ( ) . y ) ;
@@ -157,7 +169,7 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
157169 }
158170 return false ;
159171 }
160- Item :: Abs ( ..) | Item :: Placed ( ..) => { }
172+ Item :: Tag ( _ ) | Item :: Abs ( ..) | Item :: Placed ( ..) => { }
161173 Item :: Fr ( .., None ) => return false ,
162174 Item :: Frame ( ..) | Item :: Fr ( .., Some ( _) ) => return true ,
163175 }
@@ -174,7 +186,7 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
174186 self . items . remove ( i) ;
175187 break ;
176188 }
177- Item :: Abs ( ..) | Item :: Placed ( ..) => { }
189+ Item :: Tag ( _ ) | Item :: Abs ( ..) | Item :: Placed ( ..) => { }
178190 Item :: Frame ( ..) | Item :: Fr ( ..) => break ,
179191 }
180192 }
@@ -185,7 +197,7 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
185197 for item in self . items . iter ( ) . rev ( ) {
186198 match * item {
187199 Item :: Abs ( amount, 1 ..) => return amount,
188- Item :: Abs ( ..) | Item :: Placed ( ..) => { }
200+ Item :: Tag ( _ ) | Item :: Abs ( ..) | Item :: Placed ( ..) => { }
189201 Item :: Frame ( ..) | Item :: Fr ( ..) => break ,
190202 }
191203 }
@@ -219,18 +231,20 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
219231
220232 /// Processes an unbreakable block.
221233 fn single ( & mut self , single : & ' b SingleChild < ' a > ) -> FlowResult < ( ) > {
222- // Handle fractionally sized blocks.
223- if let Some ( fr) = single. fr {
224- self . items . push ( Item :: Fr ( fr, Some ( single) ) ) ;
225- return Ok ( ( ) ) ;
226- }
227-
228234 // Lay out the block.
229235 let frame = single. layout (
230236 self . composer . engine ,
231237 Region :: new ( self . regions . base ( ) , self . regions . expand ) ,
232238 ) ?;
233239
240+ // Handle fractionally sized blocks.
241+ if let Some ( fr) = single. fr {
242+ self . composer . footnotes ( & self . regions , & frame, Abs :: zero ( ) , false ) ?;
243+ self . flush_tags ( ) ;
244+ self . items . push ( Item :: Fr ( fr, Some ( single) ) ) ;
245+ return Ok ( ( ) ) ;
246+ }
247+
234248 // If the block doesn't fit and a followup region may improve things,
235249 // finish the region.
236250 if !self . regions . size . y . fits ( frame. height ( ) ) && self . regions . may_progress ( ) {
@@ -289,7 +303,7 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
289303 /// Processes an in-flow frame, generated from a line or block.
290304 fn frame (
291305 & mut self ,
292- mut frame : Frame ,
306+ frame : Frame ,
293307 align : Axes < FixedAlignment > ,
294308 sticky : bool ,
295309 breakable : bool ,
@@ -307,26 +321,13 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
307321 self . sticky = None ;
308322 }
309323
310- if !frame. is_empty ( ) {
311- // Drain tags.
312- let tags = & mut self . composer . work . tags ;
313- if !tags. is_empty ( ) {
314- frame. prepend_multiple (
315- tags. iter ( ) . map ( |& tag| ( Point :: zero ( ) , FrameItem :: Tag ( tag. clone ( ) ) ) ) ,
316- ) ;
317- }
318-
319- // Handle footnotes.
320- self . composer
321- . footnotes ( & self . regions , & frame, frame. height ( ) , breakable) ?;
322-
323- // Clear the drained tags _after_ the footnotes are handled because
324- // a [`Stop::Finish`] could otherwise lose them.
325- self . composer . work . tags . clear ( ) ;
326- }
324+ // Handle footnotes.
325+ self . composer
326+ . footnotes ( & self . regions , & frame, frame. height ( ) , breakable) ?;
327327
328328 // Push an item for the frame.
329329 self . regions . size . y -= frame. height ( ) ;
330+ self . flush_tags ( ) ;
330331 self . items . push ( Item :: Frame ( frame, align) ) ;
331332 Ok ( ( ) )
332333 }
@@ -341,11 +342,16 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
341342 // ends up at a break due to the float.
342343 let weak_spacing = self . weak_spacing ( ) ;
343344 self . regions . size . y += weak_spacing;
344- self . composer . float ( placed, & self . regions , self . items . is_empty ( ) ) ?;
345+ self . composer . float (
346+ placed,
347+ & self . regions ,
348+ self . items . iter ( ) . any ( |item| matches ! ( item, Item :: Frame ( ..) ) ) ,
349+ ) ?;
345350 self . regions . size . y -= weak_spacing;
346351 } else {
347352 let frame = placed. layout ( self . composer . engine , self . regions . base ( ) ) ?;
348353 self . composer . footnotes ( & self . regions , & frame, Abs :: zero ( ) , true ) ?;
354+ self . flush_tags ( ) ;
349355 self . items . push ( Item :: Placed ( frame, placed) ) ;
350356 }
351357 Ok ( ( ) )
@@ -382,46 +388,53 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
382388 init : DistributionSnapshot < ' a , ' b > ,
383389 forced : bool ,
384390 ) -> FlowResult < Frame > {
385- if !forced {
386- if !self . items . is_empty ( ) && self . items . iter ( ) . all ( Item :: migratable) {
387- // Restore the initial state of all items are migratable.
388- self . restore ( init) ;
389- } else {
390- // If we ended on a sticky block, but are not yet at the end of
391- // the flow, restore the saved checkpoint to move the sticky
392- // suffix to the next region.
393- if let Some ( snapshot) = self . sticky . take ( ) {
394- self . restore ( snapshot)
395- }
391+ if forced {
392+ // If this is the very end of the flow, flush pending tags.
393+ self . flush_tags ( ) ;
394+ } else if !self . items . is_empty ( ) && self . items . iter ( ) . all ( Item :: migratable) {
395+ // Restore the initial state of all items are migratable.
396+ self . restore ( init) ;
397+ } else {
398+ // If we ended on a sticky block, but are not yet at the end of
399+ // the flow, restore the saved checkpoint to move the sticky
400+ // suffix to the next region.
401+ if let Some ( snapshot) = self . sticky . take ( ) {
402+ self . restore ( snapshot)
396403 }
397404 }
398405
399406 self . trim_spacing ( ) ;
400407
401408 let mut frs = Fr :: zero ( ) ;
402409 let mut used = Size :: zero ( ) ;
410+ let mut has_fr_child = false ;
403411
404412 // Determine the amount of used space and the sum of fractionals.
405413 for item in & self . items {
406414 match item {
407415 Item :: Abs ( v, _) => used. y += * v,
408- Item :: Fr ( v, _) => frs += * v,
416+ Item :: Fr ( v, child) => {
417+ frs += * v;
418+ has_fr_child |= child. is_some ( ) ;
419+ }
409420 Item :: Frame ( frame, _) => {
410421 used. y += frame. height ( ) ;
411422 used. x . set_max ( frame. width ( ) ) ;
412423 }
413- Item :: Placed ( ..) => { }
424+ Item :: Tag ( _ ) | Item :: Placed ( ..) => { }
414425 }
415426 }
416427
417428 // When we have fractional spacing, occupy the remaining space with it.
418429 let mut fr_space = Abs :: zero ( ) ;
419- let mut fr_frames = vec ! [ ] ;
420430 if frs. get ( ) > 0.0 && region. size . y . is_finite ( ) {
421431 fr_space = region. size . y - used. y ;
422432 used. y = region. size . y ;
433+ }
423434
424- // Lay out fractionally sized blocks.
435+ // Lay out fractionally sized blocks.
436+ let mut fr_frames = vec ! [ ] ;
437+ if has_fr_child {
425438 for item in & self . items {
426439 let Item :: Fr ( v, Some ( single) ) = item else { continue } ;
427440 let length = v. share ( frs, fr_space) ;
@@ -439,6 +452,7 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
439452
440453 // Determine the region's size.
441454 let size = region. expand . select ( region. size , used. min ( region. size ) ) ;
455+ let free = size. y - used. y ;
442456
443457 let mut output = Frame :: soft ( size) ;
444458 let mut ruler = FixedAlignment :: Start ;
@@ -448,6 +462,11 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
448462 // Position all items.
449463 for item in self . items {
450464 match item {
465+ Item :: Tag ( tag) => {
466+ let y = offset + ruler. position ( free) ;
467+ let pos = Point :: with_y ( y) ;
468+ output. push ( pos, FrameItem :: Tag ( tag. clone ( ) ) ) ;
469+ }
451470 Item :: Abs ( v, _) => {
452471 offset += v;
453472 }
@@ -465,7 +484,7 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
465484 ruler = ruler. max ( align. y ) ;
466485
467486 let x = align. x . position ( size. x - frame. width ( ) ) ;
468- let y = offset + ruler. position ( size . y - used . y ) ;
487+ let y = offset + ruler. position ( free ) ;
469488 let pos = Point :: new ( x, y) ;
470489 offset += frame. height ( ) ;
471490
@@ -475,7 +494,7 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
475494 let x = placed. align_x . position ( size. x - frame. width ( ) ) ;
476495 let y = match placed. align_y . unwrap_or_default ( ) {
477496 Some ( align) => align. position ( size. y - frame. height ( ) ) ,
478- _ => offset + ruler. position ( size . y - used . y ) ,
497+ _ => offset + ruler. position ( free ) ,
479498 } ;
480499
481500 let pos = Point :: new ( x, y)
@@ -486,16 +505,6 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
486505 }
487506 }
488507
489- // If this is the very end of the flow, drain trailing tags.
490- if forced && !self . composer . work . tags . is_empty ( ) {
491- let tags = & mut self . composer . work . tags ;
492- let pos = Point :: with_y ( offset) ;
493- output. push_multiple (
494- tags. iter ( ) . map ( |& tag| ( pos, FrameItem :: Tag ( tag. clone ( ) ) ) ) ,
495- ) ;
496- tags. clear ( ) ;
497- }
498-
499508 Ok ( output)
500509 }
501510
0 commit comments