@@ -2062,6 +2062,76 @@ describe('layoutHeaderFooter', () => {
20622062 expect ( layout . pages [ 0 ] . fragments [ 0 ] ) . toMatchObject ( { kind : 'image' , height : 40 } ) ;
20632063 } ) ;
20642064
2065+ it ( 'ignores far-away behindDoc anchored fragments when computing height' , ( ) => {
2066+ const paragraphBlock : FlowBlock = {
2067+ kind : 'paragraph' ,
2068+ id : 'para-1' ,
2069+ runs : [ { text : 'Header text' , fontFamily : 'Arial' , fontSize : 12 , pmStart : 1 , pmEnd : 12 } ] ,
2070+ } ;
2071+ const imageBlock : FlowBlock = {
2072+ kind : 'image' ,
2073+ id : 'img-1' ,
2074+ src : 'data:image/png;base64,xxx' ,
2075+ anchor : {
2076+ isAnchored : true ,
2077+ behindDoc : true ,
2078+ offsetV : 1000 ,
2079+ } ,
2080+ } ;
2081+ const paragraphMeasure : Measure = {
2082+ kind : 'paragraph' ,
2083+ lines : [ { fromRun : 0 , fromChar : 0 , toRun : 0 , toChar : 11 , width : 80 , ascent : 12 , descent : 3 , lineHeight : 15 } ] ,
2084+ totalHeight : 15 ,
2085+ } ;
2086+ const imageMeasure : Measure = {
2087+ kind : 'image' ,
2088+ width : 50 ,
2089+ height : 40 ,
2090+ } ;
2091+
2092+ const layout = layoutHeaderFooter ( [ paragraphBlock , imageBlock ] , [ paragraphMeasure , imageMeasure ] , {
2093+ width : 200 ,
2094+ height : 60 ,
2095+ } ) ;
2096+
2097+ expect ( layout . height ) . toBeCloseTo ( 15 ) ;
2098+ } ) ;
2099+
2100+ it ( 'includes near behindDoc anchored fragments when computing height' , ( ) => {
2101+ const paragraphBlock : FlowBlock = {
2102+ kind : 'paragraph' ,
2103+ id : 'para-1' ,
2104+ runs : [ { text : 'Header text' , fontFamily : 'Arial' , fontSize : 12 , pmStart : 1 , pmEnd : 12 } ] ,
2105+ } ;
2106+ const imageBlock : FlowBlock = {
2107+ kind : 'image' ,
2108+ id : 'img-1' ,
2109+ src : 'data:image/png;base64,xxx' ,
2110+ anchor : {
2111+ isAnchored : true ,
2112+ behindDoc : true ,
2113+ offsetV : - 20 ,
2114+ } ,
2115+ } ;
2116+ const paragraphMeasure : Measure = {
2117+ kind : 'paragraph' ,
2118+ lines : [ { fromRun : 0 , fromChar : 0 , toRun : 0 , toChar : 11 , width : 80 , ascent : 12 , descent : 3 , lineHeight : 15 } ] ,
2119+ totalHeight : 15 ,
2120+ } ;
2121+ const imageMeasure : Measure = {
2122+ kind : 'image' ,
2123+ width : 50 ,
2124+ height : 40 ,
2125+ } ;
2126+
2127+ const layout = layoutHeaderFooter ( [ paragraphBlock , imageBlock ] , [ paragraphMeasure , imageMeasure ] , {
2128+ width : 200 ,
2129+ height : 60 ,
2130+ } ) ;
2131+
2132+ expect ( layout . height ) . toBeGreaterThan ( 15 ) ;
2133+ } ) ;
2134+
20652135 it ( 'transforms page-relative anchor offsets by subtracting left margin' , ( ) => {
20662136 // An anchored image with hRelativeFrom='page' and offsetH=545 (absolute from page left)
20672137 // When left margin is 107, the image should be positioned at 545-107=438 within the header
@@ -2187,6 +2257,204 @@ describe('layoutHeaderFooter', () => {
21872257 // margin-relative anchors should not be transformed - offsetH stays at 50
21882258 expect ( imageFragment ! . x ) . toBe ( 50 ) ;
21892259 } ) ;
2260+
2261+ it ( 'ignores behindDoc DrawingBlock with extreme offset when computing height' , ( ) => {
2262+ const paragraphBlock : FlowBlock = {
2263+ kind : 'paragraph' ,
2264+ id : 'para-1' ,
2265+ runs : [ { text : 'Header text' , fontFamily : 'Arial' , fontSize : 12 , pmStart : 1 , pmEnd : 12 } ] ,
2266+ } ;
2267+ const drawingBlock : FlowBlock = {
2268+ kind : 'drawing' ,
2269+ id : 'drawing-1' ,
2270+ drawingKind : 'vectorShape' ,
2271+ anchor : {
2272+ isAnchored : true ,
2273+ behindDoc : true ,
2274+ offsetV : 2000 , // Extreme offset beyond overflow threshold
2275+ } ,
2276+ shape : {
2277+ type : 'Rectangle' ,
2278+ } ,
2279+ } ;
2280+ const paragraphMeasure : Measure = {
2281+ kind : 'paragraph' ,
2282+ lines : [ { fromRun : 0 , fromChar : 0 , toRun : 0 , toChar : 11 , width : 80 , ascent : 12 , descent : 3 , lineHeight : 15 } ] ,
2283+ totalHeight : 15 ,
2284+ } ;
2285+ const drawingMeasure : Measure = {
2286+ kind : 'drawing' ,
2287+ drawingKind : 'vectorShape' ,
2288+ width : 100 ,
2289+ height : 50 ,
2290+ scale : 1 ,
2291+ naturalWidth : 100 ,
2292+ naturalHeight : 50 ,
2293+ geometry : { width : 100 , height : 50 , rotation : 0 , flipH : false , flipV : false } ,
2294+ } ;
2295+
2296+ const layout = layoutHeaderFooter ( [ paragraphBlock , drawingBlock ] , [ paragraphMeasure , drawingMeasure ] , {
2297+ width : 200 ,
2298+ height : 60 ,
2299+ } ) ;
2300+
2301+ // Height should only include paragraph, not the extreme behindDoc drawing
2302+ expect ( layout . height ) . toBeCloseTo ( 15 ) ;
2303+ } ) ;
2304+
2305+ it ( 'includes non-behindDoc anchored fragments in height calculation' , ( ) => {
2306+ const paragraphBlock : FlowBlock = {
2307+ kind : 'paragraph' ,
2308+ id : 'para-1' ,
2309+ runs : [ { text : 'Header text' , fontFamily : 'Arial' , fontSize : 12 , pmStart : 1 , pmEnd : 12 } ] ,
2310+ } ;
2311+ const imageBlock : FlowBlock = {
2312+ kind : 'image' ,
2313+ id : 'img-1' ,
2314+ src : 'data:image/png;base64,xxx' ,
2315+ anchor : {
2316+ isAnchored : true ,
2317+ behindDoc : false , // NOT behindDoc - should be included in height
2318+ offsetV : 20 ,
2319+ } ,
2320+ } ;
2321+ const paragraphMeasure : Measure = {
2322+ kind : 'paragraph' ,
2323+ lines : [ { fromRun : 0 , fromChar : 0 , toRun : 0 , toChar : 11 , width : 80 , ascent : 12 , descent : 3 , lineHeight : 15 } ] ,
2324+ totalHeight : 15 ,
2325+ } ;
2326+ const imageMeasure : Measure = {
2327+ kind : 'image' ,
2328+ width : 50 ,
2329+ height : 40 ,
2330+ } ;
2331+
2332+ const layout = layoutHeaderFooter ( [ paragraphBlock , imageBlock ] , [ paragraphMeasure , imageMeasure ] , {
2333+ width : 200 ,
2334+ height : 100 ,
2335+ } ) ;
2336+
2337+ // Height should include both paragraph and the anchored image
2338+ // Image is at offsetV=20 with height 40, so bottom is at 60
2339+ expect ( layout . height ) . toBeGreaterThan ( 15 ) ;
2340+ expect ( layout . height ) . toBeCloseTo ( 60 , 0 ) ;
2341+ } ) ;
2342+
2343+ it ( 'returns minimal height when header contains only behindDoc fragments with extreme offsets' , ( ) => {
2344+ const imageBlock1 : FlowBlock = {
2345+ kind : 'image' ,
2346+ id : 'img-1' ,
2347+ src : 'data:image/png;base64,xxx' ,
2348+ anchor : {
2349+ isAnchored : true ,
2350+ behindDoc : true ,
2351+ offsetV : - 5000 , // Extreme negative offset
2352+ } ,
2353+ } ;
2354+ const imageBlock2 : FlowBlock = {
2355+ kind : 'image' ,
2356+ id : 'img-2' ,
2357+ src : 'data:image/png;base64,yyy' ,
2358+ anchor : {
2359+ isAnchored : true ,
2360+ behindDoc : true ,
2361+ offsetV : 3000 , // Extreme positive offset
2362+ } ,
2363+ } ;
2364+ const imageMeasure1 : Measure = {
2365+ kind : 'image' ,
2366+ width : 100 ,
2367+ height : 50 ,
2368+ } ;
2369+ const imageMeasure2 : Measure = {
2370+ kind : 'image' ,
2371+ width : 100 ,
2372+ height : 50 ,
2373+ } ;
2374+
2375+ const layout = layoutHeaderFooter ( [ imageBlock1 , imageBlock2 ] , [ imageMeasure1 , imageMeasure2 ] , {
2376+ width : 200 ,
2377+ height : 60 ,
2378+ } ) ;
2379+
2380+ // Both images have extreme offsets and behindDoc=true, so height should be 0
2381+ expect ( layout . height ) . toBe ( 0 ) ;
2382+ } ) ;
2383+
2384+ it ( 'includes behindDoc fragments within overflow range alongside regular content' , ( ) => {
2385+ const paragraphBlock : FlowBlock = {
2386+ kind : 'paragraph' ,
2387+ id : 'para-1' ,
2388+ runs : [ { text : 'Header text' , fontFamily : 'Arial' , fontSize : 12 , pmStart : 1 , pmEnd : 12 } ] ,
2389+ } ;
2390+ const behindDocImage1 : FlowBlock = {
2391+ kind : 'image' ,
2392+ id : 'img-behind-1' ,
2393+ src : 'data:image/png;base64,xxx' ,
2394+ anchor : {
2395+ isAnchored : true ,
2396+ behindDoc : true ,
2397+ offsetV : 5 , // Within overflow range - should be included
2398+ } ,
2399+ } ;
2400+ const behindDocImage2 : FlowBlock = {
2401+ kind : 'image' ,
2402+ id : 'img-behind-2' ,
2403+ src : 'data:image/png;base64,yyy' ,
2404+ anchor : {
2405+ isAnchored : true ,
2406+ behindDoc : true ,
2407+ offsetV : 5000 , // Extreme offset - should be excluded
2408+ } ,
2409+ } ;
2410+ const regularImage : FlowBlock = {
2411+ kind : 'image' ,
2412+ id : 'img-regular' ,
2413+ src : 'data:image/png;base64,zzz' ,
2414+ anchor : {
2415+ isAnchored : true ,
2416+ behindDoc : false ,
2417+ offsetV : 25 ,
2418+ } ,
2419+ } ;
2420+ const paragraphMeasure : Measure = {
2421+ kind : 'paragraph' ,
2422+ lines : [ { fromRun : 0 , fromChar : 0 , toRun : 0 , toChar : 11 , width : 80 , ascent : 12 , descent : 3 , lineHeight : 15 } ] ,
2423+ totalHeight : 15 ,
2424+ } ;
2425+ const imageMeasure1 : Measure = {
2426+ kind : 'image' ,
2427+ width : 50 ,
2428+ height : 30 ,
2429+ } ;
2430+ const imageMeasure2 : Measure = {
2431+ kind : 'image' ,
2432+ width : 50 ,
2433+ height : 30 ,
2434+ } ;
2435+ const imageMeasure3 : Measure = {
2436+ kind : 'image' ,
2437+ width : 50 ,
2438+ height : 35 ,
2439+ } ;
2440+
2441+ const layout = layoutHeaderFooter (
2442+ [ paragraphBlock , behindDocImage1 , behindDocImage2 , regularImage ] ,
2443+ [ paragraphMeasure , imageMeasure1 , imageMeasure2 , imageMeasure3 ] ,
2444+ {
2445+ width : 200 ,
2446+ height : 100 ,
2447+ } ,
2448+ ) ;
2449+
2450+ // Height should include:
2451+ // - paragraph (15)
2452+ // - behindDocImage1 at y=5, height=30, bottom=35 (within overflow range)
2453+ // - regularImage at y=25, height=35, bottom=60
2454+ // - behindDocImage2 excluded (extreme offset)
2455+ expect ( layout . height ) . toBeGreaterThan ( 15 ) ;
2456+ expect ( layout . height ) . toBeCloseTo ( 60 , 0 ) ;
2457+ } ) ;
21902458} ) ;
21912459
21922460describe ( 'requirePageBoundary edge cases' , ( ) => {
0 commit comments