@@ -25,6 +25,53 @@ export type AnchoredTable = {
2525
2626export type AnchoredObject = AnchoredDrawing | AnchoredTable ;
2727
28+ export type AnchoredTableCollection = {
29+ byParagraph : Map < number , AnchoredTable [ ] > ;
30+ withoutParagraph : AnchoredTable [ ] ;
31+ } ;
32+
33+ function buildParagraphIndexById ( blocks : FlowBlock [ ] , len : number ) : Map < string , number > {
34+ const paragraphIndexById = new Map < string , number > ( ) ;
35+
36+ for ( let i = 0 ; i < len ; i += 1 ) {
37+ const block = blocks [ i ] ;
38+ if ( block . kind === 'paragraph' ) {
39+ paragraphIndexById . set ( block . id , i ) ;
40+ }
41+ }
42+
43+ return paragraphIndexById ;
44+ }
45+
46+ function findNearestParagraphIndex ( blocks : FlowBlock [ ] , len : number , fromIndex : number ) : number | null {
47+ for ( let i = fromIndex - 1 ; i >= 0 ; i -= 1 ) {
48+ if ( blocks [ i ] . kind === 'paragraph' ) return i ;
49+ }
50+
51+ for ( let i = fromIndex + 1 ; i < len ; i += 1 ) {
52+ if ( blocks [ i ] . kind === 'paragraph' ) return i ;
53+ }
54+
55+ return null ;
56+ }
57+
58+ function resolveAnchorParagraphIndex (
59+ blocks : FlowBlock [ ] ,
60+ len : number ,
61+ paragraphIndexById : Map < string , number > ,
62+ fromIndex : number ,
63+ anchorParagraphId : unknown ,
64+ ) : number | null {
65+ if ( typeof anchorParagraphId === 'string' ) {
66+ const explicitIndex = paragraphIndexById . get ( anchorParagraphId ) ;
67+ if ( typeof explicitIndex === 'number' ) {
68+ return explicitIndex ;
69+ }
70+ }
71+
72+ return findNearestParagraphIndex ( blocks , len , fromIndex ) ;
73+ }
74+
2875/**
2976 * Check if an anchored image should be pre-registered (before any paragraphs are laid out).
3077 * Images with vRelativeFrom='margin' or 'page' position themselves relative to the page,
@@ -78,28 +125,7 @@ export function collectPreRegisteredAnchors(blocks: FlowBlock[], measures: Measu
78125export function collectAnchoredDrawings ( blocks : FlowBlock [ ] , measures : Measure [ ] ) : Map < number , AnchoredDrawing [ ] > {
79126 const map = new Map < number , AnchoredDrawing [ ] > ( ) ;
80127 const len = Math . min ( blocks . length , measures . length ) ;
81- const paragraphIndexById = new Map < string , number > ( ) ;
82-
83- for ( let i = 0 ; i < len ; i += 1 ) {
84- const block = blocks [ i ] ;
85- if ( block . kind === 'paragraph' ) {
86- paragraphIndexById . set ( block . id , i ) ;
87- }
88- }
89-
90- const nearestPrevParagraph = ( fromIndex : number ) : number | null => {
91- for ( let i = fromIndex - 1 ; i >= 0 ; i -= 1 ) {
92- if ( blocks [ i ] . kind === 'paragraph' ) return i ;
93- }
94- return null ;
95- } ;
96-
97- const nearestNextParagraph = ( fromIndex : number ) : number | null => {
98- for ( let i = fromIndex + 1 ; i < len ; i += 1 ) {
99- if ( blocks [ i ] . kind === 'paragraph' ) return i ;
100- }
101- return null ;
102- } ;
128+ const paragraphIndexById = buildParagraphIndexById ( blocks , len ) ;
103129
104130 for ( let i = 0 ; i < len ; i += 1 ) {
105131 const block = blocks [ i ] ;
@@ -125,12 +151,7 @@ export function collectAnchoredDrawings(blocks: FlowBlock[], measures: Measure[]
125151 typeof drawingBlock . attrs === 'object' && drawingBlock . attrs
126152 ? ( drawingBlock . attrs as { anchorParagraphId ?: unknown } ) . anchorParagraphId
127153 : undefined ;
128- let anchorParaIndex =
129- typeof anchorParagraphId === 'string' ? ( paragraphIndexById . get ( anchorParagraphId ) ?? null ) : null ;
130- if ( anchorParaIndex == null ) {
131- anchorParaIndex = nearestPrevParagraph ( i ) ;
132- }
133- if ( anchorParaIndex == null ) anchorParaIndex = nearestNextParagraph ( i ) ;
154+ const anchorParaIndex = resolveAnchorParagraphIndex ( blocks , len , paragraphIndexById , i , anchorParagraphId ) ;
134155 if ( anchorParaIndex == null ) continue ; // no paragraphs at all
135156
136157 const list = map . get ( anchorParaIndex ) ?? [ ] ;
@@ -143,34 +164,15 @@ export function collectAnchoredDrawings(blocks: FlowBlock[], measures: Measure[]
143164
144165/**
145166 * Collect anchored/floating tables mapped to their anchor paragraph index.
146- * Map of paragraph block index -> anchored tables associated with that paragraph.
167+ * Also returns anchored tables that have no paragraph to attach to .
147168 */
148- export function collectAnchoredTables ( blocks : FlowBlock [ ] , measures : Measure [ ] ) : Map < number , AnchoredTable [ ] > {
149- const map = new Map < number , AnchoredTable [ ] > ( ) ;
150- const paragraphIndexById = new Map < string , number > ( ) ;
151-
152- for ( let i = 0 ; i < blocks . length ; i += 1 ) {
153- const block = blocks [ i ] ;
154- if ( block . kind === 'paragraph' ) {
155- paragraphIndexById . set ( block . id , i ) ;
156- }
157- }
158-
159- const nearestPrevParagraph = ( fromIndex : number ) : number | null => {
160- for ( let i = fromIndex - 1 ; i >= 0 ; i -= 1 ) {
161- if ( blocks [ i ] . kind === 'paragraph' ) return i ;
162- }
163- return null ;
164- } ;
165-
166- const nearestNextParagraph = ( fromIndex : number ) : number | null => {
167- for ( let i = fromIndex + 1 ; i < blocks . length ; i += 1 ) {
168- if ( blocks [ i ] . kind === 'paragraph' ) return i ;
169- }
170- return null ;
171- } ;
169+ export function collectAnchoredTables ( blocks : FlowBlock [ ] , measures : Measure [ ] ) : AnchoredTableCollection {
170+ const len = Math . min ( blocks . length , measures . length ) ;
171+ const byParagraph = new Map < number , AnchoredTable [ ] > ( ) ;
172+ const withoutParagraph : AnchoredTable [ ] = [ ] ;
173+ const paragraphIndexById = buildParagraphIndexById ( blocks , len ) ;
172174
173- for ( let i = 0 ; i < blocks . length ; i += 1 ) {
175+ for ( let i = 0 ; i < len ; i += 1 ) {
174176 const block = blocks [ i ] ;
175177 const measure = measures [ i ] ;
176178
@@ -187,18 +189,19 @@ export function collectAnchoredTables(blocks: FlowBlock[], measures: Measure[]):
187189 typeof tableBlock . attrs === 'object' && tableBlock . attrs
188190 ? ( tableBlock . attrs as { anchorParagraphId ?: unknown } ) . anchorParagraphId
189191 : undefined ;
190- let anchorParaIndex =
191- typeof anchorParagraphId === 'string' ? ( paragraphIndexById . get ( anchorParagraphId ) ?? null ) : null ;
192+ const anchorParaIndex = resolveAnchorParagraphIndex ( blocks , len , paragraphIndexById , i , anchorParagraphId ) ;
192193 if ( anchorParaIndex == null ) {
193- anchorParaIndex = nearestPrevParagraph ( i ) ;
194+ withoutParagraph . push ( { block : tableBlock , measure : tableMeasure } ) ;
195+ continue ;
194196 }
195- if ( anchorParaIndex == null ) anchorParaIndex = nearestNextParagraph ( i ) ;
196- if ( anchorParaIndex == null ) continue ; // no paragraphs at all
197197
198- const list = map . get ( anchorParaIndex ) ?? [ ] ;
198+ const list = byParagraph . get ( anchorParaIndex ) ?? [ ] ;
199199 list . push ( { block : tableBlock , measure : tableMeasure } ) ;
200- map . set ( anchorParaIndex , list ) ;
200+ byParagraph . set ( anchorParaIndex , list ) ;
201201 }
202202
203- return map ;
203+ return {
204+ byParagraph,
205+ withoutParagraph,
206+ } ;
204207}
0 commit comments