PDFKit - Custom Fonts - fs.readFileSync is not a function
Asked Answered
O

6

10

I'm using PDFKit for an application. I'm just using it in the Browser in an HTML file, with Javascript (no Node.js).

I downloaded PDFKit from GitHub: https://github.com/devongovett/pdfkit/releases

as well as Blob Stream: https://github.com/devongovett/blob-stream

I'm trying to include a custom font per the documentation like so:

doc.registerFont('Custom Font', 'fonts/GOODDP__.TTF');
doc.font('Custom Font').fontSize(fontSize).text($("#text1").val(), xPos, yPos, configObj);

But I always get this error:

 fs.readFileSync is not a function

This makes sense because fs.readFileSync is part of node.js, and I'm not using that. However, the example in the docs say this can be used in the browser.

I know there's also a Browserify option, but I'm not sure how or if that would help in this situation

Ovoviviparous answered 15/10, 2014 at 21:50 Comment(1)
browser will prevent any client file access, you should ask user to upload the file to your server first.Pyrrhotite
O
3

It seems you must use Browserify for this functionality and that using a pre-compiled PDFKit.js won't cut it for any of the Node.js-specific functionality.

Ovoviviparous answered 20/10, 2014 at 20:24 Comment(0)
S
8

You have to use an ArrayBuffer:

        var oReq = new XMLHttpRequest();
        oReq.open("GET", "css/fonts/Open_Sans/OpenSans-Regular.ttf", true);
        oReq.responseType = "arraybuffer";

        oReq.onload = function(oEvent) {
            var arrayBuffer = oReq.response; // Note: not oReq.responseText

            if (arrayBuffer) {
                PdfExporter.doc.registerFont('OpenSans', arrayBuffer)
            }
        };

        oReq.send(null);
Spicy answered 27/1, 2015 at 10:55 Comment(1)
Using this, I still get the same error once calling font() with the registered font name: Uncaught TypeError: fs.readFileSync is not a functionZymo
F
5

This works for me:

async function makePDF() {
   const font = await fetch('./fonts/arial.ttf')
   const arrayBuffer = await font.arrayBuffer()

   doc.registerFont('Arial', arrayBuffer)

   doc.font('Arial').text('Hello World!')
}
Flame answered 10/2, 2020 at 15:14 Comment(1)
I used it for custom font (Bangla, UTF-8) it working, but problem is now English now is not rendering correctly.Marvel
O
3

It seems you must use Browserify for this functionality and that using a pre-compiled PDFKit.js won't cut it for any of the Node.js-specific functionality.

Ovoviviparous answered 20/10, 2014 at 20:24 Comment(0)
T
3

Here's the steps I followed to load a custom unicode font.

  1. Create package.json using the default values:

npm init -y

  1. Install fontkit

npm install fontkit

  1. Install browserify globally (in case you do not have browserify)

npm install -g browserify

  1. Create an empty file and name it compile.js (or whatever you like)

  2. Paste the following code inside compile.js

    fontkit = require("fontkit");
    const fontName = "OpenSans-Regular";
    const fontPath = fontName + ".ttf";
    
    fetch(fontPath)
        .then(res => res.arrayBuffer())
        .then(fontBlob => {
            const customFont = fontkit.create(new Buffer(fontBlob)).stream.buffer;
            const doc = new PDFDocument({});
            const stream = doc.pipe(blobStream());
            doc.registerFont(fontName, customFont);
            doc.fontSize(14);
            doc.font(fontName)
            .fillColor("black")
                .text(
                    "Custom unicode font loaded. Ω is the 24th and last letter of the Greek alphabet.",
                    50,
                    50,
                    { width: 800, align: "left" }
                );
            doc.end();
            stream.on("finish", function() {
                const blob = stream.toBlob("application/pdf");
                const iframe = document.querySelector("iframe");
                iframe.src = stream.toBlobURL("application/pdf");
            });
        });
    

    To load a diffrent font simply change the fontName value. The above code assumes that a font file named OpenSans-Regular.ttf is present in the same directory as compile.js. You can change the fontPath to whatever path or URL you like.

  3. Run the following (in your Terminal or command prompt) to create a custom version of fontkit.

browserify compile.js -o fontkit.js

  1. You can delete compile.js file. It's no longer needed. You can also delete node_modules directory as well as package.json. (If you are not using other packages and you are only doing this for creating a custom version of fontkit)

  2. Create an index.html file and paste the following:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <title>Custom Font</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    
    <body>
        <iframe id="frame1" width="100%" height="500px"></iframe>
    </body>
    
    <script src="pdfkit.js"></script>
    <script src="blob-stream.js"></script>
    <script src="fontkit.js"></script>
    
    </html>
    
  3. Here's a screenshot of index.html in the browser:

enter image description here

Taub answered 25/6, 2018 at 14:59 Comment(1)
Fetch API cannot load '___ttf.' URL scheme must be "http" or "https" for CORS request. error occurs.Plaintiff
C
2

I also ran in to this problem and Andrea's answer is only part of the solution. You actually need to tweak the pdfkit.js file. But first you need to do what Andrea did:

var myImage;
var oReq = new XMLHttpRequest();
oReq.open("GET", "myimage.jpg", true);
oReq.responseType = "arraybuffer";
oReq.onload = function(oEvent) 
{
    myImage = oReq.response; //image as an arraybuffer
    makePDF();
};
oReq.send(null)

//then use myImage like normal:
doc.image(myImage);

As I said you need to tweak the pdfkit.js file. At about line 2888:

PDFImage.open = function(src, label) {
    var data, match;
    if (Buffer.isBuffer(src)) {
        data = src;
    } else {
        //START NEW
        if (src instanceof ArrayBuffer) {
            data = new Buffer(new Uint8Array(src), 'object');
        } else
        //END NEW
        if (match = /^data:.+;base64,(.*)$/.exec(src)) {
            data = new Buffer(match[1], 'base64');
        } else {
            data = fs.readFileSync(src);
            if (!data) {
                return;
            }
        }
    }

Make sure you also include blob-stream.js. I added an extra option after //START NEW that takes care of array buffers that come from XMLHttpRequests.

I don't know if this is the best solution, but it works.

Clarkin answered 27/8, 2015 at 12:37 Comment(1)
As of 2021, you can just pass an array buffer to the drawImage functionWinwaloe
B
0

I'm using a custom typeface for PDFKit in Node:

const FileSystem = require('fs')

const path = require('path')

const fontDBCartoonShout = FileSystem.readFileSync(path.resolve(__dirname, '../resources/typefaces/BD_Cartoon_Shout.ttf'))
pdfDoc.registerFont('BD Cartoon Shout', fontDBCartoonShout)
Bloomery answered 28/1, 2021 at 14:34 Comment(1)
Yes but the author asked explictly in the browser, without a node.js env. Otherwise he/she would not have run into this problem.Quibbling

© 2022 - 2024 — McMap. All rights reserved.