11import { describe , expect , it } from 'vitest' ;
2- import {
3- resolveAnchoredGraphicY ,
4- computeParagraphContentStartY ,
5- computeParagraphLayoutStartY ,
6- } from './graphic-placement.js' ;
2+ import { resolveAnchoredGraphicY , resolveAnchoredGraphicX } from './graphic-placement.js' ;
73
8- const base = {
4+ const yBase = {
95 objectHeight : 100 ,
106 contentTop : 72 ,
117 contentBottom : 720 ,
128 pageBottomMargin : 72 ,
139} ;
1410
11+ const columns = { width : 200 , gap : 20 , count : 2 } ;
12+ const margins = { left : 72 , right : 72 } ;
13+ const pageWidth = 600 ;
14+ const objectWidth = 80 ;
15+
1516describe ( 'resolveAnchoredGraphicY' , ( ) => {
1617 it ( 'positions margin-relative top with offset' , ( ) => {
1718 expect (
1819 resolveAnchoredGraphicY ( {
19- ...base ,
20+ ...yBase ,
2021 anchor : { vRelativeFrom : 'margin' , alignV : 'top' , offsetV : 10 } ,
2122 } ) ,
2223 ) . toBe ( 82 ) ;
@@ -25,7 +26,7 @@ describe('resolveAnchoredGraphicY', () => {
2526 it ( 'positions page-relative bottom with page margin' , ( ) => {
2627 expect (
2728 resolveAnchoredGraphicY ( {
28- ...base ,
29+ ...yBase ,
2930 anchor : { vRelativeFrom : 'page' , alignV : 'bottom' , offsetV : 5 } ,
3031 } ) ,
3132 ) . toBe ( 720 + 72 - 100 + 5 ) ;
@@ -34,7 +35,7 @@ describe('resolveAnchoredGraphicY', () => {
3435 it ( 'positions paragraph-relative center on first line' , ( ) => {
3536 expect (
3637 resolveAnchoredGraphicY ( {
37- ...base ,
38+ ...yBase ,
3839 anchor : { vRelativeFrom : 'paragraph' , alignV : 'center' , offsetV : 0 } ,
3940 anchorParagraphY : 200 ,
4041 firstLineHeight : 24 ,
@@ -45,7 +46,7 @@ describe('resolveAnchoredGraphicY', () => {
4546 it ( 'uses pre-registered fallback when vRelativeFrom is paragraph without paragraph context' , ( ) => {
4647 expect (
4748 resolveAnchoredGraphicY ( {
48- ...base ,
49+ ...yBase ,
4950 anchor : { vRelativeFrom : 'paragraph' , offsetV : 20 } ,
5051 preRegisteredFallbackToContentTop : true ,
5152 } ) ,
@@ -55,62 +56,115 @@ describe('resolveAnchoredGraphicY', () => {
5556 it ( 'ignores paragraph alignV when pre-registered fallback has no paragraph context' , ( ) => {
5657 expect (
5758 resolveAnchoredGraphicY ( {
58- ...base ,
59+ ...yBase ,
5960 anchor : { vRelativeFrom : 'paragraph' , alignV : 'center' , offsetV : 0 } ,
6061 preRegisteredFallbackToContentTop : true ,
6162 } ) ,
6263 ) . toBe ( 72 ) ;
6364 expect (
6465 resolveAnchoredGraphicY ( {
65- ...base ,
66+ ...yBase ,
6667 objectHeight : 50 ,
6768 anchor : { vRelativeFrom : 'paragraph' , alignV : 'bottom' , offsetV : 10 } ,
6869 preRegisteredFallbackToContentTop : true ,
6970 } ) ,
7071 ) . toBe ( 82 ) ;
7172 } ) ;
7273
73- it ( 'legacy undefined vRelativeFrom uses anchor paragraph Y' , ( ) => {
74+ it ( 'legacy undefined vRelativeFrom uses anchor paragraph Y plus offsetV ' , ( ) => {
7475 expect (
7576 resolveAnchoredGraphicY ( {
76- ...base ,
77+ ...yBase ,
7778 anchor : { offsetV : 15 } ,
7879 anchorParagraphY : 300 ,
7980 } ) ,
8081 ) . toBe ( 315 ) ;
8182 } ) ;
82- } ) ;
8383
84- describe ( 'computeParagraphLayoutStartY' , ( ) => {
85- it ( 'rewinds trailing then applies full spacing-before without double collapse' , ( ) => {
84+ it ( 'legacy undefined vRelativeFrom with preRegisteredFallbackToContentTop uses contentTop' , ( ) => {
8685 expect (
87- computeParagraphLayoutStartY ( {
88- cursorY : 120 ,
89- spacingBefore : 24 ,
90- trailingSpacing : 12 ,
91- rewindTrailingFromPrevious : true ,
86+ resolveAnchoredGraphicY ( {
87+ ... yBase ,
88+ anchor : { alignV : 'center' , offsetV : 20 } ,
89+ anchorParagraphY : 300 ,
90+ preRegisteredFallbackToContentTop : true ,
9291 } ) ,
93- ) . toBe ( 132 ) ;
92+ ) . toBe ( 92 ) ;
9493 } ) ;
9594
96- it ( 'collapses spacing-before against trailing when previous after-spacing is kept ' , ( ) => {
95+ it ( 'legacy undefined vRelativeFrom does not use paragraph alignV without vRelativeFrom paragraph ' , ( ) => {
9796 expect (
98- computeParagraphLayoutStartY ( {
99- cursorY : 100 ,
100- spacingBefore : 24 ,
101- trailingSpacing : 8 ,
102- rewindTrailingFromPrevious : false ,
97+ resolveAnchoredGraphicY ( {
98+ ... yBase ,
99+ anchor : { alignV : 'bottom' , offsetV : 0 } ,
100+ anchorParagraphY : 200 ,
101+ firstLineHeight : 24 ,
103102 } ) ,
104- ) . toBe ( 116 ) ;
103+ ) . toBe ( 200 ) ;
105104 } ) ;
106105} ) ;
107106
108- describe ( 'computeParagraphContentStartY' , ( ) => {
109- it ( 'adds spacing-before minus trailing collapse' , ( ) => {
110- expect ( computeParagraphContentStartY ( 100 , 24 , false , 8 ) ) . toBe ( 116 ) ;
107+ describe ( 'resolveAnchoredGraphicX' , ( ) => {
108+ const columnIndex = 1 ;
109+ const columnLeft = margins . left + columnIndex * ( columns . width + columns . gap ) ;
110+
111+ describe ( 'column-relative (default)' , ( ) => {
112+ it . each ( [
113+ { alignH : 'left' as const , offsetH : 10 , expected : columnLeft + 10 } ,
114+ { alignH : 'center' as const , offsetH : 5 , expected : columnLeft + ( columns . width - objectWidth ) / 2 + 5 } ,
115+ { alignH : 'right' as const , offsetH : 3 , expected : columnLeft + columns . width - objectWidth - 3 } ,
116+ ] ) ( 'alignH=$alignH offsetH=$offsetH' , ( { alignH, offsetH, expected } ) => {
117+ expect ( resolveAnchoredGraphicX ( { alignH, offsetH } , columnIndex , columns , objectWidth , margins , pageWidth ) ) . toBe (
118+ expected ,
119+ ) ;
120+ } ) ;
121+ } ) ;
122+
123+ describe ( 'margin-relative' , ( ) => {
124+ const baseX = margins . left ;
125+ const availableWidth = pageWidth - margins . left - margins . right ;
126+
127+ it . each ( [
128+ { alignH : 'left' as const , offsetH : 10 , expected : baseX + 10 } ,
129+ { alignH : 'center' as const , offsetH : 5 , expected : baseX + ( availableWidth - objectWidth ) / 2 + 5 } ,
130+ { alignH : 'right' as const , offsetH : 3 , expected : baseX + availableWidth - objectWidth - 3 } ,
131+ ] ) ( 'alignH=$alignH offsetH=$offsetH' , ( { alignH, offsetH, expected } ) => {
132+ expect (
133+ resolveAnchoredGraphicX (
134+ { hRelativeFrom : 'margin' , alignH, offsetH } ,
135+ columnIndex ,
136+ columns ,
137+ objectWidth ,
138+ margins ,
139+ pageWidth ,
140+ ) ,
141+ ) . toBe ( expected ) ;
142+ } ) ;
143+ } ) ;
144+
145+ describe ( 'page-relative' , ( ) => {
146+ const baseX = 0 ;
147+ const availableWidth = pageWidth ;
148+
149+ it . each ( [
150+ { alignH : 'left' as const , offsetH : 10 , expected : baseX + 10 } ,
151+ { alignH : 'center' as const , offsetH : 5 , expected : baseX + ( availableWidth - objectWidth ) / 2 + 5 } ,
152+ { alignH : 'right' as const , offsetH : 3 , expected : baseX + availableWidth - objectWidth - 3 } ,
153+ ] ) ( 'alignH=$alignH offsetH=$offsetH' , ( { alignH, offsetH, expected } ) => {
154+ expect (
155+ resolveAnchoredGraphicX (
156+ { hRelativeFrom : 'page' , alignH, offsetH } ,
157+ columnIndex ,
158+ columns ,
159+ objectWidth ,
160+ margins ,
161+ pageWidth ,
162+ ) ,
163+ ) . toBe ( expected ) ;
164+ } ) ;
111165 } ) ;
112166
113- it ( 'returns cursorY when spacing already applied ' , ( ) => {
114- expect ( computeParagraphContentStartY ( 100 , 24 , true , 0 ) ) . toBe ( 100 ) ;
167+ it ( 'defaults alignH to left and offsetH to zero ' , ( ) => {
168+ expect ( resolveAnchoredGraphicX ( { } , 0 , columns , objectWidth , margins , pageWidth ) ) . toBe ( margins . left ) ;
115169 } ) ;
116170} ) ;
0 commit comments