最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

jquery - How to render HTML pages as PNGJPEG images on single click using URLs Tabs array in Javascript only? - Stack Overflow

programmeradmin0浏览0评论

I am building a chrome extension using Javascript which gets URLs of the opened tabs and saves the html file, but the problem with html is that it does not render images on webpages fully. So i changed my code to loop each URLs of the tabs and then save each html pages as image file automatically in one click on the download icon of the chrome extension. I have been researching for more than 2 days but nothing happened. Please see my code files and guide me. I know there is library in nodejs but i want to perform html to jpeg/png maker using chrome extension only.

Only icons i have not attached which i have used in this extension, all code i have and the text in popup.js which is mented i have tried.

Current output of the attached code

Updated popup.js

File popup.js - This file has functions which gets all the URLs of the opened tabs in the browser window

// script for popup.html
window.onload = () => {  
    
    // var html2obj = html2canvas(document.body);
    // var queue  = html2obj.parse();
    // var canvas = html2obj.render(queue);
    // var img = canvas.toDataURL("image/png");
    
    let btn = document.querySelector("#btnDL");
    btn.innerHTML = "Download";

    function display(){
        // alert('Click button is pressed')
        window.open("image/url");
    }

    btn.addEventListener('click', display);
}

chrome.windows.getAll({populate:true}, getAllOpenWindows);

function getAllOpenWindows(winData) {

    var tabs = [];
    for (var i in winData) {
      if (winData[i].focused === true) {
          var winTabs = winData[i].tabs;
          var totTabs = winTabs.length;
  
          console.log("Number of opened tabs: "+ totTabs);
  
          for (var j=0; j<totTabs;j++) {

                tabs.push(winTabs[j].url);
                
                // Get the HTML string in the tab_html_string
                tab_html_string = get_html_string(winTabs[j].url)
                
                // get the HTML document of each tab
                tab_document = get_html_document(tab_html_string)

                console.log(tab_document)

                let canvasref = document.querySelector("#capture");
                canvasref.appendChild(tab_document.body);

                html2canvas(document.querySelector("#capture")).then(canvasref => {
                    
                    document.body.appendChild(canvasref)
                    var img = canvasref.toDataURL("image/png");
                    window.open(img)

                });

          }
      }
    }
    console.log(tabs);
}

function get_html_document(tab_html_string){
    
    /**
     * Convert a template string into HTML DOM nodes
     */
    
    var parser = new DOMParser();
    var doc = parser.parseFromString(tab_html_string, 'text/html');
    return doc;

}

function get_html_string(URL_string){

    /**
     * Convert a URL into HTML string
     */
    
    let xhr = new XMLHttpRequest();
    xhr.open('GET', URL_string, false);

    try {
        xhr.send();
        if (xhr.status != 200) {
            alert(`Error ${xhr.status}: ${xhr.statusText}`);
        } else {
                return xhr.response                                
            }

    } catch(err) {
        // instead of onerror
        alert("Request failed");
    }
}

File popup.html - This file represent icon and click functionality on the chrome browser search bar

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
    <script src= './html2canvas.min.js'></script>
    <script src= './jquery.min.js'></script>
    <script src='./popup.js'></script>

    <title>Capture extension</title>
    
        <!--
          - JavaScript and HTML must be in separate files: see our Content Security
          - Policy documentation[1] for details and explanation.
          -
          - [1]: .html
         -->
</head>
<body>

    <button id="btnDL"></button>
    
</body>
</html>

File manifest.json - This file is used by the chrome browser to execute the chrome extension

{ 
      "manifest_version": 2,
      "name": "CIP screen capture",
      "description": "One-click download for files from open tabs",
      "version": "1.4.0.2",
      "browser_action": {
        "default_popup": "popup.html"
      },
      "permissions": [
        "downloads", "tabs", "<all_urls>"
      ],
      "options_page": "options.html"
}

I am building a chrome extension using Javascript which gets URLs of the opened tabs and saves the html file, but the problem with html is that it does not render images on webpages fully. So i changed my code to loop each URLs of the tabs and then save each html pages as image file automatically in one click on the download icon of the chrome extension. I have been researching for more than 2 days but nothing happened. Please see my code files and guide me. I know there is library in nodejs but i want to perform html to jpeg/png maker using chrome extension only.

Only icons i have not attached which i have used in this extension, all code i have and the text in popup.js which is mented i have tried.

Current output of the attached code

Updated popup.js

File popup.js - This file has functions which gets all the URLs of the opened tabs in the browser window

// script for popup.html
window.onload = () => {  
    
    // var html2obj = html2canvas(document.body);
    // var queue  = html2obj.parse();
    // var canvas = html2obj.render(queue);
    // var img = canvas.toDataURL("image/png");
    
    let btn = document.querySelector("#btnDL");
    btn.innerHTML = "Download";

    function display(){
        // alert('Click button is pressed')
        window.open("image/url");
    }

    btn.addEventListener('click', display);
}

