简体   繁体   中英

How do I generate and download a PDF from markdown using Meteor

I have a Meteor collection of documents whose content is styled using markdown (for an example of how to do this please see: How does one actually use Markdown with Meteor )

My package stack is as follows:

npm

Atmosphere

When viewing a document I would like to present my users with a Download as PDF button. Clicking on this should convert the document's markdown to a PDF file and download it via the browser.

The conversion process is constrained; it should run on the server but is not allowed to save any temporary files to the server.

How can I achieve this?

Using iron-router , create a server-side route to serve up the PDF.

Router.route '/api/pdf/:_id',
    name : 'generatePDF'
    where: 'server'
    action: ->
        document= Documents.findOne @params._id
        sanitize = Meteor.npmRequire 'sanitize-filename'
        filename = sanitize(document.title).replace(/\s+/g, '-')
        @response.writeHead 200,
            'Content-Type': 'application/pdf'
            'Content-Disposition': "attachment; filename=#{filename}.pdf"

        markdown = document.content
        Async.runSync (done)->
            Meteor.npmRequire('markdown-pdf')
            .from.string(markdown)
            .to.buffer (err, buffer)->
                done null, new BufferStream buffer
        .result.pipe @response

The route above receives an _id as a route parameter. This is used to retrieve the associated document from the Documents collection. The PDF filename is then generated by sanitizing the document.title replacing all spaces with hyphens.

The response headers are now set to force the browser to download the PDF as a file with the sanitized filename .

The PDF is generated from the document.content markdown using the markdown-pdf package. This process is complicated by two issues:

  1. The call to generate the PDF is intrinsically asynchronous and therefore requires a callback. This needs to be converted to a synchronous call by wrapping it in Meteor's Async.runSynch method. This returns an object with a result property we can use.

  2. The markdown-pdf package has a to.buffer method that returns a buffer containing the generated PDF. This allows us to keep everything in code and removes any need to save a temporary file to the server. To pipe this buffer into the response we need to convert it to a stream. I use a helper BufferStream object to do this for me (see below)

With this route in place I just need to place a 'Download as PDF' button somewhere on my display template (the code below is a Jade link styled as a button by Bootstrap 3 classes)

a.btn.btn-primary(href='{{pathFor "generatePDF"}}' target='_blank') Download as PDF 

And finally, here is that BufferStream helper class:

stream = Meteor.npmRequire "stream" 

class @BufferStream extends stream.Readable 

    constructor: (@source, @offset = 0) ->
        throw new Meteor.Error 'InvalidBuffer', 'BufferStream source must be a buffer.' unless Buffer.isBuffer(@source)
        super
        @length = @source.length

    _read: (size) ->
        if @offset < @length
            @push @source.slice @offset, @offset + size
            @offset += size

        @push null if @offset >= @length

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM