bubkoo / html-to-image

āœ‚ļø Generates an image from a DOM node using HTML5 canvas and SVG.
MIT License
5.41k stars 505 forks source link

Library works as expected locally, but fonts do not load properly in production environment (Correct base64 string, incorrect image) #340

Closed chanzer0 closed 1 year ago

chanzer0 commented 1 year ago

Description

When using the library locally, calling .toJpeg() works as intended, providing the following image: LT30-Index_2022_10_29-2022_11_29 (17)

However, in production, with the same codebase, the following image with incorrect fonts is generated (even though the fonts render just fine on the actual page): LT30-Index_2022_10_29-2022_11_29 (21)

Update I believe the issue lies somewhere in how the package is handling network requests. Locally, we can see that successful requests are made to the fonts: image

However, in production, these requests are not made: image

I'm not sure why, but therein lies the issue.

Update 2 What's even weirder is I'm console.loging the response from .toJpeg() as follows:

.toJpeg(node, {
    cacheBust: true,
})
.then((dataUrl: string) => {
    console.log(dataUrl);
    const link = document.createElement('a');
    link.download = `Index_${dayjs(startDate).format(
        'YYYY/MM/DD',
    )}-${dayjs(endDate).format('YYYY/MM/DD')}.jpeg`;
    link.href = dataUrl;
    link.click();
});

And when I plug this base-64 string into an online image viewer/decoder, the output is correct (Even though the downloaded image is incorrect:

image

Expected Behavior

Fonts loaded in page should be styled and applied properly to the generated element.

Current Behavior

Font loaded and present in the page are not applied properly to the generated element

Steps To Reproduce

A live example of this can be found at this url by clicking on the "download image" icon present in the graph.

globals.css

@font-face {
    font-family: 'DM Sans';
    src: url('/fonts/DMSans-Regular.woff2') format('woff2');
    font-weight: 400;
    font-display: swap;
}
@font-face {
    font-family: 'Inter';
    src: url('/fonts/Inter-400.woff2') format('woff2');
    font-weight: 400;
    font-display: swap;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

component.tsx

const handleDownloadClick = async (): Promise<void> => {
    const node = document.getElementById(chartId);

    const htmlToImage = await import('html-to-image');

    htmlToImage.toJpeg(node, { quality: 1 }).then((dataUrl: string) => {
        const link = document.createElement('a');
        link.download = `LT30-Index_${dayjs(startDate).format(
            'YYYY/MM/DD',
        )}-${dayjs(endDate).format('YYYY/MM/DD')}.jpeg`;
        link.href = dataUrl;
        link.click();
    });
};
...
return (
    <div id={chartId}>]
        ....
        <div onClick={() => handleDownloadClick()} />
        ....
    </div>
)

Your Environment

vivcat[bot] commented 1 year ago

šŸ‘‹ @chanzer0

Thanks for opening your first issue here! If you're reporting a šŸž bug, please make sure you include steps to reproduce it. To help make it easier for us to investigate your issue, please follow the contributing guidelines.

We get a lot of issues on this repo, so please be patient and we will get back to you as soon as we can.

zackbiernat commented 1 year ago

We had to append the desired fonts / CSS to the target node.

        const targetNode = document.getElementById(targetId);

        const styleElement = document.createElement('style');
        styleElement.textContent = `
            @font-face {
                  # ...

            }
            # Other required styles.
        `;
        targetNode.appendChild(styleElement);

       // Use library to convert targetNode to an image type.
      // ...
chanzer0 commented 1 year ago

@zackbiernat's suggested implementation above is how we resolved this issue

vivcat[bot] commented 7 months ago

This thread has been automatically locked because it has not had recent activity. Please open a new issue for related bugs and link to relevant comments in this thread.