@@ -643,6 +643,99 @@ describe('worker document layout session', () => {
643643 localeService . dispose ( ) ;
644644 } ) ;
645645
646+ it ( 'keeps modern explicit-page drawing geometry stable across block publications' , ( ) => {
647+ vi . stubGlobal ( 'document' , undefined ) ;
648+ vi . stubGlobal ( 'OffscreenCanvas' , class {
649+ getContext ( ) {
650+ return {
651+ font : '' ,
652+ textBaseline : 'alphabetic' ,
653+ measureText ( content : string ) {
654+ return {
655+ width : content . length * 7 ,
656+ fontBoundingBoxAscent : 9 ,
657+ fontBoundingBoxDescent : 3 ,
658+ actualBoundingBoxAscent : 8 ,
659+ actualBoundingBoxDescent : 2 ,
660+ } ;
661+ } ,
662+ } ;
663+ }
664+ } ) ;
665+
666+ const T = DataStreamTreeTokenType ;
667+ const firstDrawingId = 'modern-first-page-drawing' ;
668+ const secondDrawingId = 'modern-second-page-drawing' ;
669+ const content = [
670+ `First ${ T . CUSTOM_BLOCK } page${ T . PARAGRAPH } ${ T . PAGE_BREAK } ` ,
671+ `Second ${ T . CUSTOM_BLOCK } page${ T . PARAGRAPH } ${ T . PAGE_BREAK } ` ,
672+ ...Array . from ( { length : 20 } , ( _ , index ) => `Trailing paragraph ${ index } ${ T . PARAGRAPH } ` ) ,
673+ ] . join ( '' ) ;
674+ const sourceModel = createDocumentModelWithStyle ( content , { } ) ;
675+ const snapshot = sourceModel . getSnapshot ( ) ;
676+ if ( snapshot . body == null ) {
677+ throw new Error ( 'Expected the modern drawing test document body.' ) ;
678+ }
679+ const firstDrawingStart = content . indexOf ( T . CUSTOM_BLOCK ) ;
680+ const secondDrawingStart = content . indexOf ( T . CUSTOM_BLOCK , firstDrawingStart + 1 ) ;
681+ snapshot . body . customBlocks = [
682+ { startIndex : firstDrawingStart , blockId : firstDrawingId } ,
683+ { startIndex : secondDrawingStart , blockId : secondDrawingId } ,
684+ ] ;
685+ snapshot . drawings = Object . fromEntries ( [ firstDrawingId , secondDrawingId ] . map ( ( drawingId ) => [ drawingId , {
686+ drawingId,
687+ drawingType : DrawingTypeEnum . DRAWING_BLOCK ,
688+ unitId : snapshot . id ,
689+ subUnitId : snapshot . id ,
690+ layoutType : PositionedObjectLayoutType . WRAP_NONE ,
691+ docTransform : {
692+ angle : 0 ,
693+ positionH : { relativeFrom : ObjectRelativeFromH . PAGE , posOffset : 0 } ,
694+ positionV : { relativeFrom : ObjectRelativeFromV . PARAGRAPH , posOffset : 0 } ,
695+ size : { width : 40 , height : 20 } ,
696+ } ,
697+ } ] ) ) ;
698+ const dataModel = new DocumentDataModel ( snapshot ) ;
699+ dataModel . updateDocumentStyle ( { documentFlavor : DocumentFlavor . MODERN } ) ;
700+ const session = new DocumentLayoutSession ( dataModel , new LocaleService ( ) ) ;
701+ const targetModel = new DocumentDataModel ( structuredClone ( dataModel . getSnapshot ( ) ) ) ;
702+ const targetSkeleton = DocumentSkeleton . create ( new DocumentViewModel ( targetModel ) , new LocaleService ( ) ) ;
703+ const synchronousModel = new DocumentDataModel ( structuredClone ( dataModel . getSnapshot ( ) ) ) ;
704+ const synchronousSkeleton = DocumentSkeleton . create ( new DocumentViewModel ( synchronousModel ) , new LocaleService ( ) ) ;
705+ synchronousSkeleton . calculate ( ) ;
706+ const generation = session . start ( { reason : 'initial' } ) ;
707+ const secondDrawingTops : number [ ] = [ ] ;
708+ let result = session . step ( generation , 0 ) ;
709+ for ( let step = 0 ; step < 100 ; step ++ ) {
710+ if ( result . publication ?. kind === 'block' ) {
711+ targetSkeleton . applyLayoutPublication ( structuredClone ( result . publication ) , result . progress ) ;
712+ const drawing = result . publication . block . skeDrawings . find ( ( [ drawingId ] ) => drawingId === secondDrawingId ) ?. [ 1 ] ;
713+ if ( drawing != null && ! result . progress . complete ) {
714+ secondDrawingTops . push ( drawing . aTop ) ;
715+ }
716+ }
717+ if ( result . progress . complete ) {
718+ break ;
719+ }
720+ result = session . step ( generation , 0 ) ;
721+ }
722+
723+ expect ( result . progress ) . toMatchObject ( { complete : true , mode : 'continuous' , pageCount : 1 } ) ;
724+ expect ( secondDrawingTops . length ) . toBeGreaterThan ( 1 ) ;
725+ expect ( new Set ( secondDrawingTops ) . size ) . toBe ( 1 ) ;
726+ expect ( normalizeSkeleton ( targetSkeleton . getSkeletonData ( ) ) ) . toEqual (
727+ normalizeSkeleton ( synchronousSkeleton . getSkeletonData ( ) )
728+ ) ;
729+
730+ synchronousSkeleton . dispose ( ) ;
731+ synchronousModel . dispose ( ) ;
732+ targetSkeleton . dispose ( ) ;
733+ targetModel . dispose ( ) ;
734+ session . dispose ( ) ;
735+ dataModel . dispose ( ) ;
736+ sourceModel . dispose ( ) ;
737+ } ) ;
738+
646739 it ( 'preserves tables, column groups, drawings, lists, headers and footers across Worker transport' , ( ) => {
647740 vi . stubGlobal ( 'document' , undefined ) ;
648741 vi . stubGlobal ( 'OffscreenCanvas' , class {
0 commit comments