1- import { twipsToInches , twipsToPixels , twipsToLines } from '../../helpers.js' ;
1+ import { twipsToInches , twipsToLines , twipsToPixels } from '../../helpers.js' ;
22import { testForList } from './listImporter.js' ;
33import { carbonCopy } from '../../../utilities/carbonCopy.js' ;
44import { mergeTextNodes } from './mergeTextNodes.js' ;
@@ -49,33 +49,50 @@ export const handleParagraphNode = (params) => {
4949
5050 const pPr = node . elements ?. find ( ( el ) => el . name === 'w:pPr' ) ;
5151 const styleTag = pPr ?. elements ?. find ( ( el ) => el . name === 'w:pStyle' ) ;
52+ const nestedRPr = pPr ?. elements ?. find ( ( el ) => el . name === 'w:rPr' ) ;
53+
54+ if ( nestedRPr ) {
55+ schemaNode . attrs . marksAttrs = parseMarks ( nestedRPr , [ ] ) ;
56+ }
57+
5258 if ( styleTag ) {
5359 schemaNode . attrs [ 'styleId' ] = styleTag . attributes [ 'w:val' ] ;
5460 }
5561
5662 const indent = pPr ?. elements ?. find ( ( el ) => el . name === 'w:ind' ) ;
5763 if ( indent && indent . attributes ) {
58- const { 'w:left' : left , 'w:right' : right , 'w:firstLine' : firstLine } = indent ?. attributes ;
64+ const { 'w:left' : left , 'w:right' : right , 'w:firstLine' : firstLine , 'w:hanging' : hanging } = indent ?. attributes ;
5965
6066 if ( schemaNode . attrs ) {
6167 if ( ! schemaNode . attrs . indent ) schemaNode . attrs . indent = { } ;
6268 if ( left ) schemaNode . attrs [ 'indent' ] . left = twipsToPixels ( left ) ;
6369 if ( right ) schemaNode . attrs [ 'indent' ] . right = twipsToPixels ( right ) ;
6470 if ( firstLine ) schemaNode . attrs [ 'indent' ] . firstLine = twipsToPixels ( firstLine ) ;
71+ if ( hanging ) schemaNode . attrs [ 'indent' ] . hanging = twipsToPixels ( hanging ) ;
6572 }
6673
67- const textIndentVal = left || firstLine || 0 ;
74+ const textIndentVal = left - parseInt ( hanging || 0 ) || 0 ;
6875 schemaNode . attrs [ 'textIndent' ] = `${ twipsToInches ( textIndentVal ) } in` ;
6976 }
7077
7178 const justify = pPr ?. elements ?. find ( ( el ) => el . name === 'w:jc' ) ;
7279 if ( justify && justify . attributes ) {
7380 schemaNode . attrs [ 'textAlign' ] = justify . attributes [ 'w:val' ] ;
7481 }
82+
83+ const keepLines = pPr ?. elements ?. find ( ( el ) => el . name === 'w:keepLines' ) ;
84+ if ( keepLines && keepLines . attributes ) {
85+ schemaNode . attrs [ 'keepLines' ] = keepLines . attributes [ 'w:val' ] ;
86+ }
87+
88+ const keepNext = pPr ?. elements ?. find ( ( el ) => el . name === 'w:keepNext' ) ;
89+ if ( keepNext && keepNext . attributes ) {
90+ schemaNode . attrs [ 'keepNext' ] = keepNext . attributes [ 'w:val' ] ;
91+ }
7592
7693 if ( docx ) {
7794 const defaultStyleId = node . attributes ?. [ 'w:rsidRDefault' ] ;
78- schemaNode . attrs [ 'spacing' ] = getParagraphSpacing ( node , docx ) ;
95+ schemaNode . attrs [ 'spacing' ] = getParagraphSpacing ( node , docx , schemaNode . attrs [ 'styleId' ] , schemaNode . attrs . marksAttrs ) ;
7996 schemaNode . attrs [ 'rsidRDefault' ] = defaultStyleId ;
8097 }
8198
@@ -92,21 +109,24 @@ export const handleParagraphNode = (params) => {
92109 return { nodes : schemaNode ? [ schemaNode ] : [ ] , consumed : 1 } ;
93110} ;
94111
95- export const getParagraphSpacing = ( node , docx ) => {
112+ export const getParagraphSpacing = ( node , docx , styleId = '' , marks = [ ] ) => {
96113 // Check if we have default paragraph styles to override
97114 const spacing = {
98115 lineSpaceAfter : 0 ,
99116 lineSpaceBefore : 0 ,
100117 line : 0 ,
101118 lineRule : null ,
102119 }
103-
104- const { spacing : pDefaultSpacing = { } } = getDefaultParagraphStyle ( docx ) ;
120+
121+ const { spacing : pDefaultSpacing = { } } = getDefaultParagraphStyle ( docx , styleId ) ;
105122 let lineSpaceAfter , lineSpaceBefore , line , lineRuleStyle ;
106123
107124 const pPr = node . elements ?. find ( ( el ) => el . name === 'w:pPr' ) ;
108125 const inLineSpacingTag = pPr ?. elements ?. find ( ( el ) => el . name === 'w:spacing' ) ;
109126 const inLineSpacing = inLineSpacingTag ?. attributes || { } ;
127+
128+ const textStyleMark = marks . find ( ( el ) => el . type === 'textStyle' ) ;
129+ const fontSize = textStyleMark ?. attrs ?. fontSize ;
110130
111131 // These styles are taken in order of precedence
112132 // 1. Inline spacing
@@ -117,17 +137,27 @@ export const getParagraphSpacing = (node, docx) => {
117137
118138 const beforeSpacing = inLineSpacing ?. [ 'w:before' ] || lineSpaceBefore || pDefaultSpacing ?. [ 'w:before' ] ;
119139 if ( beforeSpacing ) spacing . lineSpaceBefore = twipsToPixels ( beforeSpacing ) ;
140+
141+ const beforeAutospacing = inLineSpacing ?. [ 'w:beforeAutospacing' ] ;
142+ if ( beforeAutospacing === '1' && fontSize ) {
143+ spacing . lineSpaceBefore += Math . round ( ( parseInt ( fontSize ) * 0.5 ) * 96 / 72 ) ;
144+ }
120145
121146 const afterSpacing = inLineSpacing ?. [ 'w:after' ] || lineSpaceAfter || pDefaultSpacing ?. [ 'w:after' ] ;
122147 if ( afterSpacing ) spacing . lineSpaceAfter = twipsToPixels ( afterSpacing ) ;
123148
149+ const afterAutospacing = inLineSpacing ?. [ 'w:afterAutospacing' ] ;
150+ if ( afterAutospacing === '1' && fontSize ) {
151+ spacing . lineSpaceAfter += Math . round ( ( parseInt ( fontSize ) * 0.5 ) * 96 / 72 ) ;
152+ }
153+
124154 const lineRule = inLineSpacing ?. [ 'w:lineRule' ] || lineRuleStyle || pDefaultSpacing ?. [ 'w:lineRule' ] ;
125155 if ( lineRule ) spacing . lineRule = lineRule ;
126156
127157 return spacing ;
128158} ;
129159
130- const getDefaultParagraphStyle = ( docx ) => {
160+ const getDefaultParagraphStyle = ( docx , styleId = '' ) => {
131161 const styles = docx [ 'word/styles.xml' ] ;
132162 if ( ! styles ) {
133163 return { } ;
@@ -136,10 +166,26 @@ const getDefaultParagraphStyle = (docx) => {
136166 const pDefault = defaults . elements . find ( ( el ) => el . name === 'w:pPrDefault' ) ;
137167 const pPrDefault = pDefault ?. elements ?. find ( ( el ) => el . name === 'w:pPr' ) ;
138168 const pPrDefaultSpacingTag = pPrDefault ?. elements ?. find ( ( el ) => el . name === 'w:spacing' ) || { } ;
169+
170+ // Paragraph 'Normal' styles
171+ const stylesNormal = styles . elements [ 0 ] . elements ?. find ( ( el ) => el . name === 'w:style' && el . attributes [ 'w:styleId' ] === 'Normal' ) ;
172+ const pPrNormal = stylesNormal ?. elements ?. find ( ( el ) => el . name === 'w:pPr' ) ;
173+ const pPrNormalSpacingTag = pPrNormal ?. elements ?. find ( ( el ) => el . name === 'w:spacing' ) || { } ;
174+
175+ // Styles based on styleId
176+ let pPrStyleIdSpacingTag = { } ;
177+ if ( styleId ) {
178+ const stylesById = styles . elements [ 0 ] . elements ?. find ( ( el ) => el . name === 'w:style' && el . attributes [ 'w:styleId' ] === styleId ) ;
179+ const pPrById = stylesById ?. elements ?. find ( ( el ) => el . name === 'w:pPr' ) ;
180+ pPrStyleIdSpacingTag = pPrById ?. elements ?. find ( ( el ) => el . name === 'w:spacing' ) || { } ;
181+ }
182+
139183 const { attributes : pPrDefaultSpacingAttr } = pPrDefaultSpacingTag ;
184+ const { attributes : pPrNormalSpacingAttr } = pPrNormalSpacingTag ;
185+ const { attributes : pPrByIdSpacingAttr } = pPrStyleIdSpacingTag ;
140186
141187 return {
142- spacing : pPrDefaultSpacingAttr ,
188+ spacing : pPrByIdSpacingAttr || pPrDefaultSpacingAttr || pPrNormalSpacingAttr ,
143189 }
144190} ;
145191
@@ -323,6 +369,6 @@ export function preProcessNodesForFldChar(nodes) {
323369 } ) ;
324370 }
325371 } ) ;
326-
372+
327373 return processedNodes ;
328374}
0 commit comments