node-pptx icon indicating copy to clipboard operation
node-pptx copied to clipboard

multi lines was saved as 1 line

Open lengerrong opened this issue 4 years ago • 2 comments

test code

  let pptx = new PPTX.Composer()
  await pptx.load(__dirname + '/../../templates/test.pptx')
  const tmpPPTX = '/tmp/worship' + Math.random() + '.pptx'  
  await pptx.save(tmpPPTX)

In the test.pptx, I have a slide which has two lines as its tile.

first line
second line

but the saved ppt always rendered to 1 line

first line second line

I looked into the codes, we are using xml2js.parseString to parse xml string to js obj. and use xml2js.Builder to save js obj back to xml string

with below code, we can easily reproduce the issue:

let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><a:p><a:pPr marL="0" lvl="0" indent="0" algn="ctr" rtl="0"><a:spcBef><a:spcPts val="0"/></a:spcBef><a:spcAft><a:spcPts val="0"/></a:spcAft><a:buClr><a:schemeClr val="dk1"/></a:buClr><a:buSzPts val="1100"/><a:buFont typeface="Arial"/><a:buNone/></a:pPr><a:r><a:rPr lang="ja-JP" altLang="en-US"/><a:t>first line</a:t></a:r><a:br><a:rPr lang="en-CA" altLang="ja-JP" dirty="0"/></a:br><a:r><a:rPr lang="ja-JP" altLang="en-US"/><a:t>second line</a:t></a:r><a:endParaRPr lang="ja-JP" altLang="en-US" dirty="0"/></a:p>'

  xml2js.parseString(xml, function(err, js) {
    let builder = new xml2js.Builder({ renderOpts: { pretty: false } });
    let savexml = builder.buildObject(js);
    console.log(xml)
    console.log(savexml)
    console.log(xml === savexml)
  });

The output is:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><a:p><a:pPr marL="0" lvl="0" indent="0" algn="ctr" rtl="0"><a:spcBef><a:spcPts val="0"/></a:spcBef><a:spcAft><a:spcPts val="0"/></a:spcAft><a:buClr><a:schemeClr val="dk1"/></a:buClr><a:buSzPts val="1100"/><a:buFont typeface="Arial"/><a:buNone/></a:pPr><a:r><a:rPr lang="ja-JP" altLang="en-US"/><a:t>first line</a:t></a:r><a:br><a:rPr lang="en-CA" altLang="ja-JP" dirty="0"/></a:br><a:r><a:rPr lang="ja-JP" altLang="en-US"/><a:t>second line</a:t></a:r><a:endParaRPr lang="ja-JP" altLang="en-US" dirty="0"/></a:p>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><a:p><a:pPr marL="0" lvl="0" indent="0" algn="ctr" rtl="0"><a:spcBef><a:spcPts val="0"/></a:spcBef><a:spcAft><a:spcPts val="0"/></a:spcAft><a:buClr><a:schemeClr val="dk1"/></a:buClr><a:buSzPts val="1100"/><a:buFont typeface="Arial"/><a:buNone/></a:pPr><a:r><a:rPr lang="ja-JP" altLang="en-US"/><a:t>first line</a:t></a:r><a:r><a:rPr lang="ja-JP" altLang="en-US"/><a:t>second line</a:t></a:r><a:br><a:rPr lang="en-CA" altLang="ja-JP" dirty="0"/></a:br><a:endParaRPr lang="ja-JP" altLang="en-US" dirty="0"/></a:p>
false

As you can see in the output, the break line node <a:br> was saved after the two lines <a:r> rather than between them. That is the reason why multi line was saved into 1 line.

lengerrong avatar Aug 05 '21 08:08 lengerrong

it is not xml2js lib's bug. since the json object parsed from the xml string do not have the sequence information.

{
  'a:p': {
    'a:pPr': [ [Object] ],
    'a:r': [ [Object], [Object] ],
    'a:br': [ [Object] ],
    'a:endParaRPr': [ [Object] ]
  }
}

and we won't be able to get below json object via xml2js lib.

{
  'a:p': {
    'a:pPr': [ [Object] ],
    'a:r': [[Object]],
    'a:br': [ [Object] ],
    'a:r': [[Object]],
    'a:endParaRPr': [ [Object] ]
  }
}

lengerrong avatar Aug 05 '21 09:08 lengerrong

log a ticket to xml2js, hope can find a solution then. https://github.com/Leonidas-from-XIV/node-xml2js/issues/621

lengerrong avatar Aug 05 '21 09:08 lengerrong