add pasted image in a docx file by docxtemplater-image-module-free

287 views Asked by At

I want to make a local web page that accept users' pasted image and add to a docx file. To achieve this, I decided to use docxtemplater.js and docxtemplater-image-module-free module, but I can't get the 'getImage' callback function right. Any help is really appreciated! Here is the minimum example that repeat my current error.

include js

<script src="sources/vue.js"></script>
<script src="sources/elementui.js"></script>
<script src="sources/docxtemplater.js"></script>
<script src="sources/docxtemplater-image-module-free.js"></script>
<script src="sources/pizzip.js"></script>
<script src="sources/pizzip-utils.js"></script>
<script src="sources/FileSaver.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js" integrity="sha512-XMVd28F1oH/O71fzwBnV7HucLxVwtxf26XV8P4wPk26EDxuGZ91N8bsOttmnomcCD3CS5ZMRL50H0GgOHvegtg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip-utils/0.1.0/jszip-utils.min.js" integrity="sha512-3WaCYjK/lQuL0dVIRt1thLXr84Z/4Yppka6u40yEJT1QulYm9pCxguF6r8V84ndP5K03koI9hV1+zo/bUbgMtA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

html

<div style="text-align: center">
  <input type="file" id="doc" />
</div>
<div>Copy an image from word<br />Press Ctrl+v to paste it here <br /></div>
<div id="editor"></div>
<button onclick="handleConfirm()">confirm</button>

javascript

var blob = null;
// create paste event listener
window.addEventListener("paste", pasteHandler);

// handle paste events
function pasteHandler(e) {
  if (e.clipboardData) {
    var items = e.clipboardData.items;
    if (items) {
      for (var i in items) {
        // iterate through clipbord items
        var item = items[i];
        if (item.kind == "file") {
          //image is a file

          // create image source
          blob = item.getAsFile();
          var URLObj = window.URL || window.webkitURL;
          var source = URLObj.createObjectURL(blob);
          var pastedImage = new Image();
          pastedImage.src = source;

          // add it in editor
          document.getElementById("editor").innerHTML =
            document.getElementById("editor").innerHTML +
            pastedImage.outerHTML;
        }
      }
    }
  }
}

function handleConfirm() {
    const reader = new FileReader();
    const docs = document.getElementById("doc");
    if (docs.files.length === 0) {
      alert("No files selected");
    }
    reader.readAsBinaryString(docs.files.item(0));

    var opts = {}
    opts.centered = false;
    opts.getImage = function (tagValue, tagName) {
      return new Promise(function (resolve, reject) {
        tagValue[1].arrayBuffer().then(r => {
          const uint8array = new Uint8Array(r);
          resolve(uint8array.buffer);
        })
      });
    }
    opts.getSize = function (img, tagValue, tagName) {
      // FOR FIXED SIZE IMAGE :
      return [150, 150];
    }
    var imageModule = new ImageModule(opts);

    reader.onload = function (evt) {
      const content = evt.target.result;
      const zip = new PizZip(content);
      const doc = new window.docxtemplater(zip, {
        paragraphLoop: true,
        linebreaks: true,
        modules: [imageModule]
      });
      doc.compile();


      // Render the document (Replace {first_name} by John, {last_name} by Doe, ...)
      doc.resolveData({
        last_name:'a',
        first_name: 'b',
        phone: '123',
        description: 'aa',
        img: blob
      }).then(function () {
        console.log('ready');
        doc.render();
        const blob = doc.getZip().generate({
          type: "blob",
          mimeType:
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
          // compression: DEFLATE adds a compression step.
          // For a 50MB output document, expect 500ms additional CPU time
          compression: "DEFLATE",
        });
        // Output the document using Data-URI
        saveAs(blob, "output.docx");
      })
    }
  }

And I got a error:

{"error": [{
  "stack": "TypeError: Cannot set property namespaceURI of #<Element> which has only a getter\n    at ImgManager.addExtensionRels (/dev/sources/docxtemplater-image-module-free.js:323:37)\n    at ImgManager.addImageRels (/dev/sources/docxtemplater-image-module-free.js:350:22)\n    at file:///E://dev/sources/docxtemplater-image-module-free.js:164:42\n    at async Promise.all (index 2)\n    at async Promise.all (index 4)",
  "message": "Cannot set property namespaceURI of #<Element> which has only a getter",
  "properties": {
    "file": "word/document.xml"
  }
}

] } It seems that the getImage callback function in opts of the ImageModule return a unsupported result (maybe I'm wrong), Can anyone help me to fix this error? Thanks a lot!

1

There are 1 answers

0
Mario Varchmin On

The img property of the object you pass to resolveData() needs to contain a filename string and no blob, like so:

doc.resolveData({
    last_name:'a',
    first_name: 'b',
    phone: '123',
    description: 'aa',
    img: 'filename.jpg'
})

The image data is produced by the getImage() method. It has not to be passed in the data object.