javascriptblobdocxclient-sidearraybuffer

docx.js: How to use document patcher on client side


I'm trying to generate docx file using the docx javascript library but only on the client side.

The generation of new documents works like charm, but I would like to patch a document using the patcher. The documentation only shows code for server side loading with patchDocument(fs.readFileSync("My Document.docx"). There is no information about client side usage.

I tried it using the following code:

fetch('./public/template.docx')
  .then(response => {
    if (!response.ok) {
      throw new Error('Failed to fetch template.docx');
    }
    return response.blob();
  })
  .then(blob => {
    console.dir(blob);
    const reader = new FileReader();
    reader.onload = function(event) {
        const arrayBufferData = event.target.result;
        console.log(arrayBufferData);

        patchDocument({
            outputType: "blob",
            data: arrayBufferData,
            patches: {
                title: {
                    type: PatchType.PARAGRAPH,
                    children: [new TextRun("NEUER TITEL")],
                },
                number: {
                    type: PatchType.PARAGRAPH,
                    children: [new TextRun("XX")],
                }
            },
        });

    };

    reader.readAsArrayBuffer(blob);
  })
  .catch(error => {
    console.error('Error fetching template.docx:', error);
  });

I get the following error:

Uncaught (in promise) Error: Can't read the data of 'the loaded zip file'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?` at docx.js?v=ba760d4b:16887:43

But after checking it with console.log, the data actually is an ArrayBuffer. I also tried inputing the blob, with the same result. I’m not experienced with node, and I dont know what object type fs.readFileSync will return. But I would've guessed it’s a ArrayBuffer too!? The error also mentions, that it expects Blob or ArrayBuffer types.

Am I doing something wrong, or is the Patcher simply not supported on the client side? Are there even differences in the data between loading the file via fs.readFileSync and an ArrayBuffer converted from a blob? Aren’t both simply ArrayBuffer Objects?


Solution

  • I finally found the problem with the code above. While the syntax above matches the one in the documentation, it only seems to work with this syntax:

    fetch('./public/template.docx')
    .then(response => {
        if (!response.ok) {
            throw new Error('Failed to fetch template.docx');
        }
        return response.blob();
    })
    .then(blob => {
        console.dir(blob);
        const reader = new FileReader();
        reader.onload = function (event) {
            const arrayBufferData = event.target.result;
            console.log(arrayBufferData);
    
            patchDocument(arrayBufferData, {
                outputType: "nodebuffer",
                patches: {
                    title: {
                        type: PatchType.PARAGRAPH,
                        children: [new TextRun("NEUER TITEL")],
                    },
                    number: {
                        type: PatchType.PARAGRAPH,
                        children: [new TextRun("XX")],
                    }
                },
            }).then(finaldata => {
                const blob = new Blob([finaldata], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
                // Now work with the data, e.g. download it.
                const url = URL.createObjectURL(blob);
                const link = document.createElement('a');
                link.href = url;
                link.download = 'output.docx';
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
                URL.revokeObjectURL(url);
            });
    
        };
    
        reader.readAsArrayBuffer(blob);
    })
    .catch(error => {
        console.error('Error fetching template.docx:', error);
    });
    

    The difference here is that the data is given as the first argument, and the rest of the options are given within the object. Using the data field in the options object doesn't work on the client side. I'm not sure whether this is a bug or if I missed an obvious detail.

    Also I used "nodebuffer" as the outputType and then converted it into a Blob Object afterwards

    Regardless, I wanted to share the solution in case somebody else encounters this problem.