jsreport / jsreport-core

The minimalist jsreport rendering core
GNU Lesser General Public License v3.0
86 stars 24 forks source link

Example to render html with external IMG, CSS with only jsreport-core ? #15

Closed rebrec closed 7 years ago

rebrec commented 7 years ago

Hello,

I try to generate a PDF from an html file linked to an external CSS (stored with a relative path) and a few images (referenced with a relative path too).

I don't want to use the whole JSReport web modules etc just because it's overkill...

Here is my JS code :

const fs = require('fs');
const path = require('path');
let templateDir = path.join(__dirname, '..', 'pdfTemplate', 'xcover3');

options = {
    tasks: {strategy: 'in-process'},
    assets: {
        // wildcard pattern for accessible linked or external files
        allowedFiles: "static/**.css",
        // enables access to files not stored as linked assets in jsreport store
        searchOnDiskIfNotFoundInStore: true,
        // root url used when embedding assets as links {#asset foo.js @encoding=link}
        rootUrlForLinks: templateDir,
        // make all assets accessible to anonymous requests
        publicAccessEnabled: true
    },
    phantom: {
        allowLocalFilesAccess: true
    },
    logger: {
        console: {transport: "console", "level": "debug"}
    },
    autoTempCleanup: false
}
const jsreport = require('jsreport-core')(options);
let htmlfile = path.join(templateDir, 'template.html');
console.log('**********', templateDir);
readFile(htmlfile)
    .then(fileContent => {
        jsreport.init()
            .then(function (reporter) {
                return jsreport.render({
                    template: {
                        content: fileContent,
                        engine: 'jsrender',
                        // recipe: 'html'
                        recipe: 'phantom-pdf'
                    },
                    data: {
                        foo: "world"
                    }
                }).then(function (out) {
                    // out.result.pipe(fs.createWriteStream('test.pdf'));
                    console.log(out.content);
                });
            }).catch(function (e) {
            console.log(e)
        })
    });

function readFile(file) {
    return new Promise(function (resolve, reject) {
        let fileContent = '';
        fs.readFile(file, 'utf8', function (err, contents) {
            return resolve(contents);
        })
    });
}

When watching the produced log files, i see a bunch or Warn messages telling :

