Render HTML to an image

后端 未结 17 1770
栀梦
栀梦 2020-11-22 16:06

Is there a way to render html to image like PNG? I know that it is possible with canvas but I would like to render standard html element like div for example.

相关标签:
17条回答
  • 2020-11-22 16:22

    May I recommend dom-to-image library, that was written solely to address this problem (I'm the maintainer).
    Here is how you use it (some more here):

    var node = document.getElementById('my-node');
    
    domtoimage.toPng(node)
        .then (function (dataUrl) {
            var img = new Image();
            img.src = dataUrl;
            document.appendChild(img);
        })
        .catch(function (error) {
            console.error('oops, something went wrong!', error);
        });
    
    0 讨论(0)
  • 2020-11-22 16:22

    You can add reference HtmlRenderer to your project and do the following,

    string htmlCode ="<p>This is a sample html.</p>";
    Image image = HtmlRender.RenderToImage(htmlCode ,new Size(500,300));
    
    0 讨论(0)
  • 2020-11-22 16:24

    HtmlToImage.jar will be the simplest way to convert a html into an image

    Converting HTML to image using java

    0 讨论(0)
  • 2020-11-22 16:25

    I read the answer by Sjeiti which I found very interesting, where you with just a few plain JavaScript lines can render HTML in an image.

    We of course have to be aware of the limitations of this method (please read about some of them in his answer).

    Here I have taken his code a couple of steps further.

    An SVG-image has in principle infinite resolution, since it is vector graphics. But you might have noticed that the image that Sjeiti's code generated did not have a high resolution. This can be fixed by scaling the SVG-image before transferring it to the canvas-element, which I have done in the last one of the two (runnable) example codes i give below. The other thing I have implemented in that code is the last step, namely saving it as a PNG-file. Just to complete the whole thing.

    So, I give two runnable snippets of code:

    The first one demonstrates the infinite resolution of an SVG. Run it and zoom in with your browser to see that the resolution does not diminish as you zoom in.

    In the snippet that you can run I have used backticks to specify a so called template string with line breaks so that you can more clearly see the HTML that is rendered. But otherwise, if that HTML is within one line, then the code will be very short, like this.

    const body = document.getElementsByTagName('BODY')[0];
    const img = document.createElement('img')
    img.src = 'data:image/svg+xml,' + encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml" style="border:1px solid red;padding:20px;"><style>em {color:red;}.test {color:blue;}</style>What you see here is only an image, nothing else.<br /><br /><em>I</em> really like <span class="test">cheese.</span><br /><br />Zoom in to check the resolution!</div></foreignObject></svg>`);        
    body.appendChild(img);
    

    Here it comes as a runnable snippet.

    const body = document.getElementsByTagName('BODY')[0];
    const img = document.createElement('img')
    img.src = 'data:image/svg+xml,' + encodeURIComponent(`
      <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
        <foreignObject width="100%" height="100%">
          <div xmlns="http://www.w3.org/1999/xhtml" style="border:1px solid red;padding:20px;">
            <style>
              em {
                color:red;
              }
              .test {
                color:blue;
              }
            </style>
            What you see here is only an image, nothing
            else.<br />
            <br />
            <em>I</em> really like <span class="test">cheese.</span><br />
            <br />
            Zoom in to check the resolution!
          </div>
        </foreignObject>
      </svg>
    `);
    body.appendChild(img);

    Zoom in and check the infinite resolution of the SVG.

    The next runnable, below, is the one that implements the two extra steps which I mentioned above, namely improving resolution by first scaling the SVG, and then the saving as a PNG-image.

    window.addEventListener("load", doit, false)
    var canvas;
    var ctx;
    var tempImg;
    function doit() {
        const body = document.getElementsByTagName('BODY')[0];
        const scale = document.getElementById('scale').value;
        let trans = document.getElementById('trans').checked;
      if (trans) {
        trans = '';
      } else {
        trans = 'background-color:white;';
      }
        let source = `
            <div xmlns="http://www.w3.org/1999/xhtml" style="border:1px solid red;padding:20px;${trans}">
                <style>
                    em {
                        color:red;
                    }
                    .test {
                        color:blue;
                    }
                </style>
                What you see here is only an image, nothing
                else.<br />
                <br />
                <em>I</em> really like <span class="test">cheese.</span><br />
                <br />
                <div style="text-align:center;">
                    Scaling:
                    <br />
                    ${scale} times!
                </div>
            </div>`
        document.getElementById('source').innerHTML = source;
        canvas = document.createElement('canvas');
        ctx = canvas.getContext('2d');
        canvas.width = 200*scale;
        canvas.height = 200*scale;
        tempImg = document.createElement('img');
        tempImg.src = 'data:image/svg+xml,' + encodeURIComponent(`
            <svg xmlns="http://www.w3.org/2000/svg" width="${200*scale}" height="${200*scale}">
                <foreignObject 
                    style="
                        width:200px;
                        height:200px;
                        transform:scale(${scale});
                    "
                >` + source + `
                </foreignObject>
            </svg>
        `);
    }
    function saveAsPng(){
        ctx.drawImage(tempImg, 0, 0);
        var a  = document.createElement('a');
        a.href = canvas.toDataURL('image/png');
        a.download = 'image.png';
        a.click();
    }
    <table border="0">
        <tr>
            <td colspan="2">
                The claims in the HTML-text is only true for the image created when you click the button.
            </td>
        </tr>
        <tr>
            <td width="250">
                <div id="source" style="width:200px;height:200px;">
                </div>
            </td>
            <td valign="top">
                <div>
                    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;In this example the PNG-image will be squarish even if the HTML here on the left is not exactly squarish. That can be fixed.<br>
                    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;To increase the resolution of the image you can change the scaling with this slider.
                    <div style="text-align:right;margin:5px 0px;">
                        <label style="background-color:#FDD;border:1px solid #F77;padding:0px 10px;"><input id="trans" type="checkbox" onchange="doit();" />&nbsp;&nbsp;Make it transparent</label>
                    </div>
                    <span style="white-space:nowrap;">1<input id="scale" type="range" min="1" max="10" step="0.25" value="2" oninput="doit();" style="width:150px;vertical-align:-8px;" />10&nbsp;&nbsp;<button onclick="saveAsPng();">Save as PNG-image</button></span>
                </div>
                
            </td>
        </tr>
    </table>

    Try with different scalings. If you for example set scaling to 10, then you get a very good resolution in the generated PNG-image. And I added a little extra feature: a checkbox so that you can make the PNG-image transparent if you like.

    Notice:

    The Save-button does not work in Chrome and Edge when this script is run here at Stack Overflow. The reason is this https://www.chromestatus.com/feature/5706745674465280 .

    Therefore I have also put this snippet on https://jsfiddle.net/7gozdq5v/ where it works for those browsers.

    0 讨论(0)
  • 2020-11-22 16:28

    All the answers here use third party libraries while rendering HTML to an image can be relatively simple in pure Javascript. There is was even an article about it on the canvas section on MDN.

    The trick is this:

    • create an SVG with a foreignObject node containing your XHTML
    • set the src of an image to the data url of that SVG
    • drawImage onto the canvas
    • set canvas data to target image.src

    const {body} = document
    
    const canvas = document.createElement('canvas')
    const ctx = canvas.getContext('2d')
    canvas.width = canvas.height = 100
    
    const tempImg = document.createElement('img')
    tempImg.addEventListener('load', onTempImageLoad)
    tempImg.src = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml"><style>em{color:red;}</style><em>I</em> lick <span>cheese</span></div></foreignObject></svg>')
    
    const targetImg = document.createElement('img')
    body.appendChild(targetImg)
    
    function onTempImageLoad(e){
      ctx.drawImage(e.target, 0, 0)
      targetImg.src = canvas.toDataURL()
    }

    Some things to note

    • The HTML inside the SVG has to be XHTML
    • For security reasons the SVG as data url of an image acts as an isolated CSS scope for the HTML since no external sources can be loaded. So a Google font for instance has to be inlined using a tool like this one.
    • Even when the HTML inside the SVG exceeds the size of the image it wil draw onto the canvas correctly. But the actual height cannot be measured from that image. A fixed height solution will work just fine but dynamic height will require a bit more work. The best is to render the SVG data into an iframe (for isolated CSS scope) and use the resulting size for the canvas.
    0 讨论(0)
  • 2020-11-22 16:28

    I don't expect this to be the best answer, but it seemed interesting enough to post.

    Write an app that opens up your favorite browser to the desired HTML document, sizes the window properly, and takes a screen shot. Then, remove the borders of the image.

    0 讨论(0)
提交回复
热议问题