I’m trying to export an HTML element that includes images to a Word doc using the below function I’ve put together using various online sources. The Word document download is triggered, but the 1st download does not include the images. The 2nd time and every time after the download is triggered, it includes the images.
Has anyone come across this issue before? Is there anything wrong with my exportToDoc function?
export function exportToDoc(element, filename = "") {
let html =
"<html xmlns:o='urn:schemas-microsoft-com:office:office'" +
" xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'><head><meta><title>Export HTML To Doc</title></head><body>";
let footer = "</body></html>";
html = html + document.getElementById(element).innerHTML + footer;
var statiC = {
mhtml: {
top:
/* eslint-disable */
"Mime-Version: 1.0nContent-Base: " +
location.href +
'nContent-Type: Multipart/related; boundary="NEXT.ITEM-BOUNDARY";type="text/html"nn--NEXT.ITEM-BOUNDARYnContent-Type: text/html; charset="utf-8"nContent-Location: ' +
location.href +
"nn<!DOCTYPE html>n<html>n_html_</html>",
head: '<head>n<meta http-equiv="Content-Type" content="text/html; charset=utf-8">n<style>n_styles_n</style>n</head>n',
body: "<body>_body_</body>",
},
};
var css =
"<style>" +
"img {width:auto; height: 800px, margin-right: 100px}" +
"</style>";
// Image Area %%%%
var options = { maxWidth: 500 };
var images = Array();
var img = document.querySelectorAll("#" + element)[0].querySelectorAll("img");
for (var i = 0; i < img.length; i++) {
// Calculate dimensions of output image
var w = Math.min(img[i].width, options.maxWidth);
var h = img[i].height * (w / img[i].width);
// Create canvas for converting image to data URL
var canvas = document.createElement("CANVAS");
canvas.width = w;
canvas.height = h;
// Draw image to canvas
var context = canvas.getContext("2d");
context.drawImage(img[i], 0, 0, w, h);
// Get data URL encoding of image
var uri = canvas.toDataURL("image/png");
// document.querySelectorAll(img[i])[0].attr("src", img[i].src);
img[i].src = img[i].src;
img[i].width = w;
img[i].height = h;
// Save encoded image to array
images[i] = {
type: uri.substring(uri.indexOf(":") + 1, uri.indexOf(";")),
encoding: uri.substring(uri.indexOf(";") + 1, uri.indexOf(",")),
location: img[i].src,
data: uri.substring(uri.indexOf(",") + 1),
};
}
// Prepare bottom of mhtml file with image data
var imgMetaData = "n";
for (var i = 0; i < images.length; i++) {
imgMetaData += "--NEXT.ITEM-BOUNDARYn";
imgMetaData += "Content-Location: " + images[i].location + "n";
imgMetaData += "Content-Type: " + images[i].type + "n";
imgMetaData += "Content-Transfer-Encoding: " + images[i].encoding + "nn";
imgMetaData += images[i].data + "nn";
}
imgMetaData += "--NEXT.ITEM-BOUNDARY--";
// end Image Area %%
const output = statiC.mhtml.top.replace("_html_", statiC.mhtml.head.replace("_styles_", css) + html) + imgMetaData;
const url = "data:application/vnd.ms-word;charset=utf-8," + encodeURIComponent(output);
//file name
filename = filename ? filename + ".doc" : "document.doc";
// Creates the download link element dynamically
let downloadLink = document.createElement("a");
document.body.appendChild(downloadLink);
//Link to the file
downloadLink.href = url;
//Setting up file name
downloadLink.download = filename;
//triggering the function
downloadLink.click();
//Remove the a tag after download starts.
document.body.removeChild(downloadLink);
}
The element being exported just contain some basic div
, p
, and img
tags
2
Make sure to ensure all images are fully loaded before triggering the export. Here’s an improved version of the exportToDoc function that handles image loading properly:
exportToDoc.js
function exportToDoc(element) {
const images = element.getElementsByTagName('img');
const imagePromises = Array.from(images).map(img => {
return new Promise((resolve, reject) => {
if (img.complete) {
resolve();
} else {
img.onload = resolve;
img.onerror = reject;
}
});
});
Promise.all(imagePromises).then(() => {
const html = element.innerHTML;
const blob = new Blob(['ufeff', html], {
type: 'application/msword'
});
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'document.doc';
link.click();
URL.revokeObjectURL(url);
});
}
This creates promises for each image’s load event and uses Promise.all to wait until all images are loaded before proceeding with the export. The first download was failing because the images weren’t fully loaded when the export was triggered. Now it will work correctly on the first and subsequent downloads.
watertrainer is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.