Image rendering in content but not header



  • I'm currently trying out jsreport as a library within an existing application. I've read the advice in https://jsreport.net/learn/phantom-pdf, but I'm still having trouble rendering an image in the header. It renders fine in the body.

    I'm planning to look at the base64 encoded method next to see if that will work for me, but in the mean time, any advice?

    const jsreport = require('jsreport');
    
    router.get('/jsreport', (req, res) => {
    
      const headerImageUrl = 'http://www.planwallpaper.com/static/images/abstract-colourful-cool-wallpapers-55ec7905a6a4f.jpg';
      const invoiceTemplate = `<img src='${headerImageUrl}' style="display: none;"></img>
        <h1> Invoice </h1>
        <div><span>from: {{:from}}</span></div>
        <div><span>to: {{:to}}</span></div>
        <div><span>price: \${{:price}}</span></div>
        <div style='page-break-before: always;'></div>
        <h1>Hello from Page 2</h1>`;
    
    	jsreport.render({
        template: {
          content: invoiceTemplate,
          engine: 'jsrender',
          recipe: 'phantom-pdf',
          phantom: {
            header: `<img src='${headerImageUrl}' width="135" height="25"/> <span>My Great Report</span>`,
            footer: '<span>{#pageNum}/{#numPages}</span>',
          }
        },
        data: {
          from: 'Bob Barker',
          to: 'Amy Adams',
          price: 41.99,
        },
      }).then(function(out) {
        console.log('SUCCESS!');
        out.stream.pipe(res);
      }).catch(function(e) {    
        console.log('error ');
        console.log(e);
        res.end(e.message);
      });
    });
    


  • This is known phantomjs issue. The workaround is to add the same image to the template content but invisible.

    See it described in our docs
    https://jsreport.net/learn/phantom-pdf#images-in-header-and-footer



  • Saw that. And I'm including it in the template content above with display: none, but it still wasn't working. Wondering if you had any insight about additional settings that might cause this.



  • Ah, I didn't see that.

    phantomjs for some reason doesn't like your particular linked image and doesn't print any other info when running in debug. I recommend to re-save your image to png or try different image.
    Here you see it should work with other images
    https://playground.jsreport.net/studio/workspace/SJQA3b1Xb/3



  • Very strange. I can link to your image directly and the report renders fine, but if I download it and put it into public/images folder (a vanilla node-express test app), it doesn't render in the header.

    Are there any end-to-end examples of using the asset option when I'm using jsreport as a library? When I use {#asset images/home.jpg @encoding=link} I'm seeing:

    Error: Asset images/home.jpg not found
        at /Users/mikecullingham/Documents/dev/span/utilities/report-test/node_modules/jsreport-assets/lib/assets.js:148:15
    

  • administrators

    @mike-subtilis are you following all the steps mentioned here (https://jsreport.net/learn/assets#embedding-assets-as-links, https://jsreport.net/learn/assets#external-files-access)? i think that you don't have some asset's options enabled.



  • I think so. Using a basic node-express app, I've added the following endpoint.

    I've tried:

    • fileAsset equals the absolute path as listed below, as link or dataURI, and with the rootUrlForLinks present or missing
    • fileAsset equals the '/images/home.jpg' with the rootUrlForLinks, and I can access that image via "http://localhost:3000/images/home.jpg"

    I'm not sure if there's additional work I need to do to preload the assets. But in all cases I get Error during rendering report: Asset ~/Documents/dev/span/utilities/report-test/public/images/home.jpg not found.

    I'm probably missing something obvious. I can get it to work in the standalone jsreport server, but not when using jsreport as a library.

    router.get('/', function(req, res, next) {
      const fileAsset = '~/Documents/dev/span/utilities/report-test/public/images/home.jpg';
      const invoiceTemplate = `<h1>BODY</h1><img src='{#asset ${fileAsset} @encoding=link}' style='display: none;'></img>
        <h1> Invoice </h1>`;
    
    	jsreport.render({
        template: {
          content: invoiceTemplate,
          engine: 'jsrender',
          recipe: 'phantom-pdf',
          phantom: {
            header: `<img src='{#asset ${fileAsset} @encoding=link}' width="135" height="25"/> <span>My Great Report</span>`,
            footer: '<span>{#pageNum}/{#numPages}</span>',
          },
          assets: {
            publicAccessEnabled: true,
            searchOnDiskIfNotFoundInStore: true,
            rootUrlForLinks: "http://localhost:3000",
          }
        },
        data: {
        },
      }).then(function(out) {
        console.log('SUCCESS!');
        out.stream.pipe(res);
      }).catch(function(e) {    
        console.log('error ');
        console.log(e);
        res.end(e.message);
      });
    });
    

  • administrators

    you need to also set (as shown in the docs of assets) the publicAccessEnabled and searchOnDiskIfNotFoundInStore options to true.

    have you done that too?


  • administrators

    ah sorry, you are doing that, let me check more closely


  • administrators

    maybe try to pass a real absolute path, you are using ~/Documents/dev/span/utilities/report-test/public/images/home.jpg which has a ~ character, try to normalize that to a full path, maybe using path.resolve function of node's path module.



  • I tried the full regular path (/Users/mikecullingham/Documents/dev/span/utilities/report-test/public/images/home.jpg) trying dataURI & link encoding types with the same result both times.


  • administrators

    asset options must be passed when creating a jsreport instance (not in render method), in your case since you are not creating any instance you don't have the chance to pass those options, so you will need to create a jsreport instance.

    example working code:

    var express = require('express');
    // creating jsreport with custom asset options
    var jsreport = require('jsreport')({
      assets: {
        allowedFiles: '**/*.*', // this is important because here you pass what types files (or even inside directories) do you allow, in this case i'm allowing everything
        publicAccessEnabled: true,
        searchOnDiskIfNotFoundInStore: true,
        rootUrlForLinks: "http://localhost:5488"
      }
    });
    var app = express();
    var server;
    
    app.get('/render', function(req, res) {
      const fileAsset =
        '/Users/mikecullingham/Documents/dev/span/utilities/report-test/public/images/home.jpg';  
      const invoiceTemplate = `<h1>BODY</h1><img src='{#asset ${fileAsset} @encoding=dataURI}' style='display: none;'></img>
        <h1> Invoice </h1>`;
    
      jsreport
        .render({
          template: {
            content: invoiceTemplate,
            engine: 'jsrender',
            recipe: 'phantom-pdf',
            phantom: {
              header: `<img src='{#asset ${fileAsset} @encoding=dataURI}' width="135" height="25"/> <span>My Great Report</span>`,
              footer: '<span>{#pageNum}/{#numPages}</span>'
            }
          },
          data: {}
        })
        .then(function(out) {
          console.log('SUCCESS!');
          out.stream.pipe(res);
        })
        .catch(function(e) {
          console.log('error ');
          console.log(e);
          res.end(e.message);
        });
    });
    
    server = app.listen(9500, () => {
      console.log('SERVER STARTED');
    
      jsreport
        .init()
        .then(() => console.log('JSREPORT STARTED'))
        .catch(err => console.error('ERROR WHILE STARTING JSREPORT', err));
    });
    


  • Thanks for all the quick communication btw. I'll take a look at that. I'm also looking at doing a more SOA approach where we use the jsreport server and call out from out own app, which has its own advantages.


  • administrators

    I'm also looking at doing a more SOA approach where we use the jsreport server and call out from out own app, which has its own advantages.

    exactly, in my opinion that is the best way to communicate with jsreport, it will also let you manage server resources separately, which it's better


  • administrators

    and just for the record, the reason why it works as standalone server and not when using it as a library is because in standalone server you can put assets options in dev.config.json/prod.config.json, but when using jsreport as a library, you either create a new instance of jsreport var jseport = require('jsreport')(/* custom options here */); jsreport.render() (with the possibility to pass custom options, like the assets options) or use a default one var jsreport = require('jsreport'); jsreport.render() which has some defaults (which you can not override).

    so since you were using the default instance you were not able to override assets options, fixable by creating an instance.



  • just for the record there is method to change options for render shortcut as well. It just got somehow out of the documentation.

    var jsreport = require('jsreport')
    
    jsreport.renderDefaults.assets = {....}
    
    jsreport.render(...)
    

Log in to reply
 

Looks like your connection to jsreport forum was lost, please wait while we try to reconnect.