@@ -22,7 +22,6 @@ export const handleListNode = (params) => {
2222 if ( isList ) {
2323 node . isList = true ;
2424 const result = handleListNodes ( params , node ) ;
25-
2625 return {
2726 nodes : [ result ] ,
2827 consumed : 1 ,
@@ -71,9 +70,15 @@ function handleListNodes(params, node) {
7170 // If no iLvl, try an outline level
7271 if ( ! iLvl && iLvl !== 0 ) iLvl = getOutlineLevelFromStyleTag ( styleId , docx ) ;
7372
74- const { listType, listOrderingType, listrPrs, listpPrs, start, lvlText, lvlJc, customFormat } =
75- getNodeNumberingDefinition ( node , iLvl , docx ) ;
73+ let numberingDefinition = getNodeNumberingDefinition ( node , iLvl , docx ) ;
74+ if ( ! Object . keys ( numberingDefinition ) . length ) {
75+ const { definition, ilvl } = getNodeNumberingDefinitionByStyle ( node , docx ) ;
76+ if ( definition ) numberingDefinition = definition ;
77+ if ( Number . isNaN ( iLvl ) ) iLvl = ilvl ;
78+ }
7679
80+ const { listType, listOrderingType, listrPrs, listpPrs, start, lvlText, lvlJc, customFormat } = numberingDefinition ;
81+
7782 // Fallback if the list definition is not found or is invalid
7883 // See invalid-list-def-fallback.docx for example and
7984 if ( ! listType ) {
@@ -196,6 +201,7 @@ export function testForList(node, docx) {
196201 let outlinelvl ;
197202
198203 const styleId = paragraphStyle ?. attributes [ 'w:val' ] ;
204+
199205 const styleTag = getStyleTagFromStyleId ( styleId , docx ) ;
200206 if ( styleTag && ! numId ) {
201207 const { numPr : numPrRecursve , type } = getNumPrRecursive ( { node, styleId, docx } ) ;
@@ -212,6 +218,11 @@ export function testForList(node, docx) {
212218 const levelDefinition = abstractNumDefinition ?. elements ?. find (
213219 ( el ) => el . name === 'w:lvl' && el . attributes ?. [ 'w:ilvl' ] == ilvl ,
214220 ) ;
221+
222+ if ( numId && ! levelDefinition && abstractNumDefinition ) {
223+ return true ;
224+ }
225+
215226 if ( ! levelDefinition ) return false ;
216227
217228 return ! ! numId ;
@@ -478,7 +489,7 @@ export function normalizeLvlTextChar(lvlText) {
478489 * @returns
479490 */
480491export function getNodeNumberingDefinition ( item , level , docx ) {
481- if ( ! item ) return ;
492+ if ( ! item ) return { } ;
482493 const { attributes = { } } = item ;
483494
484495 const { paragraphProperties = { } } = attributes ;
@@ -525,6 +536,137 @@ export function getNodeNumberingDefinition(item, level, docx) {
525536 return { listType, listOrderingType : listTypeDef , listrPrs, listpPrs, start, lvlText, lvlJc, customFormat } ;
526537}
527538
539+ export function getNodeNumberingDefinitionByStyle ( item , docx ) {
540+ if ( ! item ) return { } ;
541+
542+ const initialPpr = item . elements ?. find ( ( el ) => el . name === 'w:pPr' ) ;
543+ const styleTag = initialPpr ?. elements ?. find ( ( el ) => el . name === 'w:pStyle' ) ;
544+ const styleId = styleTag ?. attributes [ 'w:val' ] ;
545+ const styleDef = getStyleTagFromStyleId ( styleId , docx ) ;
546+ if ( ! styleDef ) return { } ;
547+
548+ const pPr = styleDef . elements ?. find ( ( el ) => el . name === 'w:pPr' ) ;
549+ const numPr = pPr ?. elements ?. find ( ( el ) => el . name === 'w:numPr' ) ;
550+ const numIdTag = numPr ?. elements ?. find ( ( el ) => el . name === 'w:numId' ) ;
551+ const numId = numIdTag ?. attributes ?. [ 'w:val' ] ;
552+ if ( ! numId ) return { } ;
553+
554+ const abstractNumId = getAbstractNumIdByNumId ( numId , docx ) ;
555+ if ( ! abstractNumId ) return { } ;
556+
557+ const levelData = getLevelDataFromAbstractNum ( abstractNumId , styleId , docx ) ;
558+ if ( ! levelData ) return { } ;
559+
560+ const definition = extractDefinitionFromLevel ( levelData . level , initialPpr ) ;
561+
562+ return {
563+ definition,
564+ ilvl : levelData . ilvl ,
565+ } ;
566+ }
567+
568+ function getAbstractNumIdByNumId ( numId , docx ) {
569+ const numbering = docx [ 'word/numbering.xml' ] ;
570+ if ( ! numbering ) return null ;
571+
572+ const { elements } = numbering ;
573+ const listData = elements [ 0 ] ;
574+ const numberingElements = listData . elements || [ ] ;
575+
576+ const numDef = numberingElements . find ( ( el ) =>
577+ el . name === 'w:num' &&
578+ el . attributes ?. [ 'w:numId' ] === numId
579+ ) ;
580+
581+ if ( ! numDef ) return null ;
582+
583+ const abstractNumIdRef = numDef . elements ?. find ( ( el ) => el . name === 'w:abstractNumId' ) ;
584+ return abstractNumIdRef ?. attributes ?. [ 'w:val' ] ;
585+ }
586+
587+ function getLevelDataFromAbstractNum ( abstractNumId , styleId , docx ) {
588+ const numbering = docx [ 'word/numbering.xml' ] ;
589+ if ( ! numbering ) return null ;
590+
591+ const { elements } = numbering ;
592+ const listData = elements [ 0 ] ;
593+ const numberingElements = listData . elements || [ ] ;
594+
595+ const abstractNum = numberingElements . find ( ( el ) =>
596+ el . name === 'w:abstractNum' &&
597+ el . attributes ?. [ 'w:abstractNumId' ] === abstractNumId
598+ ) ;
599+
600+ if ( ! abstractNum ) return null ;
601+
602+ const levels = abstractNum . elements ?. filter ( el => el . name === 'w:lvl' ) || [ ] ;
603+ for ( const level of levels ) {
604+ const pStyle = level . elements ?. find ( ( el ) => el . name === 'w:pStyle' ) ;
605+ if ( pStyle ?. attributes ?. [ 'w:val' ] === styleId ) {
606+ const found = {
607+ level,
608+ ilvl : Number ( level . attributes ?. [ 'w:ilvl' ] ) || 0
609+ } ;
610+ return found ;
611+ }
612+ }
613+
614+ const level0 = levels . find ( ( level ) => level . attributes ?. [ 'w:ilvl' ] === '0' ) ;
615+ if ( level0 ) {
616+ return {
617+ level : level0 ,
618+ ilvl : 0 ,
619+ } ;
620+ }
621+
622+ return null ;
623+ }
624+
625+ function extractDefinitionFromLevel ( level , initialPpr ) {
626+ if ( ! level ) return { } ;
627+
628+ const start = level . elements ?. find ( ( el ) => el . name === 'w:start' ) ?. attributes ?. [ 'w:val' ] ;
629+
630+ let numFmtTag = level . elements ?. find ( ( el ) => el . name === 'w:numFmt' ) ;
631+ let numFmt = numFmtTag ?. attributes ?. [ 'w:val' ] ;
632+
633+ let lvlText = level . elements ?. find ( ( el ) => el . name === 'w:lvlText' ) ?. attributes ?. [ 'w:val' ] ;
634+ lvlText = normalizeLvlTextChar ( lvlText ) ;
635+
636+ let customFormat ;
637+ if ( numFmt === 'custom' ) customFormat = numFmtTag ?. attributes ?. [ 'w:format' ] ;
638+
639+ const lvlJc = level . elements ?. find ( ( el ) => el . name === 'w:lvlJc' ) ?. attributes ?. [ 'w:val' ] ;
640+ const pPr = level . elements ?. find ( ( el ) => el . name === 'w:pPr' ) ;
641+ const rPr = level . elements ?. find ( ( el ) => el . name === 'w:rPr' ) ;
642+
643+ let listpPrs , listrPrs ;
644+ if ( pPr ) listpPrs = _processListParagraphProperties ( pPr , initialPpr ) ;
645+ if ( rPr ) listrPrs = _processListRunProperties ( rPr ) ;
646+
647+ let listType ;
648+ if ( unorderedListTypes . includes ( numFmt ?. toLowerCase ( ) ) ) {
649+ listType = 'bulletList' ;
650+ } else if ( orderedListTypes . includes ( numFmt ) ) {
651+ listType = 'orderedList' ;
652+ } else if ( numFmt === 'custom' ) {
653+ listType = 'orderedList' ;
654+ } else {
655+ return { } ;
656+ }
657+
658+ return {
659+ listType,
660+ listOrderingType : numFmt ,
661+ listrPrs,
662+ listpPrs,
663+ start,
664+ lvlText,
665+ lvlJc,
666+ customFormat,
667+ } ;
668+ }
669+
528670export function getDefinitionForLevel ( data , level ) {
529671 return data ?. elements ?. find ( ( item ) => Number ( item . attributes [ 'w:ilvl' ] ) === level ) ;
530672}
0 commit comments