Home > OS >  How to load external CSS file with Pug
How to load external CSS file with Pug

Time:10-08

I am trying to load a css file and an image into my pug file and neither are loading. It feels like I have tried everything I could find with no solution. I have my css and image in the public folder which is set as static with express. I appreciate any help I can get.

The folder structure is as follows:

server.js
views
  checklist.pug
routes
  checklist.js
public
  images
    logo.png
  stylesheets
    checklist.css
pdfs

server.js

require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const app = express();

const checklists = require('./routes/checklists');

const port = 3000;

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// View engine
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(express.static(path.join(__dirname, 'public')));

// Routes
app.use('/checklists', checklists);

app.listen(port, () => {
    console.log(`Server started on port ${port}...`);
});

checklist.pug

html(lang='en')
    head
        link(href='stylesheets/checklist.css' rel='stylesheet')
        link(rel='preconnect' href='https://fonts.googleapis.com')
        link(rel='preconnect' href='https://fonts.gstatic.com' crossorigin)
        link(href='https://fonts.googleapis.com/css2?family=Roboto&display=swap' rel='stylesheet')
    body 

        div(class='checklist-title')
            img(src='images/logo.png' alt='Logo...')

checklist.js

const express = require('express');
const aws = require('aws-sdk');
const { v4 } = require('uuid');
const puppeteer = require('puppeteer');
const pug = require('pug');

const dynamodb_config = require('../config/dynamodb/dynamo_config');
aws.config.update(dynamodb_config);
const { validateChecklist } = require('../middleware/validation');
const authenticateToken = require('../middleware/authenticateToken');

const router = express.Router();
const docClient = new aws.DynamoDB.DocumentClient();

const TABLE_NAME = 'Checklists';

// Generate html from checklist object, convert to pdf and send in response
router.get('/:id/pdf', authenticateToken, (req, res) => {
    // Get the checklist and make sure the user owns it
    const params = {
        TableName: TABLE_NAME,
        Key: {
            id: req.params.id
        }
    }
    docClient.get(params, async (err, data) => {
        if (err) {
            console.error(err);
            res.status(500);
            res.json({
                error: err
            });
        }
        else {
            // Check that an item was found
            if (typeof data.Item != 'undefined') {
                // An item was found
                // Check that the user who sent the request is the owner of the item
                if (req.username == data.Item.createdBy) {
                    // CODE TO GENERATE CHECKLIST HERE
                    try {
                        const checklist = {
                            checklist: data.Item
                        }
                        // render html
                        const compiledFunction = pug.compileFile('views/checklist.pug');
                        const htmlContent = compiledFunction(checklist);

                        // convert html to pdf and save file
                        const browser = await puppeteer.launch();
                        const page = await browser.newPage();
                        await page.setContent(htmlContent, { waitUntil: 'networkidle2' });
                        await page.pdf({ path: `pdfs/${req.username}.pdf`, printBackground: true });
                        await browser.close();

                        res.status(200);
                        res.json({
                            message: 'success'
                        });
                    }
                    catch (err) {
                        console.error(err);
                    }
                }
                else {
                    // User does not own the item
                    console.error('You are not authorized to access that item');
                    res.status(403);
                    res.json({
                        error: 'You are not authorized to access that item'
                    });
                }
            }
            else {
                // No item was found
                console.error(`A checklist with id ${req.params.id} could not be found`);
                res.status(404);
                res.json({
                    error: `A checklist with id ${req.params.id} could not be found`
                });
            }
        }
    });
});

module.exports = router;

CodePudding user response:

So the problem ended up lying with puppeteer in the end like @OldGeezer said. Since I was using the page.setContent() function from puppeteer to load html content directly into the browser instead of giving it a path to an html file, neither of the relative paths to my image or css worked. How I ended up solving it was by using the page.goto(path) function in puppeteer to load an empty html file I created in the public folder and then using the page.setContent() function to load the html I wanted. This allowed the browser to be in the right directory to load the external files via their relative paths.

server.js
views
  checklist.pug
routes
  checklist.js
public
  empty.html
  images
    logo.png
  stylesheets
    checklist.css
pdfs

checklist.js

const express = require('express');
const aws = require('aws-sdk');
const { v4 } = require('uuid');
const puppeteer = require('puppeteer');
const pug = require('pug');

const dynamodb_config = require('../config/dynamodb/dynamo_config');
aws.config.update(dynamodb_config);
const { validateChecklist } = require('../middleware/validation');
const authenticateToken = require('../middleware/authenticateToken');

const router = express.Router();
const docClient = new aws.DynamoDB.DocumentClient();

const TABLE_NAME = 'Checklists';

// Generate html from checklist object, convert to pdf and send in response
router.get('/:id/pdf', authenticateToken, (req, res) => {
    // Get the checklist and make sure the user owns it
    const params = {
        TableName: TABLE_NAME,
        Key: {
            id: req.params.id
        }
    }
    docClient.get(params, async (err, data) => {
        if (err) {
            console.error(err);
            res.status(500);
            res.json({
                error: err
            });
        }
        else {
            // Check that an item was found
            if (typeof data.Item != 'undefined') {
                // An item was found
                // Check that the user who sent the request is the owner of the item
                if (req.username == data.Item.createdBy) {
                    // CODE TO GENERATE CHECKLIST HERE
                    try {
                        const checklist = {
                            checklist: data.Item
                        }
                        // render html
                        const compiledFunction = pug.compileFile('views/checklist.pug');
                        const htmlContent = compiledFunction(checklist);

                        // convert html to pdf and save file
                        const browser = await puppeteer.launch();
                        const page = await browser.newPage();

                        // go to empty html file
                        await page.goto('file://'   __dirname   '/../public/empty.html');

                        await page.setContent(htmlContent, { waitUntil: 'networkidle2' });
                        await page.pdf({ path: `pdfs/${req.username}.pdf`, printBackground: true });
                        await browser.close();

                        res.status(200);
                        res.json({
                            message: 'success'
                        });
                    }
                    catch (err) {
                        console.error(err);
                    }
                }
                else {
                    // User does not own the item
                    console.error('You are not authorized to access that item');
                    res.status(403);
                    res.json({
                        error: 'You are not authorized to access that item'
                    });
                }
            }
            else {
                // No item was found
                console.error(`A checklist with id ${req.params.id} could not be found`);
                res.status(404);
                res.json({
                    error: `A checklist with id ${req.params.id} could not be found`
                });
            }
        }
    });
});

module.exports = router;

CodePudding user response:

According to here, it seems like the page rendered this way has no current folder. So one way to include images is to use a data url. I tested the below and it worked.

checklist.pug:

div(class="checklist-title")
  img(src="data:image/png;base64," logo alt="Logo...")

Server Javascript:

// read image file into a base64 string. You should not use sync read.
const logo = fs.readFileSync("./public/images/logo.png", { encoding: "base64"});

// add property `logo` to your compile function data
const htmlContent = pug.compileFile("views/checklist.pug")({logo: logo});

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(htmlContent, { waitUntil: ["domcontentloaded", "networkidle0"]});
await page.pdf( { path: `pdfs/${req.params.id}.pdf`, printBackground: true});
await browser.close();

  • Related