warn: Unable to load resource (#2URL:file:///static/css/bootstrap.min.css) timestamp=Mon Jun 12 2017 09:05:42 GMT+0200 (Paris, Madrid (heure d’été)), requestId=1 warn: Error code: 203. Description: Error opening /static/css/bootstrap.min.css: Le chemin d’accès spécifié est introuvable. timestamp=Mon Jun 12 2017 09:05:42 GMT+0200 (Paris, Madrid (heure d’été)), requestId=1 warn: Unable to load resource (#4URL:file:///img/BLAH2017-06-01 11_31_52-SM-G389F.png) timestamp=Mon Jun 12 2017 09:05:42 GMT+0200 (Paris, Madrid (heure d’été)), requestId=1 warn: Error code: 203. Description: Error opening /img/BLAH2017-06-01 11_31_52-SM-G389F.png: Le chemin d’accès spécifié est introuvable. timestamp=Mon Jun 12 2017 09:05:42 GMT+0200 (Paris, Madrid (heure d’été)), requestId=1 warn: Unable to load resource (#5URL:file:///img/2017-06-01 11_32_15-SM-G389F.png) timestamp=Mon Jun 12 2017 09:05:42 GMT+0200 (Paris, Madrid (heure d’été)), requestId=1

From the log messages, it seems that each of my relative path are converted to absolute path, while in my template.html source i reference images this way <img class="screenshot" src="img/BLAH2017-06-01 11_31_52-SM-G389F.png" />.

I have searched quite a lot and do not find any example on Internet showing such use case (most examples seems to be based on using the Web front end (designer).

Thanks for anyhelp you will be able to provide

Regards

bjrmatos commented 7 years ago

From the log messages, it seems that each of my relative path are converted to absolute path, while in my template.html source i reference images this way .

this is the same behaviour that you get from browsers too, relative paths are always converted to absolute paths relative to the URL of the page that is being served.

seems like you just need to reference your images using an absolute path to a local file, file:///path/to/img/file.png, you can even put the root part of these URL in a variable from data and just use it in img src, then when you need to change the root part of the URL you just need to change it in one place

rebrec commented 7 years ago

Thanks for the answer,

When i open the html file (with relative path) within my browser, it works without modifications, can't that be the same with jsrepport-core ? Also, would it be easier if i was serving the htlm + external files through a local webserver ?

My goal is to be able to designe the HTML template locally, test it without the need of an HTTP server. When the template is done, it's not a problem to serve it through an HTTP server

bjrmatos commented 7 years ago

When i open the html file (with relative path) within my browser, it works without modifications, can't that be the same with jsrepport-core ?

we are not doing something special about URL resolution, i guess that the behavior of phantom must be to resolve all relatives URLs in that way. just for curiosity, can you share the html part of your template? i would like to run a test and compare relative URL resolution in different browsers/engines.

Also, would it be easier if i was serving the htlm + external files through a local webserver ?

yes, it would be easier, just install jsreport-express in your jsreport-core installation as reference your assets with the link encoding of jsreport-assets.

pofider commented 7 years ago

One solution is also to use the html base tag. Then all the phantomjs resource requests will go relative to it. You can add it to the html head and fill it with your working directory.

rebrec commented 7 years ago

@bjrmatos here is the HTML template (you will notice there is currently no fields to feed the templating engine)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>template</title>
    <link rel="stylesheet" href="../static/css/bootstrap.min.css">
    <link rel="stylesheet" href="css/main.css">
</head>

<body>
<div>
    <header>
        <h1>Reconfiguration de la messagerie sur son smartphone</h1>
    </header>
    <div class="content">
        <div class="row">
            <div class="col-md-12">
                Some Text
            </div>
        </div>
        <div class="row">
            <div class="col-md-12">
                <h2>Procédure</h2>
                <div>
                    <img class="screenshot" src="img/BLAH2017-06-01 11_31_52-SM-G389F.png"/>
                </div>
                <div class="comment">
                    Commencer par lancer l'application <b>Email</b>
                </div>
            </div>
            <!--
                Other Divs with other images
                -->
            </div>
        </div>

    </div>

    <footer>
        Service Informatique SDIS72
    </footer>
</div>
</body>
</html>
rebrec commented 7 years ago

@pofider thanks for the comment, i read about it but i didn't tried it : i don't find this very neat to have to hardcode some path that can evolve in time. Moreover, i don't like much the idea to prevent user from testing locally. What i would ideally want is :

But thanks for the solution anyway

pofider commented 7 years ago

create your template locally :

each template is within a folder, the template name is names template.html you can add img + custom CSS within your folder any template specific information will be referenced relativelly if you want to use some common libs like jquery or bootstraps, reference to them in the folder ../static/{css|js|img} test your template by just opening it in your browser from the file:// url

upload the template folder structure and you are done ...

Yes, this is great and reasonable. Hopefully we will converge to such storing schema one day. This is something we will definitely look into.

The main issue currently is that we need to support some kind of API on the top of the stored templates or other stored entities. The change you propose looks great for the apps using just file based store, but would not be that simple for apps using jsreportonline or other jsreport instances using mongo or sql based stores. We need to take into consideration all of this use cases. Mainly we need to keep it consistent so user can for example export templates from the local files into the external mongo driven instance.

I've created and issue which better reflects this requirement. https://github.com/jsreport/jsreport-core/issues/17

I'm closing this now as the mentioned new issue better reflects the issue.

I would also like to note that there is new jsreport extension available. It allows to relatively reference the images and assets to the working directory without hard coding paths. It is not 100% solution for your problem, but can help at least partially The link to docs https://jsreport.net/learn/base It will be part of jsreport@1.8 or it can be installed separately already using npm install jsreport-base