PDFkit js how to save document to file (Win 8 app)

3.6k Views Asked by At

I am working on a Windows 8 Javascript application that generates a PDF using PDFKit.

Normally you'd make a blobstream using pipe and then make an URL of it that you send to the browser. This only works if the website is run from a server, which is not the case since it's a local app. However I do have access to the file system and I'd like to save the generated PDF to a file.

To do this I was inspired by this question: How to pipe a stream using pdfkit with node js

I tried both above's solutions using Browserify, replacing the printer code with code that saves to a file:

Windows.Storage.ApplicationData.current.localFolder.createFileAsync("test.pdf",
    Windows.Storage.CreationCollisionOption.replaceExisting).then(function (file) {
        //write as text 
        Windows.Storage.FileIO.writeTextAsync(file, data).then(function () {});
        //or write as buffer
        var buffUTF8 = Windows.Security.Cryptography.CryptographicBuffer.convertStringToBinary(data, 0);
        Windows.Storage.FileIO.writeBufferAsync(file, buffUTF8).then(function () {});
    });

As you can see I both tried using writing as text or writing as a buffer, both gave me exactly the same files.

The PDF file is created but when I open it it shows a blank page. If I open the pdf in a text editor and compare it to a valid PDF I can tell that the stream part of the file has different kind of characters.

Also the Visual Studio output window is spamming me with Javascript run-time error messages about the URI having invalid encoding.

So it's obviously an encoding issue and I'm not sure what to do next. Any tips on how to save a PDFkit document to a file?

3

There are 3 best solutions below

0
On

This is working for me in firefox

_downloadAsPdf: (data) =>

doc = new PDFDocument()
imgData = @renderer.canvas.toDataURL('image/png')
stream = doc.pipe(blobStream())
doc.image(imgData)
doc.end()

stream.on 'finish', ->

  blob = stream.toBlob('application/pdf')

  # save as file
  date = new Date()
  time = date.toLocaleTimeString()
  pageNum = data.pageNumber
  fileName = "page" + pageNum + "_" + time + ".pdf"
  saveAs(blob, fileName)
0
On

A cross-browser solution that works for me: set the data-URL you generate from the PDF as the source for a temporary link element:

function data_url_to_download(data_url, filename) {
  var a = document.createElement("a");
  document.body.appendChild(a);
  a.style = "display: none;";
  a.href = data_url;
  a.download = filename;
  a.click();
  a.remove();
};

The file can then be downloaded automatically when the PDF generation is complete:

// Generate document to a stream with PDFKit, then...
stream.on("finish", function() {
    data_url_to_download(stream.toBlobURL("application/pdf"), "output.pdf");
});
1
On

I took a different approach and found out that base64 is the way to go. This way I didn't get it just to work in Windows but on other platforms like OSX as well.

The key was to use blob-stream.js and a FileReader object to convert a Blob object to a data url (which is basically base64).

<script src="/pdfkit.js"></script>
<script src="/blob-stream.js"></script>

For Windows you then convert the base64 to an IBuffer object and save that to file. Below shows an example, the part after var base64 is specific for Windows but it works similar on other platforms:

var doc = new PDFDocument({ size: 'A4', layout: 'portrait' });
stream = doc.pipe(blobStream());

stream.on('finish', function () {
    fileReader = new FileReader();

    fileReader.onload = function () {
        //get base64 part of the data url
        var base64 = fileReader.result.substr(fileReader.result.indexOf(',') + 1);
        //create file, create IBuffer, save and launch pdf reader
        Windows.Storage.ApplicationData.current.temporaryFolder.createFileAsync("test.pdf", Windows.Storage.CreationCollisionOption.generateUniqueName).then(function (file) {
            var buffUTF8 = Windows.Security.Cryptography.CryptographicBuffer.decodeFromBase64String(base64);
            Windows.Storage.FileIO.writeBufferAsync(file, buffUTF8).then(function () {
                Windows.System.Launcher.launchFileAsync(file);
            });
        });
    };
    fileReader.onerror = function () {
        console.log('error loading file');
    };

    fileReader.readAsDataURL(stream.toBlob('application/pdf'));
});

//draw PDF content here
doc.text('Hello World!', 100, 100);
doc.end();