chrome.windows.getAll({populate:true}, getAllOpenWindows);

function getAllOpenWindows(winData) {

    var tabs = [];
    for (var i in winData) {
      if (winData[i].focused === true) {
          var winTabs = winData[i].tabs;
          var totTabs = winTabs.length;
  
          console.log("Number of opened tabs: "+ totTabs);
  
          for (var j=0; j<totTabs;j++) {

                tabs.push(winTabs[j].url);
                
                // Get the HTML string in the tab_html_string
                tab_html_string = get_html_string(winTabs[j].url)
                
                // get the HTML document of each tab
                tab_document = get_html_document(tab_html_string)

                console.log(tab_document)

                let canvasref = document.querySelector("#capture");
                canvasref.appendChild(tab_document.body);

                html2canvas(document.querySelector("#capture")).then(canvasref => {
                    
                    document.body.appendChild(canvasref)
                    var img = canvasref.toDataURL("image/png");
                    window.open(img)

                });

          }
      }
    }
    console.log(tabs);
}

function get_html_document(tab_html_string){
    
    /**
     * Convert a template string into HTML DOM nodes
     */
    
    var parser = new DOMParser();
    var doc = parser.parseFromString(tab_html_string, 'text/html');
    return doc;

}

function get_html_string(URL_string){

    /**
     * Convert a URL into HTML string
     */
    
    let xhr = new XMLHttpRequest();
    xhr.open('GET', URL_string, false);

    try {
        xhr.send();
        if (xhr.status != 200) {
            alert(`Error ${xhr.status}: ${xhr.statusText}`);
        } else {
                return xhr.response                                
            }

    } catch(err) {
        // instead of onerror
        alert("Request failed");
    }
}

File popup.html - This file represent icon and click functionality on the chrome browser search bar

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
    <script src= './html2canvas.min.js'></script>
    <script src= './jquery.min.js'></script>
    <script src='./popup.js'></script>

    <title>Capture extension</title>
    
        <!--
          - JavaScript and HTML must be in separate files: see our Content Security
          - Policy documentation[1] for details and explanation.
          -
          - [1]: http://developer.chrome./extensions/contentSecurityPolicy.html
         -->
</head>
<body>

    <button id="btnDL"></button>
    
</body>
</html>

File manifest.json - This file is used by the chrome browser to execute the chrome extension

{ 
      "manifest_version": 2,
      "name": "CIP screen capture",
      "description": "One-click download for files from open tabs",
      "version": "1.4.0.2",
      "browser_action": {
        "default_popup": "popup.html"
      },
      "permissions": [
        "downloads", "tabs", "<all_urls>"
      ],
      "options_page": "options.html"
}
Share Improve this question edited Jan 17, 2021 at 11:17 Nick jones asked Jan 12, 2021 at 15:40 Nick jonesNick jones 832 silver badges20 bronze badges 9
  • The chrome extension currently only extracts URLs of all the opened tabs, If you have option for different approach to save the opened tabs in the windows then please discuss – Nick jones Commented Jan 15, 2021 at 4:58
  • html2canvas should work on browser. You don't need nodeJS html2canvas.hertzen./getting-started. You should be able to import its scripts from some public CDN – João Pimentel Ferreira Commented Jan 15, 2021 at 8:25
  • Check cdnjs./libraries/html2canvas – João Pimentel Ferreira Commented Jan 15, 2021 at 8:26
  • yes but i am confused with developer.chrome./docs/extensions/reference/tabs/…, in this link there is funtion captureVisibleTab which work for capturing but the parameters and its usage in my for loop is not clear. – Nick jones Commented Jan 15, 2021 at 8:35
  • @JoãoPimentelFerreira i know about the html2canvas but if we use it in the for loop then there is need to change the focus of the tabs and download function which download all the opened tabs in image format on single click – Nick jones Commented Jan 15, 2021 at 8:40
 |  Show 4 more ments

3 Answers 3

Reset to default 6 +50

You can use the library html2canvas which renders any html element, particularly the body element, and from the resulted canvas you can have your image

<script type="text/javascript" src="https://github./niklasvh/html2canvas/releases/download/v1.0.0-rc.7/html2canvas.min.js"></script>
<script>
  html2canvas(document.body).then(
    (canvas) => {
      var img = canvas.toDataURL("image/png"); 
    }
  );
</script>

You can get html2canvas from any public CDN, like this one. You don't need nodeJS. Or perhaps directly from the original git repo

<script type="text/javascript" src="https://github./niklasvh/html2canvas/releases/download/v1.0.0-rc.7/html2canvas.min.js"></script>

You can also save canvas as a blob

html2canvas(document.body).then(function(canvas) {
    // Export canvas as a blob 
    canvas.toBlob(function(blob) {
        // Generate file download
        window.saveAs(blob, "yourwebsite_screenshot.png");
    });
});

