dolanmiu / docx

Easily generate and modify .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.
https://docx.js.org/
MIT License
4.24k stars 479 forks source link

Generated docx document.xml structure differs from Word - rendering Google Drive uploads incompatible #2712

Closed gen3vra closed 3 weeks ago

gen3vra commented 1 month ago

If you export a simple table document with this library and attempt to upload it, it will be completely mangled. This can be fixed by changing literally anything and saving it in Word, thereby 'replacing' the document structure with a freshly saved Word generated one.

The problem lies in the document.xml structure. In other words, renaming both documents to .zip and extracting both, then replacing the 'broken' file's document.xml with the 'fixed' one will fix the broken one too.

The document.xml structure of the broken one https://pastebin.com/raw/kaa3Erq5

The document.xml structure of the fixed one https://pastebin.com/raw/mHn2QPK9

Here's an example of the broken docx upload broken.docx broken

And what it should look like after making a small edit (changing 'Test Text' -> 'Test Text (edit)' fixed.docx fixed

I'm unsure how to make default exported documents compatible with Google Drive from the get-go or what's causing this issue.

anti-the-social commented 1 month ago

Hi!

TableCell needs width to be specified. If that would not fully help, then add width for the Table too.

Not the first issue on that e.g. https://github.com/dolanmiu/docx/issues/1947 but there was some reasoning somewhere else. :)

BTW, which version of docxjs you use?

gen3vra commented 1 month ago

Appreciate your response.

This is an example of the top section.

schoolInformationSection = new docx.Table({
        width: {
            size: 100,
            type: docx.WidthType.PERCENTAGE,
        },
        rows: [
            new docx.TableRow({
                children: [
                    new docx.TableCell({
                        children: [
                            new docx.Paragraph({
                                children: [
                                    new docx.TextRun({
                                        text: "",
                                        font: "Arial",
                                        size: 20,
                                    }),
                                ],
                            }),
                        ],
                        width: {
                            size: 30,
                            type: docx.WidthType.PERCENTAGE,
                        },
                    }),
                    // Centered spacer
                    new docx.TableCell({
                        children: [
                            new docx.Paragraph({
                                children: [
                                    new docx.TextRun({
                                        text: "",
                                        font: "Arial",
                                        size: 20,
                                    }),
                                ],
                            }),
                        ],
                        width: {
                            size: 30,
                            type: docx.WidthType.PERCENTAGE,
                        },
                    }),
                    new docx.TableCell({
                        children: [
                            new docx.Paragraph({
                                children: [
                                    new docx.TextRun({
                                        text: schoolInfoName,
                                        font: "Arial",
                                        size: 20,
                                        bold: false
                                    }),
                                    ...schoolInfoAddressTextRuns,
                                ],
                                alignment: docx.AlignmentType.LEFT,
                            }),
                        ],
                        width: {
                            size: 40,
                            type: docx.WidthType.PERCENTAGE,
                        },
                    }),
                ],
            })
        ],
        borders: {
            top: {style: docx.BorderStyle.NONE, size: 0, color: "FFFFFF"},
            bottom: {style: docx.BorderStyle.NONE, size: 0, color: "FFFFFF"},
            left: {style: docx.BorderStyle.NONE, size: 0, color: "FFFFFF"},
            right: {style: docx.BorderStyle.NONE, size: 0, color: "FFFFFF"},
            insideHorizontal: {style: docx.BorderStyle.NONE, size: 0, color: "FFFFFF"},
            insideVertical: {style: docx.BorderStyle.NONE, size: 0, color: "FFFFFF"},
        },
    });
}

As far as I can tell, every section that should have a width set, does. Apparently the CDN I was using was on 7.1.0, but I upgraded to latest and it makes no difference. Here's the document.xml from the latest. https://hastebin.skyra.pw/raw/nehalojisu (pastebin was under maintenance)

And an upload to docs. broken

anti-the-social commented 1 month ago

Oh yeah, if I remember correctly, Google docs does not support percentage very well, while standard Word does recalculate them into twips. (Thats why after opening in Word and re-saving would cure document for Google docs)

So for you, to support the platform you are looking for you would have to use absolute values.

gen3vra commented 1 month ago

What absolute values are those? Is there some sort of reference somewhere? What values correspond to 100% width of a normal Word document page?

anti-the-social commented 1 month ago

Here is list of all width types https://docx.js.org/api/variables/WidthType.html DXA (Twips) is the absolute value which is 20points; while point is 0.75pixels; And there about 28.34646 points is one cm Your width calculations would include page width minus margins.

So if width is 21cm and margins 2cm left and 2cm right its something like (21-2-2)*28.34646*20

anti-the-social commented 1 month ago

If I am not mistaken, width should be rounded number, so do not forget of Math.round()

gen3vra commented 3 weeks ago

Thank you