Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NodeJS version does not work out of the box #1312

Open
mLuby opened this issue Mar 14, 2018 · 37 comments
Open

NodeJS version does not work out of the box #1312

mLuby opened this issue Mar 14, 2018 · 37 comments

Comments

@mLuby
Copy link

mLuby commented Mar 14, 2018

Clearly Node ("server-side") setup is a stumbling block, especially around fonts.
#916
#1044 (comment)
#1062

I'd say libraries should just work out of the box. Specifically, this should work:

var PdfPrinter = require('pdfmake')
var fs = require('fs')

const doc = new PdfPrinter().createPdfKitDocument({content: 'my text'})
doc.pipe(fs.createWriteStream('myFile.pdf'))
doc.end()

Unfortunately, this simple example errors out with Error: Font 'Roboto' in style 'normal' is not defined in the font section of the document definition because PdfMake requires a font definition object. I think that's something that should be provided by default, and can be optionally overwritten by people who want custom fonts. Doing so would make using this library much simpler to use with Node. This is the minimal example that works:

var PdfPrinter = require('pdfmake');
var fs = require('fs');

const doc = new PdfPrinter({
	Roboto: {normal: new Buffer(require('pdfmake/build/vfs_fonts.js').pdfMake.vfs['Roboto-Regular.ttf'], 'base64')}
}).createPdfKitDocument({content: 'my text'})
doc.pipe(fs.createWriteStream('myFile.pdf'))
doc.end()

Perhaps it should be in the wiki.

@mozfet
Copy link

mozfet commented Apr 13, 2018

Its my first day with pdfmake trying to get it to work server side on Meteor; its been a long day, and this post was quite helpful. Using the minimal example proved here I was able to get the pdf generated and downloaded in the client, but now I get a pdf document (with my metadata) with adobe complaining "cannot extract the embedded font 'UIMONA+Roboto-Regular'. I'm stumped...

@mozfet
Copy link

mozfet commented Apr 15, 2018

After some digging, I leaned that PDFKit, which pdfmake is built on, includes support for 14 standard fonts that are built into the pdf standard. Using these fonts avoid embedding any fonts in the document, thus also use less bandwidth. Since I run meteor on production clusters, I stay away from using the file system and use a buffer stream for my example instead.

Thus this makes the minimum node (es6) example I can create:

