Puppeteer: cannot render pdf with images stored locally - pdf-generation

I cannot get images stored locally to be rendered in generated pdf with Puppeteer, but external images for which I specify a url work.
In particular, in the sample code below, rendering the page in test_html1 works, while rendering the test_html2 does not work.
(async () => {
const browser = await puppeteer.launch({ args: ['--no-sandbox'] });
const page = await browser.newPage();
const test_html1 = `<html><h3>Hello world!</h3><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Google_2015_logo.svg/1024px-Google_2015_logo.svg.png"></html>`;
// const test_html2 = `<html><h3>Hello world!</h3><img src="file:///home/cristina/Documents/logo.jpg"></html>`;
await page.goto(`data:text/html,${test_html}`, { waitUntil: 'networkidle0' });
await page.pdf({ path: `${this.outputPath}/test-puppeteer.pdf`,
format: 'A4', landscape: !data.isPortrait,
margin: { top: '0.5cm', right: '1cm', bottom: '0.8cm', left: '1cm' }, printBackground: true });
await browser.close();
})();
Result with test_html1:
Result with test_html2:
My questions:
Does Puppeteer work with absolute img paths?
If yes, am I specifying the path correctly? Or is there something else I am doing wrong?

Solved on Puppeteer GitHub: https://github.com/GoogleChrome/puppeteer/issues/1643
Basically, it doesn't work because, as a security measure against malicious websites, access to local images is blocked.

function base64_encode(file) {
var bitmap = fs.readFileSync(file);
return new Buffer(bitmap).toString('base64');
}
img.src = 'data:image/png;base64,' + base64_encode(imagePath);
You can use above function it's render very fast.

Related

Image uploaded incorrectly to s3 using amplify Storage (React Native - Expo - Amplify)

I'm trying to upload an image to s3 with Amplify Storage API.
I use the EXPO imagePicker to upload the image from the phone to React-Native, and it's displayed correctly.
Then I upload it to s3 with amplify Storage.put, and it reaches the correct folder in s3, with the proper filename that I gave it and with public access I provided (to the image and the bucket itself).
BUT if I try to open the photo uri in s3 it doesn't display. When I inspect the browser it shows this error on console:
If I paste in the browser the uri I get from Storage.get , i get this error:
My code is the following (I helped myself with this tutorial https://blog.expo.io/how-to-build-cloud-powered-mobile-apps-with-expo-aws-amplify-2fddc898f9a2):
_handleImagePicked = async (pickerResult) => {
const s3path = 'fotos_cdcc/' + '186620' + '/'
const imageName = '186620_4' + '.jpeg'
const key = s3path + imageName
const fileType = pickerResult.type;
const access = { level: "public", contentType: 'image/jpg' };
const imageData = await global.fetch(pickerResult.uri)
const blobData = await imageData.blob()
try {
await Storage.put(
key,
blobData,
access,
fileType
).then(result => console.log(result))
} catch (err) {
console.log('error: ', err)
}
}
The pickerResult has this form:
Object {
"cancelled": false,
"height": 750,
"type": "image",
"uri": "file:///var/mobile/Containers/Data/Application/021D6288-9E88-4080-8FBF-49F5195C2073/Library/Caches/ExponentExperienceData/%2540anonymous%252Fcaballos-app-dd2fa92e-4625-47e7-940e-1630e824347a/ImagePicker/D9EE1F24-D840-457F-B884-D208FFA56892.jpg",
"width": 748,
}
I tried making the bucket and the photo public in s3, but the error persists.
Any ideas?
Thanks in advance!
Maybe this is not an issue with aws-amplify, instead with fetch and blob. Can you try this?
function urlToBlob(url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.onerror = reject;
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
resolve(xhr.response);
}
};
xhr.open('GET', url);
xhr.responseType = 'blob'; // convert type
xhr.send();
})
}
Then instead of
const imageData = await global.fetch(pickerResult.uri)
const blobData = await imageData.blob()
Try
const blobData = await urlToBlob(pickerResult.uri)
Please find full discussion https://github.com/expo/firebase-storage-upload-example/issues/13

Wait for SVG to load (Ajax) before running a function

I've really searched for days, but couldn't find a solution:
I have an external SVG (800k) which i need to load completely before calling the following function. Someone helped me with the code to load the SVG but I cant figure out how to detect when the SVG is completely loaded. (I am not using jQuery). I need the function initialSvgSettings() to run after the SVG has finished loading completely. Where I have it right now, it just runs when the SVG starts loading but not when it has completed.
This is the code I am using to load the SVG:
// LOAD SVG
var svgOverlay = viewer.svgOverlay();
var path = "images/elementosV6.svg";
function loadSVG(path, callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(e) {
try {
if (xhr.readyState == 4 && xhr.status == 200) {
callback(xhr.responseXML.documentElement);
initialSvgSettings();
}
} catch (e) {
console.log(e);
}
};
xhr.open("GET", path, true);
xhr.overrideMimeType("text/xml");
xhr.responseType = "document";
xhr.send();
}
loadSVG(path, function(data) {
var g = data.getElementById("elements");
svgOverlay.node().appendChild(g);
}); // FIN LOAD SVG
The answer nearest to your question would be to point out that instead of using XMLHttpRequest.onreadystatechange, you could use the .onload event handler, which is called only after the resource has been completely loaded.
But you can also simplify your code by loading the image into an offscreen <object>. Attach your callback to .onload, and with .contentDocument you can access the DOM of the SVG.
var svgContainer = document.createElement('object');
svgContainer.type = 'image/svg+xml';
svgContainer.onload = function () {
var g = svgContainer.contentDocument.getElementById("elements");
svgOverlay.node().appendChild(g);
}
svgContainer.data = 'images/elementosV6.svg';

How can I use NativeScript 3 to capture image and send to a remote server

I'm new to NativeScript and I'm trying to capture an image with the camera module (this works fine), and convert it to base64 (this is not working) and POST to server.
I've googled for days. Any help you can lend would be immensely appreciated.
I've tried this about 16 billion different ways and this is my current code:
viewModel.takePicture1 = function() {
camera.requestPermissions();
var isAvailable = camera.isAvailable();
console.log(isAvailable);
var options = { width: 640, keepAspectRatio: true, saveToGallery: false };
camera.takePicture().then(function (img) {
try{
var imageData = img.toBase64String("jpeg"); // fails here
console.log(imageData);
}catch(err){
console.log("Error: "+err);
}
http.request({
url: "http://[server address]/lab/ns_exp/upload_test.php",
method: "POST",
headers: { "Content-Type": "application/base64" },
content: imageData
}).then(function() {
console.log("Upload successful");
}).catch(function(e) {
console.log("Unsuccessful upload", e);
});
});
}//
Oh, I do want to make clear that I'm not using angular (obviously), so please don't provide an answer that does so. : ) (Vuejs Holdout)
The key here is that base64 needs to know that the image is a JPEG, and what quality the image should be. The code should look like this:
camera.takePicture(cameraOptions)
.then(imageAsset => {
imageSource.fromAsset(imageAsset).then(res => {
myImageSource = res;
var base64 = myImageSource.toBase64String("jpeg", 100);
Just in case someone finds this later and wonders about putting the image (UI) and/or the image (base64) into an observableArray, here is my complete function:
viewModel.takePhoto = function(){
var self = this;
camera.requestPermissions();
var cameraOptions = { width: 640, keepAspectRatio: true, saveToGallery: false };
camera.takePicture(cameraOptions)
.then(imageAsset => {
imageSource.fromAsset(imageAsset).then(res => {
myImageSource = res;
var base64 = myImageSource.toBase64String("jpeg", 100);
self.photoData.push({"data": base64});
var image = new imageModule.Image();
image.src = imageAsset;
self.photoUI.push({"src": image.src});
listView.refresh();
})
}).catch(function (err) {
console.log("Error -> " + err.message);
});
}

how to append iframe to hosted page using Firefox SDK addon?

Assume frame.html inside data folder in Firefox SDK addon,
how to append an iframe and define frame.html as its source?
Additional info:
Because of CPS, its not possible to use inline source, so I can't use data.load('frame.html'), I need to use URL of the file:
lib/main.js
tabs.activeTab.attach({
contentScriptFile: [self.data.url("js/appender.js") ],
attachTo: 'top',
contentScriptOptions: {
scriptUrl: self.data.url("js/sandbox.js"),
imageUrl: self.data.url("image.jpg")
frameUrl: self.data.url("sandbox.html")
}
});
data/appender.js
var scriptTag = document.createElement("script");
scriptTag.src = self.options.scriptUrl;
document.body.appendChild(scriptTag); // worked fine
var imageTag = document.createElement("image");
imageTag.src = self.options.imageUrl;
document.body.appendChild(imageTag); // worked fine
var iframeTag = document.createElement("iframe");
iframeTag.src = self.options.frameUrl;
document.body.appendChild(iframeTag); // the html tag of iframe is empty
It's blank because of the firefox security policy which doens't allow content scripts to load resources URLs as iframes. The solution could be to set it directly from the background script, for this you'll need to use low level sdk.
var { viewFor } = require("sdk/view/core");
var tab_utils = require("sdk/tabs/utils");
var iframe = viewFor(tab).linkedBrowser._contentWindow.document.querySelector('iframe[src="' + self.data.url("sandbox.html") + '"]')
iframe.contentWindow.location.href = iframe.getAttribute("src")
so the completely working code would look like:
data/appender.js
var iframeTag = document.createElement("iframe");
iframeTag.src = self.options.frameUrl;
document.body.appendChild(iframeTag); // the html tag of iframe is empty
// note that the iframe is attached after the content ready event, so you have to inform the background when it can start working with the iframe
self.port.emit("attached", true);
main.js
tabs = require("sdk/tabs")
self = require("sdk/self")
var { viewFor } = require("sdk/view/core");
tab_utils = require("sdk/tabs/utils");
tabs.on("ready", function(tab) {
var worker = tab.attach({
contentScriptFile: [self.data.url("js/appender.js") ],
attachTo: 'top',
contentScriptOptions: {
frameUrl: self.data.url("sandbox.html")
}
});
worker.port.on('attached', function() {
reinitIframe(tab)
});
function reinitIframe(tab) {
var iframe = viewFor(tab).linkedBrowser._contentWindow.document.querySelector('iframe[src="' + self.data.url("sandbox.html") + '"]')
iframe.contentWindow.location.href = iframe.getAttribute("src")
}
})
You'll probably need a cross-process wrapper in order to make it work in future version of Firefox with electrolysis
Found a better way. You add a blank <iframe> and attach an event listener for the load event. Then you can easily append whatever elements you want to the iframe as usual.
Include this in your content-script:
var iframe = document.createElement('iframe'),
div = document.createElement('div');
div.textContent('Hello World');
document.body.appendChild(iframe);
iframe.addEventListener('load', function(){
this.contentWindow.document.body.appendChild(div)
})

Kendo Chart Export to Pdf in IE9

kendo UI chart API not working in ie9.Its working fine in chrome, firefox and ie10.I didn't get any exception in ie9.simply page refreshed.please see below code is not working in ie9
$("#btnExportToPDFView").click(function () {
// Convert the DOM element to a drawing using kendo.drawing.drawDOM
kendo.drawing.drawDOM($(".content-wrapper"))
.then(function (group) {
// Render the result as a PDF file
return kendo.drawing.exportPDF(group, {
paperSize: "auto",
margin: { left: "1cm", top: "1cm", right: "1cm", bottom: "1cm" }
});
})
.done(function (data) {
// Save the PDF file
kendo.saveAs({
dataURI: data,
fileName: "Reports.pdf"
//proxyURL: "http://demos.telerik.com/kendo-ui/service/export"
});
});
});
The kendo documentation here says that IE9 requires the use of the proxyURL that you have commented out in your code. You will need a service that can "return the decoded file with set "Content-Disposition" header."
Here is an example of a web service that you could use:
var response = System.Web.HttpContext.Current.Response;
var request = System.Web.HttpContext.Current.Request;
var fileName = request.Params["fileName"];
var contentType = request.Params["contentType"];
var encodedString = request.Params["base64"];
byte[] bytes = Convert.FromBase64String(encodedString);
response.Clear();
response.ClearHeaders();
response.ContentType = contentType;
response.AddHeader("Content-Disposition", "attachment; filename=\"" + fileName);
response.OutputStream.Write(bytes,0,bytes.Length);
response.Flush();
response.End();

Resources