It's possible with vanilla JS, I guess vanilla is more likely to be used on a extension, the drawback is the end result, when a 3rd party library my already fixed the issues you will encounter with fonts and style (html2canvas or whatever).

So, vanilla js, for some reason stack overflow snippet does not permit window.open, demo here: https://jsfiddle/j67nqsme/

Few words on how it was implemented:

  • using html to svg transformation, a 3rd party library can be used here
  • using canvas to transform svg into pdg or jpg
  • example can export html or page to svg, png, jpg
  • it is not bullet proof - rendering can suffer, style, fonts, ratios, media query

Disclaimer: I won't help you debug or find solution of your problem, it is an answer to your question using vanilla js, from there on is your decision what you are using or how you are using it.

Source code bellow:

<html>
<body>
    <script>
        function export1() {
            const html = '<div style="color:red">this is <br> sparta </div>';
            renderHtmlToImage(html, 800, 600, "image/png");
        }

        function export2() {
            renderDocumentToImage(window.document, 800, 600, "image/png");
        }
        // format is: image/jpeg, image/png, image/svg+xml
        function exportImage(base64data, width, height, format) {
            var img = new Image();
            img.onload = function () {
                if ("image/svg+xml" == format) {
                    var w = window.open("");
                    w.document.write(img.outerHTML);
                    w.document.close();

                }
                else {
                    const canvas = document.createElement("Canvas");
                    canvas.width = width;
                    canvas.height = height;
                    const ctx = canvas.getContext('2d');
                    ctx.fillStyle = "white";
                    ctx.fillRect(0, 0, canvas.width, canvas.height);
                    ctx.drawImage(img, 0, 0);
                    var exportImage = new Image();
                    exportImage.onload = function () {
                        var w = window.open("");
                        w.document.write(exportImage.outerHTML);
                        w.document.close();
                    }
                    exportImage.src = canvas.toDataURL(format);
                }
            }
            img.src = base64data;
        }

        // format is: image/jpeg, image/png, image/svg+xml
        function renderHtmlToImage(html, width, height, format) {
            var svgData = '<svg xmlns="http://www.w3/2000/svg" width="' + width + '" height="' + height + '">'
                + '<foreignObject width="100%" height="100%">'
                + html2SvgXml(html)
                + '</foreignObject>'
                + '</svg>';
            var base64data = "data:image/svg+xml;base64," + btoa(svgData);
            exportImage(base64data, width, height, format);

        }

        function renderDocumentToImage(doc, width, height, format) {
            var svgData = '<svg xmlns="http://www.w3/2000/svg" width="' + width + '" height="' + height + '">'
                + '<foreignObject width="100%" height="100%">'
                + document2SvgXml(doc)
                + '</foreignObject>'
                + '</svg>';
            var base64data = "data:image/svg+xml;base64," + btoa(svgData);
            exportImage(base64data, width, height, format);
        }


        // plain html to svgXml
        function html2SvgXml(html) {
            var htmlDoc = document.implementation.createHTMLDocument('');

            htmlDoc.write(html);
            // process document
            return document2SvgXml(htmlDoc);
        }

        // htmlDocument to
        function document2SvgXml(htmlDoc) {
            // Set the xmlns namespace
            htmlDoc.documentElement.setAttribute('xmlns', htmlDoc.documentElement.namespaceURI);

            // Get XML
            var svcXml = (new XMLSerializer()).serializeToString(htmlDoc.body);
            return svcXml;
        }
    </script>
    <div>
        <h3 style="color:blue">My Title</h3>
        <div style="color:red">
            My Text
        </div>
        <button onclick="export1()">Export Plain Html</button>
        <button onclick="export2()">Export Entire Document</button>
    </div>
</body>
</html>

There are 3 ways this could be approached:

  1. Render this in client javascript using an API like HTML2Canvas. It's isolated, runs in the browser, but relies on a re-render that could get details of the captured page wrong (see most issues with H2C). That might be fine if you just want a small preview and don't mind the odd difference. Also be careful as the render is slow and somewhat heavyweight - background renders of large pages may be noticeable by users, but so will waits for the images to render.

  2. Use a service that runs Chrome to visit the page and then screenshot it. You could write this or buy in a service like Site-Shot to do it for you. This requires a service which will have a cost, but can guarantee the render is accurate and ensures the load on the users' machines is minimal.

  3. Use the Chrome tab capture API to screenshot the tab. This is probably the best option for an extension, but currently you can only capture the current active tab. captureOffscreenTab is ing soon, but currently only in Canary behind a flag.

I'd remend option 3, as by the time you have finished the ponent you could have access to the new features. If you need to release soon you could use 1 or 2 as a short term solution.

发布评论

评论列表(0)

  1. 暂无评论