import PdfPrinter from 'pdfmake';
import streamBuffers from 'stream-buffers';
const doc = new PdfPrinter({timesRoman:{normal: 'Times-Roman'})
    .createPdfKitDocument({defaultStyle:{font: 'timesRoman'}}, {content: 'my text'});
const writableBuffer = new streamBuffers.WritableStreamBuffer();
doc.pipe(writableBuffer);
doc.end();

I do not see any errors on the server console, and on the client Adobe Acrobat does not generate any warnings for docs created this way... but neither shows any text... for me at least...

@mozfet
Copy link

mozfet commented Apr 15, 2018

I concluded perhaps generating pdf server side is not such a good idea because I cannot get it to work, so I looked for a workaround. Now I generate the document definition on the server, and return it to the client to generate the pdf with pdfmake there. It works well so far.

@mozfet
Copy link

mozfet commented Apr 15, 2018

The workaround stopped working well... well because, because when I want a footer with a page number in it, it is defined as footer key in the document definition as a function to dynamically determine the content of the footer. Functions are not very portable and tricky to pass from server to client, and in my case using meteor methods to transfer the document definition does not work, so my reports cannot have a page number.

@mozfet
Copy link

mozfet commented Apr 15, 2018

The workaround for the workaround is to create the document definition, excluding the footer on the server, and to add the footer on the client.

@FrancisCalizo
Copy link

@mozfet Hey Mozfet, do you happen to have code for me to get an idea on how to get a pdf generated server-side? I've been stuck on this for a bit and just need some sort of idea. I am using Node and Express at the moment and am confused as to how to even get a pdf generated with these docs.

@mLuby
Copy link
Author

mLuby commented Apr 18, 2018

@FrancisCalizo does my second code block above not work for you?

@mozfet I remember seeing mention of fonts when tracing this issue through pdfmake's dependencies but I don't remember actually ever finding the font definitions. You think one or more deps actually come packaged with font files?

@officer-rosmarino
Copy link

@mLuby for me it works, but I don't need to create a file. I need to create a Buffer in order to be able to send the generated pdf as an email attachment. Do you have any idea on how to do it?

@FrancisCalizo
Copy link

@mLuby thanks for the reply. I am sort somewhat unfamiliar with some backend aspects, I've been studying JS for about 3-4 months.

I created a file using Express Generator. Would that second block of code go into a file in the routes folder? I used the second block of code in the index.js file in the routes folder.

Sorry for the newbie question, still trying to get a hang of all this.

@officer-rosmarino
Copy link

@FrancisCalizo it doesn't matter where you put it. You could put it in a route handler in an Express application, in a standalone script or whatever else.

For instance, if you want to generate and save a PDF file when you call a certain API, you could do something like this:

[create express server]
var PdfPrinter = require('pdfmake');
var fs = require('fs');

app.get('/generatePDF', (req, res)=>{
  const doc = new PdfMake().createPdfKitDocument({content: 'my text'})
  doc.pipe(fs.createWriteStream('myFile.pdf'))
  doc.end()
})

@mozfet
Copy link

mozfet commented Apr 19, 2018

@mLuby I never got it working server side... I generate the document definition server side and the pdf client side...

@mozfet
Copy link

mozfet commented Apr 19, 2018

@honestserpent I would not expect that code to work server side because PdfMake is never imported, PdfPrinter is. I could not find a PdfMake function available for import server side (I logged the imported object to the console to check), and got an error when trying to use pdf printer without a fonts object.

As I understand it PdfMake is for use client side, PdfPrinter is used server side.

@mozfet
Copy link

mozfet commented Apr 19, 2018

@mLuby I think that you will not find these standard fonts in the pdfmake dependency chain because they are embedded in the pdf viewer binaries already installed as client-side software, they are thus built in and need not be embedded in pdf document and need not be known to pdfmake, are thus only referred to by name.

@officer-rosmarino
Copy link

officer-rosmarino commented Apr 19, 2018

@mozfet it doesn't work because I messed it up and didn't pass the font argument to the constructor. Here is a working gist

@officer-rosmarino
Copy link

@mozfet @mLuby I also made a first attempt to create the express version. Here is the gist.

@mozfet
Copy link

mozfet commented Apr 19, 2018

@honestserpent I got that far as well, which is basically your opening post. Then the pdf downloads on the client side, it complains about missing font. When I used the standard fonts, then there was no error in pdf viewer, but nothing was visible either. The meta information is however clearly readable inside adobe as file properties, and thus I do not expect it to be a transmission encoding issue...

@officer-rosmarino
Copy link

@mozfet the one I did is purely server side, and it does not give me any problems with the generated PDFs. Gotta say that the generated pdf is VERY simple (just a line of text). But hey, it works! :D

@mozfet
Copy link

mozfet commented Apr 19, 2018

@honestserpent I have created a reply gist of doing the same on Meteor. To me it looks like I am doing the same as you...

@officer-rosmarino
Copy link

And this doesn't work for you?

@mozfet
Copy link

mozfet commented Apr 19, 2018

Nooo...

When I download using Chrome and open the generated pdf on OSX using Adobe Acrobat I get an error.
screen shot 2018-04-19 at 17 21 59

@officer-rosmarino
Copy link

Well, I don't know specifically. Have you tried my example? Try to implement it in meteor and let me know. I can try meteor but not now

@mozfet
Copy link

mozfet commented Apr 19, 2018

@honestserpent It is all based on your example, which is the same as mLuby's, just refactored, and I tested the meteor gist this afternoon, resulted in the snapshot above.

Anyhoo I do not need it now anymore, it is working ok with the workaround, and I'm starting to think it may even be a good thing doing it this way, less server processing means faster responses and it is quite fast enough on the client. Perhaps in a few weeks I give it another go.

@officer-rosmarino
Copy link

I am wondering if this is an issue with Mac OS. Do you have a chance to try it on a different OS?

@mozfet
Copy link

mozfet commented Apr 20, 2018

I do not have another OS instance read ATM... here is the file; can you open it and see any text?
report.pdf

@officer-rosmarino
Copy link

No, I can open it but I don't see any text. But it makes sense. Would be interesting to test your code on a different platform. I'm gonna test it on mine.

@dvruette
Copy link

Thanks for the hint! This was the final piece in the puzzle of generating a PDF on the server. It was quite confusing that I had to load the fonts on my own when they were just specified as a path in every example as well as in the documentation.

@mLuby
Copy link
Author

mLuby commented Aug 3, 2018

@bpampuch would you welcome a PR to fix this? I believe it's a non-breaking API change.

@liborm85
Copy link
Collaborator

liborm85 commented Aug 4, 2018

@mLuby, I do not see any bug to fix, because:

  • Wiki page Building font file via shell script describe building custom fonts for client-side. Not for server-side.
  • In server-side do not use virtual file system in vfs_fonts.js, use real font files.
  • How to use pdfmake on server-side and how to set fonts see: examples/basics.js
  • Fonts to use on server side is available in examples/fonts (any other font files may be used)

@mLuby
Copy link
Author

mLuby commented Aug 4, 2018

It's not a bug exactly, just a frustrating user experience (and others agree 👍).

  • The wiki says "pdfMake uses the Roboto font by default" which is incorrect; pdfMake uses no fonts by default (server-side at least). This makes it confusing when the user gets an error saying pdfMake can't find Roboto.
  • it's highly irregular for the user to have to do a special build step to get an npm-installed library to work, especially a step using bash.
  • The fact that it's not obvious you have to do this build step from the README adds to the confusion.
  • examples/basics.js doesn't work because examples/fonts are stripped away by .npmignore

My goal here is to save others the time and frustration I went through to get this library working. Once it works, pdfMake is a super helpful piece of software, so thank you! 😄

It's your work, so I'll close this issue if you still disagree with me that the UX could be improved.

@muffeeee
Copy link

This module is a nightmare for server-side. You should really just remove it from npm so people don't have to waste their time on this until you can provide proper (and functional) documentation. It doesn't even seem like you really understand your own project..

Even after two hours of digging in GitHub issues and a couple stackoverflow questions, the library still wouldn't render a PDF document with my specified font. Tried doing it the way the docs said (if you can even call that docs, half the functionality of this project aren't even mentioned), tried converting the fonts to base64 using the vfs_fonts.js method (as described in the comments here).

