I am trying to generate html to pdf in Node.js using html-pdf-node package. The page is working fine if I open the html file in the browser but when I generate it to pdf using html-pdf-node, the images and the css is not rendered in the pdf.
Here is my code:
template.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Bootstrap Example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type='text/css' href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<link rel="stylesheet" type='text/css' href="../css/style.css">
</head>
<body>
<div class="container">
<div class="text-center">
<img src="../images/logo.png" class="img-fluid" alt="Logo image">
</div>
<div class="container align-items-center atd-title text-center">
<h3>Title Here</h3>
</div>
<div class="container align-items-center atd-container">
<div class="row">
<div class="col-lg-12">
<p>Sir:</p>
<p class="atd-paragraph">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
</div>
</div>
</div>
</div>
</body>
</html>
service.js
let fs = require('fs');
let path = require('path');
let ejs = require('ejs');
let html_to_pdf = require('html-pdf-node');
// Read HTML Template
let template = fs.readFileSync(path.resolve(__dirname, "../path/to/template.html"), 'utf8');
let html = ejs.render(template, {name: "test"});
let options = { format: 'A4' };
let file = { content: html };
let pdf = await html_to_pdf.generatePdf(file, options);
return pdf;
That's because the HTML string is being passed to html-pdf-node, the other assets like images and CSS are not. html-pdf-node uses Puppeteer as a headless browser to render the web page before saving it as PDF, when it tries to resolve the assets it sends HTTP requests that just fail.
You have two options to solve this:
Create a standalone HTML file with inline CSS and images as data URLs
Open a web server so that the assets can be resolved without being embedded
The simplest option is the second one, here is a minimal example:
const express = require('express')
const html_to_pdf = require('html-pdf-node')
const ejs = require('ejs')
const fs = require('fs')
const path = require('path')
const app = express()
const port = 3000
const template = fs.readFileSync(path.resolve(__dirname, "./index.html"), 'utf8')
const content = ejs.render(template, { title: "Awesome title!" })
fs.writeFile(path.resolve(__dirname, "./public/index.html"), content, () => {
app.use(express.static('src/public'))
const server = app.listen(port, async () => {
const url = `http://localhost:${port}`
const options = { format: 'A4', path: 'output.pdf' }
const file = { url }
await html_to_pdf.generatePdf(file, options)
server.close()
})
})
I've created a working project here: https://github.com/Guerric-P/html-pdf-node-demo
Related
I'm currently making a project to put up "Post" from mysql onto a website and then they can also be updated from the website into Mysql. My insert function works fine because I can add post like nothing. Whenever I try and delete a post it gives me a long CORS policy error. I've looked all over the internet for an answer, but haven't found a solution. I've tried installing the CORS extensions in chrome and to also change the header into no cors. I'm the owner of the API being used.
index.js
const baseUrl = "**redacted for security**"
//const baseUrl = "https://localhost:5001/api/post"
//update
function getPost(){
//const allPostsApi = "https://localhost:5001/api/post";
const allPostsApi = baseUrl;
fetch(allPostsApi).then(function(response){
console.log(response);
return response.json();
}).then(function(json){
let html = "<ul>";
json.forEach((post)=>{
html += "<li>" + post.text + " Posted by Big Al! </li>";
})
html += "</ul>";
document.getElementById("Post").innerHTML = html;
}).catch(function(error){
console.log(error);
});
}
function handleOnSubmit(){
console.log("We made it");
var postText = document.getElementById("text").value;
//const placeHolder = document.getElementById("Nothing").value;
//const addPostsApi = "https://localhost:5001/api/post";
console.log(postText);
const addPostsApi = baseUrl;
var text ={
Text: postText
}
PlacePost(text);
}
function handleOnEnter(){
console.log("We made it");
var postId = document.getElementById("id").value;
//const placeHolder = document.getElementById("Nothing").value;
//const addPostsApi = "https://localhost:5001/api/post";
console.log(postId);
const addPostsApi = baseUrl;
var id ={
Text: postId
}
RemovePost(postId);
}
function PlacePost(text){
const PlacePostUrl = baseUrl;
fetch(PlacePostUrl, {
method: "POST",
headers: {
"Accept": 'application/json',
"Content-Type": 'application/json'
},
body: JSON.stringify(text)
}).then(response=>{
getPost();
})
}
function RemovePost(id){
const RemovePostUrl = baseUrl;
fetch(RemovePostUrl, {
mode: 'cors',
method: "PUT",
headers: {
"Accept":'application/json',
"Content-Type": 'application/json; charset=UTF-8'
},
body: JSON.stringify(id)
}).then(response=>{
getPost();
})
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="resources/index.css" rel="stylesheet">
<title>Document</title>
</head>
<body onload = "getPost()">
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script type = "text/javascript" src = "scripts/index.js"></script>
<div id="Post">
</div>
<div class = "row">
<form id = "addPost" onsumbit ="return false;" method = "post">
<label for="title">Enter Post</label>
<input type ="text" name = "text" id ="text">
<input type ="button" value = "Submit" onclick="handleOnSubmit()">
</form>
<form id = "RemovePost" onsubmit ="return false;" method = "put">
<label for ="title">Enter Post Number you wish to delete</label>
<input type ="text" name = "text" id ="id">
<input type ="button" value = "Submit" onclick="handleOnEnter()">
</form>
</div>
</body>
</html>
I'm just very confused on how it does through on the PlacePost, but gets caught up during RemovePost. Any and all help is appreciated.
Remove mode: 'cors' from your RemovePostUrl fetch.
You could set it to same-origin if your js is running on the same domain as your API.
https://developer.mozilla.org/en-US/docs/Web/API/Request/mode
You 100% do not need a browser extension.
install 'cors' by using 'npm i cors'
const express = require("express");
const app = express();
const cors = require("cors");
app.use(
cors({
origin: "*"
}
));
cors does not care about requests between the same origin. If you want to allow request from an within the same origin then you can set the origin value to the URL you want to request. e.g
origin: 'http://127.0.0.1:8000'
And if you want to just allow all URLs to access your site you can instead use origin: '*' in the header and that will allow cross origin requests from any URL
Hope this helps :)
"events.js: 377" occurs when I am running the app.js as a node server on Hyper. Please also mention the full descriptive meaning of this "events.js: 377" and how we can figure it out this.
I am coding a Signup Newsletter and the code related to .html, .js and .css files are attached below.
I have many times changed the pattern of my code but I am not able to run the app.js as it is getting crashed every time.
If anybody knows about the solution then please answer.
Given below is link of the image of the error that is shown on the hyper when I run app.js as a server and it gets crashed.
The below given code is of app.js :
const express = require("express");
const https = require("https");
const bodyParser = require("body-parser");
const request = require("request");
const app = express();
app.use(express.static("public"));
app.use(bodyParser.urlencoded({extended: true}));
app.get("/", function(req, res){
res.sendFile(__dirname + "/signup.html");
})
app.post("/", function(req, res){
const firstName = req.body.fName;
const lastName = req.body.lName;
const email = req.body.email;
const data = {
members:[
{
email_address: email,
status: "subscribed",
merge_fields: {
FNAME: firstName,
LNAME: lastName
}
}
]
}
});
const jsonData = JSON.stringify("data");
const url = "https://us5.api.mailchimp.com/3.0/lists/21760b9a041";
const options = {
method: "POST",
auth: "xxxx"
}
const requestt = https.request(url, options, function(response) {
response.on("data", function(data){
console.log(JSON.parse(data));
})
requestt.write(jsonData);
requestt.end
});
app.listen("3000", function(){
console.log("Server is running on port 3000");
});
The given below code is of signup.html :
***<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
<meta name="generator" content="Hugo 0.88.1">
<title>Signin Template · Bootstrap v5.1</title>
<link rel="canonical" href="https://getbootstrap.com/docs/5.1/examples/sign-in/">
<!-- When Internet will be ON only then this link of Bootstrap will be applied to your website. -->
<!-- Bootstrap core CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-uWxY/CJNBR+1zjPWmfnSnVxwRheevXITnMqoEIeG1LJrdI0GlVs/9cVSyPYXdcSF" crossorigin="anonymous">
<!-- Favicons -->
<link rel="apple-touch-icon" href="/docs/5.1/assets/img/favicons/apple-touch-icon.png" sizes="180x180">
<link rel="icon" href="/docs/5.1/assets/img/favicons/favicon-32x32.png" sizes="32x32" type="image/png">
<link rel="icon" href="/docs/5.1/assets/img/favicons/favicon-16x16.png" sizes="16x16" type="image/png">
<link rel="manifest" href="/docs/5.1/assets/img/favicons/manifest.json">
<link rel="mask-icon" href="/docs/5.1/assets/img/favicons/safari-pinned-tab.svg" color="#7952b3">
<link rel="icon" href="/docs/5.1/assets/img/favicons/favicon.ico">
<meta name="theme-color" content="#7952b3">
<!-- Custom styles for this template -->
<link href="css/styles.css" rel="stylesheet">
</head>
<body class="text-center">
<form class="form-signin" action="/" method="POST">
<img class="mb-4" src="Image/lab.jpg" alt="" width="72" height="57">
<h1 class="h3 mb-3 fw-normal">Signup to my Newsletter.</h1>
<input type="text" name = fName class="form-control middle" id="inputPassword" placeholder="First Name" required>
<input type="text" name = lName class="form-control top" id="inputEmail" placeholder="Last Name" required autofocus>
<input type="email"name = email class="form-control bottom" placeholder="Email" required>
<button class="w-100 btn btn-lg btn-primary btn-block" type="submit">Sign Me Up!</button>
<p class="mt-5 mb-3 text-muted">© 2017–2021</p>
</form>
</body>
</html>***
The given below code is of styles.css :
body {
height: 100%;
}
body {
display: flex;
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
}
.form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: auto;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.top {
margin-bottom: 2px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.middle {
border-radius: 0;
margin-bottom: 2px;
}
.bottom{
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
Looks like you are very new to javascript. I would recommend formatting your code a bit better and you might catch errors like these a bit easier.
The exact cause of your error is that you are getting an error when making your API request to mailchimp and you do not have an error handler. Secondarily the reason you are likely getting that error is due to the fact that you are just sending them the literal string 'data' and nothing else.
Your code when properly formatted looks like this:
app.post("/", function(req, res) {
const firstName = req.body.fName;
const lastName = req.body.lName;
const email = req.body.email;
const data = {
members: [{
email_address: email,
status: "subscribed",
merge_fields: {
FNAME: firstName,
LNAME: lastName
}
}]
}
});
const jsonData = JSON.stringify("data");
const url = "https://us5.api.mailchimp.com/3.0/lists/21760b9a041";
const options = {
method: "POST",
auth: "xxxx"
};
const requestt = https.request(url, options, function(response) {
response.on("data", function(data) {
console.log(JSON.parse(data));
});
requestt.write(jsonData);
requestt.end
});
Note that your post to mailchimp is outside of your app.post callback handler. Due to that code's location it will execute the moment you run your code. First you need to move this code inside your route handler.
app.post("/", function(req, res) {
const firstName = req.body.fName;
const lastName = req.body.lName;
const email = req.body.email;
const data = {
members: [{
email_address: email,
status: "subscribed",
merge_fields: {
FNAME: firstName,
LNAME: lastName
}
}]
};
const jsonData = JSON.stringify("data");
const url = "https://us5.api.mailchimp.com/3.0/lists/21760b9a041";
const options = {
method: "POST",
auth: "xxxx"
};
const requestt = https.request(url, options, function(response) {
response.on("data", function(data) {
console.log(JSON.parse(data));
});
requestt.write(jsonData);
requestt.end
});
});
This will prevent your code from executing the moment you run your code.
Secondly you need to add an error handler to your request method so that if an error occurs you do not cause an unhandled error to crash your program. That is what is causing you to see the Unhandled 'error' event message right below events:377.
const requestt = https.request(url, options, function(response) {
response.on("data", function(data) {
console.log(JSON.parse(data));
});
response.on("error", function(error) {
console.error('Error from mailchimp', error);
});
requestt.write(jsonData);
requestt.end
});
Lastly, the actual error you are seeing is socket hang up. This error is generally a network error meaning that your client's TCP requests to the target server were dropped/rejected or otherwise terminated before they could be completed. This may be a result of not actually calling the end function when making your request. Specifically you have requestt.end, which is a function, but you are not calling it, should be requestt.end();. If this doesn't resolve it then you may want to check the network connectivity of the computer you are running this code on.
Another error I noticed in your code:
const jsonData = JSON.stringify("data");
You probably mean to send the data object that you are creating, in that case you do not want to wrap data in quotes as it is a variable name, not a string. Instead it should be:
const jsonData = JSON.stringify(data);
I also experienced the same issue and I got this fixed. The root cause at my end was that port number I used was engaged/used at another program.
app.listen("3000", function(){...
Once I closed the other program that was using this port number, my program worked fine.
I'm currently trying to parse my products.json file and an icon from FontAwesome. I was coding and then after coming back to my computer, both my JSON file and font-awesome files aren't being read, but I haven't touched my code.
Currently, my file system looks like this:
And below is my code which has the importing statements:
index.html
<head>
<meta charset="utf-8">
<title>Comfy House</title>
<meta name="description" content="The HTML5 Herald">
<meta name="author" content="SitePoint">
<!-- custom css -->
<link rel="stylesheet" href="styles.css">
<script defer src="/font-awesome/js/all.js"></script>
</head>
app.js
// responsible for getting the products from local storage
class Products{
async getProducts(){
try {
let result = await fetch("products.json");
//let's wait until we get the result
let data = await result.json();
//have products variable hold array
let products = data.items;
//destructure the products
products = products.map(item => {
//get the title and price from the fields property
const {title, price} = item.fields;
//get the id from the sys property
const {id} = item.sys;
const image = item.fields.image.fields.file.url;
//return the new product
return {title, price, id, image}
})
return products
} catch (error) {
console.log(error)
}
}
}
class UI {
displayProducts(products){
let result = '';
products.forEach(product => {
result += `
<article class="product">
<div class='img-container'>
<img src=${product.image} alt="product" class='product-img'>
<button class='bag-btn' data-id='${product.id}'>
<i class='fas fa-shopping-cart'></i>
add to bag
</button>
</div>
<h3>${product.title}</h3>
<h4>$${product.price}/roll</h4>
</article>
`
});
//insert the products into the productsDOM
productsDOM.innerHTML = result;
}
...more code...
Just getting the hang of API calls and fetch and have put together the below code to fetch some info from the Trip Advisor API and log a message to the console using this info.
When I call the fetch request function it logs to the console just fine, but as soon as I wrap it in an event listener callback it no longer executes, why is this?
Appreciate any help!
//This is the fetch function kept in a file names request.js
const findRest = async (reviews, closed) => {
const respond = await fetch(
"https://tripadvisor1.p.rapidapi.com/restaurants/list-by-latlng?limit=30¤cy=EUR&distance=2&lunit=km&lang=en_US&latitude=53.3498&longitude=-6.2603",
{
method: "GET",
headers: {
"x-rapidapi-host": "tripadvisor1.p.rapidapi.com",
"x-rapidapi-key": /* my rapidapi key */
}
}
);
if (respond.status === 200) {
let data = await respond.json();
let newData = await data.data;
let data1 = await newData.filter(
review => parseInt(review.num_reviews) >= reviews
);
let data2 = await data1.filter(close => close.is_closed == closed);
return data2;
} else {
throw new Error("Could not provide results within specified parameters");
}
};
//This is the event listener kept in a file names app.js - both files are active and no problems communicating with each other
document.querySelector(".subButton").addEventListener("click", e => {
e.preventDefault();
console.log("click");
const userReviews = parseInt(document.querySelector(".userRev").value);
const userClose = document.querySelector(".userClose").value;
findRest(userReviews, userClose)
.then(data => {
data.forEach(element => {
console.log(
`${element.name} matches your search criterea and is located at ${element.address}
To make a booking, please call ${element.phone}`
);
});
})
.catch(err => {
console.log(err);
});
});
//HTML below
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>What Wine?</title>
<meta name="author" content="Phil My Glass" />
<meta
name="description"
content="An app to help you find the wine you like or something new based on your preferences"
/>
<meta name="keywords" content="wine" />
<link rel="stylesheet" href="style.css" type="text/css" />
</head>
<body>
<header>
<h1>What Restaurant?</h1>
</header>
<main>
<form>
<input class="userRev" /><br />
<input class="userClose" />
<button class="subButton" type="submit">Find!</button>
</form>
</main>
</body>
<script src="req.js" type="text/Javascript"></script>
<script src="app.js" type="text/Javascript"></script>
</html>
Those two lines look like they could break the thread:
const userReviews = parseInt(document.querySelector(".userRev").value);
const userClose = document.querySelector(".userClose").value;
If either one of document.querySelector(".userRev"), document.querySelector(".userClose") is null, that will be uncaught TypeError.
Will know for sure with the HTML.
I'm trying to send a message when the 'q' key is pressed from my index.js file to the script on index.html, but I don't really know why It's not working properly.
Here is my js file
const url = require('url');
const path = require('path');
const {app, BrowserWindow, globalShortcut, ipcMain, webContents} = require('electron');
let mainWindow;
app.on('ready', function(){
// Create new window
mainWindow = new BrowserWindow({
backgroundColor: '#000000',
fullscreen : true,
frame : false,
icon : __dirname + "/res/icon.jpg",
webPreferences: {
nodeIntegration : true
}
});
// Load html in window
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes:true
}))
globalShortcut.register('Esc', () => {
app.quit();
});
globalShortcut.register('q', () => {
leftLight();
});
});
function leftLight() {
mainWindow && mainWindow.webContents.send('key-pressed-q');
console.log("Sending q pressed to html...");
}
And the html
<!DOCTYPE html>
<html lang="en">
<meta name="viewport" content="width=device-width, initial-scale=1">
<head>
<link rel="stylesheet" href="styles.css">
<title>Document</title>
</head>
<body>
<div class = rect_green> <h2 class=blocktext >LEFT FENCER</h2></div>
<div class = rect_red><h2 class=blocktext> RIGHT FENCER</h2> </div>
<div class = crono> <h2 class=crontext>3:00</h2></div>
</body>
<script type="text/javascript">
var ipc = require('electron').ipcRenderer;
ipc.on('key-pressed-q', (e) => {
//var element = document.getElementsByClassName("rect_green");
//element.style["background-color"] = "yellow";
console.log("q pressed in html file");
});
</script>
</html>
The key pressed is detected, but the message is not received by the ipcRenderer. Any mistakes on my code?
It seems like you've got the syntax of .webContents.send( wrong.
You need to provide a channel for your message, E.g. .webContents.send('channel', 'msg'), then you listen for that channel in your page.
A channel can be whatever you want. For example:
Electron js file:
mainWindow.webContents.send('channelNameCanBeAnything', 'key-pressed-q');
Html file:
ipc.on('channelNameCanBeAnything', (msgStr) => {
console.log("q pressed in html file");
console.log(msgStr); // Will output 'key-pressed-q'.
});
See the docs