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 commented 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.com/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"
}
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.com/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.com/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.net/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.org/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.org/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:
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.
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.
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 coming soon, but currently only in Canary behind a flag.
I'd recommend option 3, as by the time you have finished the component 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.
I am trying to make a PDF viewer, and it runs perfectly when I run it through live server on vscode, but for some reason, when I go to my files, right click on the index.html and click open with chrome, firefox, or internet explorer it comes up with 2 errors:
1)GET file://mozilla.github.io/pdf.js/build/pdf.js net::ERR_FILE_NOT_FOUND
main.js:18
2)Uncaught TypeError: Cannot read property 'GlobalWorkerOptions' of undefined
at main.js:18
Here is my index.html code and my main.js code:
Index.html
<!DOCTYPE html>
<html>
<head>
<title>PDF Viewer Attempt 2</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<h1>PDF Name</h1>
<div>
<button id="prev">Previous</button>
<button id="next">Next</button>
<span>Page: <span id="page_num"></span> / <span id="page_count"></span></span>
</div>
<canvas id="the-pdf"></canvas>
<script src="//mozilla.github.io/pdf.js/build/pdf.js"></script>
<script src="js/main.js"></script>
</body>
</html>
main.js:
//Code adapted from https://jsfiddle.net/pdfjs/wagvs9Lf/
// If absolute URL from the remote server is provided, configure the CORS
// header on that server.
var url = 'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf'; //works with this internet pdf
//url = 'pdf.pdf'; // Works with local pdf
// Loaded via <script> tag, create shortcut to access PDF.js exports.
var pdfjsLib = window['pdfjs-dist/build/pdf'];
// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = '//mozilla.github.io/pdf.js/build/pdf.worker.js';
var pdfDoc = null,
pageNum = 1,
pageRendering = false,
pageNumPending = null,
scale = 0.8,
canvas = document.getElementById('the-pdf'),
ctx = canvas.getContext('2d');
/**
* Get page info from document, resize canvas accordingly, and render page.
* #param num Page number.
*/
function renderPage(num) {
pageRendering = true;
// Using promise to fetch the page
pdfDoc.getPage(num).then(function(page) {
var viewport = page.getViewport({scale: scale});
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
var renderTask = page.render(renderContext);
// Wait for rendering to finish
renderTask.promise.then(function() {
pageRendering = false;
if (pageNumPending !== null) {
// New page rendering is pending
renderPage(pageNumPending);
pageNumPending = null;
}
});
});
// Update page counters
document.getElementById('page_num').textContent = num;
}
/**
* If another page rendering in progress, waits until the rendering is
* finised. Otherwise, executes rendering immediately.
*/
function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
renderPage(num);
}
}
/**
* Displays previous page.
*/
function onPrevPage() {
if (pageNum <= 1) {
return;
}
pageNum--;
queueRenderPage(pageNum);
}
document.getElementById('prev').addEventListener('click', onPrevPage);
/**
* Displays next page.
*/
function onNextPage() {
if (pageNum >= pdfDoc.numPages) {
return;
}
pageNum++;
queueRenderPage(pageNum);
}
document.getElementById('next').addEventListener('click', onNextPage);
/**
* Asynchronously downloads PDF.
*/
pdfjsLib.getDocument(url).promise.then(function(pdfDoc_) {
pdfDoc = pdfDoc_;
document.getElementById('page_count').textContent = pdfDoc.numPages;
// Initial/first page rendering
renderPage(pageNum);
});
Any help is appreciated! This was my first post so let me know if there is anything I can do to format better! Dont worry about that line that says error for now, that is just a paragraph block.
I am trying to use pdf.js to load pdf into a web application so that I can further extract information from the pdf file live. But I am getting this error with a very minimal example.
I have tried wrapping the code in $(document).ready() as suggested in Uncaught ReferenceError: PDFJS is not defined when initialising PDF.JS
I am not able to access PDFJS in console either.
Below is the code I am using (from https://www.sitepoint.com/custom-pdf-rendering/)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>PDF.js Learning</title>
</head>
<body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript" src="https://mozilla.github.io/pdf.js/build/pdf.js"></script>
<script type="text/javascript" src="https://mozilla.github.io/pdf.js/build/pdf.worker.js"></script>
<script>
$(document).ready(function () {
var url = "https://github.com/mozilla/pdf.js/blob/master/web/compressed.tracemonkey-pldi-09.pdf";
// Asynchronous download PDF
PDFJS.getDocument(url)
.then(function(pdf) {
return pdf.getPage(1);
})
.then(function(page) {
// Set scale (zoom) level
var scale = 1.5;
// Get viewport (dimensions)
var viewport = page.getViewport(scale);
// Get canvas#the-canvas
var canvas = document.getElementById('the-canvas');
// Fetch canvas' 2d context
var context = canvas.getContext('2d');
// Set dimensions to Canvas
canvas.height = viewport.height;
canvas.width = viewport.width;
// Prepare object needed by render method
var renderContext = {
canvasContext: context,
viewport: viewport
};
// Render PDF page
page.render(renderContext);
});
})
</script>
<canvas id='the-canvas'></canvas>
</body>
</html>
pdfjsLib.getDocument() works. Now I just need to know why...
I am trying to write some code that converts the first page of a PDF to a PNG thumbnail. I have found several examples here on stackoverflow and other place on the web and it seems like a simple thing to do, but I cannot make it work.
I have a HTML file that looks like this
<!DOCTYPE html>
<html>
<head>
<title>JR PDF test</title>
</head>
<body>
<script type="text/javascript" src="/build/pdf.js"></script>
<script type="text/javascript" src="jr-pdf.js"></script>
<script type="text/javascript" src="canvas2image.js"></script>
</body>
<div id="pdf-main-container">
<a id="download-image" href="#">Download PNG</a>
</div>
</div>
</html>
<canvas id="the-canvas"></canvas>
and a the jr-pdf.js looks like this
// URL of PDF document
var url = "REGLEMENT_FOR_RALLY_2012.pdf";
// Asynchronous download PDF
PDFJS.getDocument(url)
.then(function(pdf) {
return pdf.getPage(1);
})
.then(function(page) {
// Set scale (zoom) level
var scale = 1.5;
// Get viewport (dimensions)
var viewport = page.getViewport(scale);
// Get canvas#the-canvas
var canvas = document.getElementById('the-canvas');
// Fetch canvas' 2d context
var context = canvas.getContext('2d');
// Set dimensions to Canvas
canvas.height = viewport.height;
canvas.width = viewport.width;
// Prepare object needed by render method
var renderContext = {
canvasContext: context,
viewport: viewport
};
// Render PDF page
page.render(renderContext);
// JR experiments
console.log('hej');
url = canvas.toDataURL();
downloadButton = document.getElementById('download-image');
downloadButton.setAttribute('download', 'jr.png');
downloadButton.setAttribute('href', url);
});
The first page of the PDF file is rendered correctly to the screen and a jr.png file is generated. When I look at the PNG file the header seems right, but when I try to view the file with an image viewer it is empty (actually it is shown as transparent).
So I guess these lines are wrong:
url = canvas.toDataURL();
downloadButton = document.getElementById('download-image');
downloadButton.setAttribute('download', 'jr.png');
downloadButton.setAttribute('href', url);
Any suggestions on how to make a correct PNG file?
page.render is asynchronous function -- you need to wait until it finishes painting
var renderTask = page.render(renderContext);
renderTask.promise.then(function () {
// use canvas now
var url = canvas.toDataURL();
var downloadButton = document.getElementById('download-image');
downloadButton.setAttribute('download', 'jr.png');
downloadButton.setAttribute('href', url);
});
I'm currently writing a simple Android App due to testing purpose. In it there's a WebView-Element in which a html-File (with javascript (Mozilla PDFJS)) from asset folder should be displayed. From Fragment-Code:
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().setAllowFileAccess(true);
webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
webView.loadDataWithBaseURL("file:///android_asset/", content, "text/html", "utf-8", null);
with content already containing the folowing html:
<script type="text/javascript">
var mypdf = null;
var currentPage = 1;
PDFJS.workerSrc = 'file:///android_asset/pdf.worker.jas';
PDFJS.getDocument('file:///android_asset/pdf.pdf').then(function(pdf) {
mypdf = pdf;
renderPage(currentPage);
});
function renderPage(pageNumber) {
mypdf.getPage(pageNumber).then(function(page) {
var scale = 1;
var viewport = page.getViewport(scale);
var canvas = document.getElementById('viewer');
var context = canvas.getContext('2d');
canvas.height = viewport.height-40;
canvas.width = viewport.width;
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
});
}
</script>
....
<body>
<h1>Header</h1>
<canvas id="viewer"></canvas>
</body>
In logcat I receive the following chromium console output and due to that rendering of the requestet pdf-page does not happen (still, the header is displayed normally)
I/chromium﹕ [INFO:CONSOLE(22)] "Uncaught (in promise) TypeError: Cannot read property 'getContext' of null", source: file:///android_asset/ (22)
Line 22 refering to
var context = canvas.getContext('2d');
When I load the exact same file from a remote server (replacing the file:///-links) via webView.loadUrl("http://www.dennissch.de/pdftest/"); everything works fine.
So how come that document.getElementById seems not to work on the local file?
That might be due to the fact that your script tag is declared above the canvas. try to put your tag below the viewer, and it should work
<body>
<h1>Header</h1>
<canvas id="viewer"></canvas>
<script type="text/javascript">
var mypdf = null;
var currentPage = 1;
PDFJS.workerSrc = 'file:///android_asset/pdf.worker.jas';
PDFJS.getDocument('file:///android_asset/pdf.pdf').then(function(pdf) {
mypdf = pdf;
renderPage(currentPage);
});
function renderPage(pageNumber) {
mypdf.getPage(pageNumber).then(function(page) {
var scale = 1;
var viewport = page.getViewport(scale);
var canvas = document.getElementById('viewer');
var context = canvas.getContext('2d');
canvas.height = viewport.height-40;
canvas.width = viewport.width;
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
});
}
</script>
</body>
Or at least put the call to renderPage below the viewer in an other script tag