I'll just find something else to generate my PDFs, I guess.

@dvruette
Copy link

You’re right, the features of this module are great, but getting it to work server side is a hassle and the documentation doesn’t help whatsoever, which is a shame..

@mLuby
Copy link
Author

mLuby commented Sep 26, 2018

@muffeeee It's still a pretty darn useful piece of software, but I 💯agree with you that the documentation and dev experience could be significantly improved for server-side users.

Makes me wonder whether there's support for a server-focused fork? 👍👎

@kaushikpathak
Copy link

This module is a nightmare for server-side. You should really just remove it from npm so people don't have to waste their time on this until you can provide proper (and functional) documentation. It doesn't even seem like you really understand your own project..

Even after two hours of digging in GitHub issues and a couple stackoverflow questions, the library still wouldn't render a PDF document with my specified font. Tried doing it the way the docs said (if you can even call that docs, half the functionality of this project aren't even mentioned), tried converting the fonts to base64 using the vfs_fonts.js method (as described in the comments here).

I'll just find something else to generate my PDFs, I guess.

Hey, @muffeeee
I am using pdfmake for server side and I have used multiple fonts, even I am using MICR for printing cheque. Just a few changes you need to do in code of pdfmake.
I agree pdfmake is not that good but we can modify code as per our use.

@YoannBuzenet
Copy link

Hey ! So could anyone make a small recap on how to make it work serverside ? I'm stuck on the font problem and I still don't get how to proceed.

Thank you :)

@YoannBuzenet
Copy link

YoannBuzenet commented Jul 3, 2020

Got it working. Just to share with you guys What I did :

  • I took the Roboto package from NPM and installed it. ("roboto-font": "0.1.0")
    npm i roboto-font --save
  • Wrote the font loading command (let printer = new pdfMake(fonts)) AFTER the font object declaration

And I finally stopped having this message...

Credits to this SO answer :
https://stackoverflow.com/questions/45196528/how-to-create-a-pdf-on-node-js-using-pdfmake-and-vfs-fonts

So it looks like that ;

let fonts = {
    Roboto: {
        normal: 'node_modules/roboto-font/fonts/Roboto/roboto-regular-webfont.ttf',
        bold: 'node_modules/roboto-font/fonts/Roboto/roboto-bold-webfont.ttf',
        italics: 'node_modules/roboto-font/fonts/Roboto/roboto-italic-webfont.ttf',
        bolditalics: 'node_modules/roboto-font/fonts/Roboto/roboto-bolditalic-webfont.ttf'
    }
};
let printer = new pdfMake(fonts);

EDIT : Here is my whole code that works server side :

var PdfPrinter = require("pdfmake");
var fs = require("fs");

var fonts = {
  Roboto: {
    normal: "node_modules/roboto-font/fonts/Roboto/roboto-regular-webfont.ttf",
    bold: "node_modules/roboto-font/fonts/Roboto/roboto-bold-webfont.ttf",
    italics: "node_modules/roboto-font/fonts/Roboto/roboto-italic-webfont.ttf",
    bolditalics:
      "node_modules/roboto-font/fonts/Roboto/roboto-bolditalic-webfont.ttf",
  },
};

var printer = new PdfPrinter(fonts);

function writePDF() {
  var docDefinition = {
    content: [
      {
        layout: "lightHorizontalLines", // optional
        table: {
          // headers are automatically repeated if the table spans over multiple pages
          // you can declare how many rows should be treated as headers
          headerRows: 1,
          widths: ["*", "auto", 100, "*"],

          body: [
            ["First", "Second", "Third", "The last one"],
            ["Value 1", "Value 2", "Value 3", "Value 4"],
            [{ text: "Bold value", bold: true }, "Val 2", "Val 3", "Val 4"],
          ],
        },
      },
    ],
  };
  var options;
  var pdfDoc = printer.createPdfKitDocument(docDefinition, options);
  pdfDoc.pipe(fs.createWriteStream("document.pdf"));
  pdfDoc.end();
}

module.exports = { writePDF };

@paddotk
Copy link

paddotk commented Aug 7, 2020

This answer seems to work. However I think pdfMake should be doing this by default, I almost gave up trying..

@govnag
Copy link

govnag commented Jan 11, 2021

@mLuby Thank you!! Your buffer example is the only thing that worked for me; including adding the @YoannBuzenet fix for including the roboto font

I updated new Buffer() -> Buffer.from() and it still works

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests