I'm having trouble trying to embed a Highchart's chart image into an Excel file. I am basically trying to use an existing "Export Excel" button on a website to now export a static image of the Highchart displayed on the website into an excel file. I'll add the relevant code below:
async getChartPng(chartInstance): Promise<string> {
console.log('getChartPng called');
return new Promise(async (resolve, reject) => {
console.log('Inside the promise');
try {
chartInstance.exportChart({}, (dataUrl) => {
console.log('Inside exportChart callback');
// Log the value of dataUrl
console.log('dataUrl value:', dataUrl);
if (dataUrl) {
console.log('DataUrl:', dataUrl);
resolve(dataUrl);
} else {
console.log('DataUrl is empty or undefined');
reject('Failed to get dataUrl');
}
});
console.log('After exportChart function call');
} catch (error) {
console.error('Error in exportChart function:', error);
reject(error);
}
});
}
async addChartImage(workbook: ExcelJS.Workbook, chartInstance {
console.log('addChartImage called');
const chartWorksheet = workbook.addWorksheet('HighChart Image');
console.log('chartWorksheet created:', chartWorksheet);
try {
const chartImagePng = await this.getChartPng(chartInstance);
console.log('chartImagePng:', chartImagePng);
const base64Image = chartImagePng.split(',')[1];
const imageId = workbook.addImage({
base64: base64Image,
extension: 'png',
});
chartWorksheet.addImage(imageId, {
tl: { col: 0, row: 0 },
ext: { width: 600, height: 400 },
});
} catch (error) {
console.error('Error adding chart image to workbook:', error);
}
}
My code never enters into the exportChart callback function and I'm not sure why. That is to say the console.log message ('Inside exportChart callback') never prints but the message ('After exportChart function call') does print to the console browser.
I should add that a worksheet named HighChart image does show up in an excel file when I click an existing custom button on the website as intended. However, the worksheet shows up as empty and a snapshot of the Highchart image downloads as a separate png file along with the Excel file. However, what I really need is the Highchart image to show up in the excel file, in the proper worksheet. How can I do that?
The recommended solution to get the image programmatically and not to a file is to get the SVG through getSVG
and then convert the SVG to an image. Neither exportChart
nor exportChartLocal
the latter despite the name (local means no external server is used) don't allow that, I ask myself as @RNanoware where did you get that whole asynchronous handler as second argument. Maybe I'm missing something?
Since people seem to still ask similar questions and no simple solution exists (let me know if there is, so I'll delete this post), I'm giving here a basic solution based on this post; it can be extended and adapted to other formats. Adapting yourgetChartPng
function, I'd have (pure js):
async function getChartPng(chartInstance, options = {}){
const svg = chartInstance.getSVG(options);
const svgData = `data:image/svg+xml,${encodeURIComponent(svg)}`
const loadImage = async url => {
const img = document.createElement('img');
img.src = url
return new Promise((resolve, reject) => {
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
}
const img = await loadImage(svgData);
const canvas = document.createElement('canvas')
canvas.width = options.width || chartInstance.chartWidth;
canvas.height = options.height || chartInstance.chartHeight;
canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
return canvas.toDataURL(`image/png`, 1.0);
}
Here's a snippet with its use to add an image to a div element, which also contains an adaptation of your exceljs code (unused):
const myChart = new Highcharts.Chart({
series: [
{
name: "Purchase",
data: [44, 55, 57, 56, 61, 58, 63, 60, 66],
color: "#4FC0FF",
},
{
name: "Sales",
data: [76, 85, 101, 98, 87, 105, 91, 114, 94],
color: "#DF5638",
},
{
name: "Expense",
data: Array.from({length: 9}, () => Math.floor(200 + Math.random() * 100)),
color: "#00A825",
}
],
chart: {
renderTo: 'chart1',
type: "bar",
},
plotOptions: {
bar: {
horizontal: false,
columnWidth: "55%",
endingShape: "rounded",
},
},
dataLabels: {
enabled: false,
},
xAxis: {
categories: [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
],
},
fill: {
opacity: 1,
colors: ["#4FC0FF", "#DF5638", "#00A825"],
}
});
async function getChartPng(chartInstance, options = {}){
const svg = chartInstance.getSVG(options);
const svgData = `data:image/svg+xml,${encodeURIComponent(svg)}`
const loadImage = async url => {
const img = document.createElement('img');
img.src = url
return new Promise((resolve, reject) => {
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
}
const img = await loadImage(svgData);
const canvas = document.createElement('canvas')
canvas.width = options.width || chartInstance.chartWidth;
canvas.height = options.height || chartInstance.chartHeight;
canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
return canvas.toDataURL(`image/png`, 1.0);
}
async function addChartImageToElement(element, chartInstance, options){
try {
const base64Image = await this.getChartPng(chartInstance, options);
const img = document.createElement('img');
img.src = base64Image;
element.appendChild(img);
} catch (error) {
console.error('Error adding chart image to dom:', error);
}
}
async function addChartImageToWorksheet(chartWorksheet, chartInstance, options, col = 0, row = 0) {
try {
const base64Image = await this.getChartPng(chartInstance, options);
const imageId = workbook.addImage({
base64: base64Image,
extension: 'png',
});
chartWorksheet.addImage(imageId, {
tl: { col, row},
ext: {
width: options.width || chartInstance.chartWidth,
height: options.height || chartInstance.chartHeight
},
});
} catch (error) {
console.error('Error adding chart image to workbook:', error);
}
}
function addToWorkbook(workbook){
const chartWorksheet = workbook.addWorksheet('HighChart Image');
addChartImageToWorksheet(
document.querySelector('#out_png'), myChart, {width: 600, height: 400}
);
}
function displayPNG(){
addChartImageToElement(
document.querySelector('#out_png'), myChart, {width: 300, height: 200}
);
}
document.querySelector('#export').onclick = displayPNG;
<div id="chart1" style="height:250px; width:80vw">
</div>
<button id="export">Export</button>
<div id="out_png" style=""></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highcharts/10.3.3/highcharts.js" integrity="sha512-8cJ3Lf1cN3ld0jUEZy26UOg+A5YGLguP6Xi6bKLyYurrxht+xkLJ9oH9rc7pvNiYsmYuTvpe3wwS6LriK/oWDg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highcharts/10.3.3/modules/exporting.min.js" integrity="sha512-azuQazgd4m6TavtXB0Zgm1Pb7RBk7Bq0G9JV8nmec7/toRckUZ6L/fG8D3xcoXk0Le9DS+5YmAi421fry057Fg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>