generate-pdf
v0.0.4
Published
Generate PDFs from React + Redux
Downloads
18
Readme
generatePDF()
The generate-pdf
package exports a Promise interface for creating PDF documents from web pages.
In its simplest form:
const configuration = {};
generatePDF(configuration)
.then((pdf) => console.log(pdf))
.catch((e) => console.error(e))
Of course, you wouldn't want to output a PDF file to the console, but you would want to provide some configuration parameters.
To generate a PDF we make use of several other packages. We expose their configuration parameters on the configuration object: see the Packages section.
React + Redux
While the generate-pdf
package implements some interfaces to Hogan.js templates, it's meant to be used with isomorphic React. But it isn't opinionated about how you implement isomorphic React, so no React or Redux packages are included as dependencies.
For stateful components implementing React Router, we use the react-routes-renderer
and redux-routes-renderer
packages. You should visit the repositories for React.Routes.Render and Redux.Routes.Renderer to evaluate them.
For stateless components, we use react-stateless-renderer
. You should visit the repository for React.Stateless.Renderer, too.
Install each separately within your project, as you need them.
We suggest that you use stateless components for rendering the PDF header and footer HTML.
To implement your own rendering mechanism: the ReactDOMServer
class (from the react-dom/server
package) exposes the renderToString()
and renderToStaticMarkup()
methods.
Using JSX they could be implemented as:
ReactDOMServer.renderToString(
<Component
{...props}
/>
)
ReactDOMServer.renderToStaticMarkup(
<Component
{...props}
/>
)
Without JSX:
ReactDOMServer.renderToString(
ReactDOMServer.createFactory(Component, props)
)
ReactDOMServer.renderToStaticMarkup(
ReactDOMServer.createFactory(Component, props)
)
In either case, the props
object is just a literal. As you'll see in the Rendering HTML section, we pass the configuration object to each "thenable" step of the process as we generate a PDF so the props
object could simply be that configuration object, or another object composed from its attributes.
Packages
make-face
While it's possible to use any of a number of font formats supported by Phantom JS within the main layout of the PDF (the fonts only have to be declared within the CSS used by the rendered HTML page), headers and footers render without fonts unless the font data is embedded in the header and footer HTML, too.
Converting font files to CSS files with data embedded as base64
is handled by the make-face
package. You should visit its repository for usage instructions.
We use makeFace
to convert font files to CSS then readFace
to populate those files to an object (which is the return value of that function).
To configure the package, you should define a makeFace
attribute on the parameters object for the method of the same name, and a readFace
attribute for the method of that name, too.
{
makeFace: {
srcPath: '/path/to/src',
cssPath: '/path/to/css'
},
readFace: {
path: '/path/to/css'
}
}
There is no return value from makeFace
, but when readFace
has executed it will replace the readFace
attribute of the configuration object with its return value.
Each key is the (truncated) file path of a CSS file and each value is the CSS file data, as a string. Assuming you have one CSS file is named 'FontName.css' then the configuration object will then look something like:
{
makeFace: {
srcPath: '/path/to/src',
cssPath: '/path/to/css'
},
readFace: {
FontName: '@font-face { /* etc */ }'
}
}
hogan-cache
For rendering HTML layouts, headers and footers without React, we implement Hogan.js handled by the hogan-cache
package.
To configure the package, you should define a hogan
attribute on the configuration object, with its attributes being key and value pairs describing a name and a file path for a Hogan template.
{
hogan: {
header: '/path/to/header.mustache',
footer: '/path/to/footer.mustache',
navigation: '/path/to/partials/navigation.mustache'
}
}
You can name the template anything you like, of course, and you can also define any templates you want to use as partials, too.
When the mechanism has executed it will replace the value of each attribute on the parameters object with the compiled template of that name.
node-html-pdf
Generating the PDF is handled by the node-html-pdf
package. You should visit its repository to see all of the configuration options available.
We don't enforce any of those options, but we do use our own script
if none is populated.
At the very least, you should populate the base
option. Think of it as the 'location' for the Phantom JS browser instance. Images, stylesheets, scripts and other assets will not load without it, and routed React components may not render correctly.
To configure the package, you should define an htmlPdf
attribute on the parameters object:
{
htmlPdf: {
base: 'http://localhost:8080'
}
}
Where http://localhost:8080
is the location of the server from which any assets can be requested.
Rendering HTML
Once the makeFace
, readFace
and hogan
sections have executed, we proceed to the rendering steps. These are defined as methods on the configuration object.
Either
{
render: (parameters) => ({ layout: '<html />', header: '<div />', footer: '<div />' })
}
To clarify, the return value of render
should be an object having at least the layout attribute populated with valid HTML. Otherwise, the node-html-pdf
package will explode.
Or
{
layout: (parameters) => '<html />',
header: (parameters) => '<div />',
footer: (parameters) => '<div />'
}
To clarify, the return values of layout
, header
and footer
should be strings populated with valid HTML. At least the layout method should return valid HTML. Otherwise, the node-html-pdf
package will explode.
Inside these methods, you can do whatever you like to generate that HTML. Each method is passed the parameters object so that you have access to any readFace
data or hogan
templates.
Similarly, you can render React components as described in the React + Redux section. Remember: it's Promises all the way down, so you can return another Promise as long as it eventually returns the appropriate object or string.
For the PDF header and footer we recommend you implement stateless components.
For the PDF layout you might prefer to expose your rendering mechanism with a Promise.
The PDF
The PDF is a Buffer instance. You can pass it into the send
method of an Express response:
const configuration = {}
router.get('/pdf', (req, res) => {
generatePdf(configuration)
.then((pdf) => {
res.attachment('name.pdf')
res.send(pdf)
})
.catch((e) => {
res.send(e)
})
})
The node-html-pdf
package does implement toStream()
and toFile()
methods, but we don't currently support them.
Examples
There are some example implementations included in the package. On the file system, cd
to the generate-pdf
package and the directory named pub
. In there, you'll find client
, server
and public
. Look in client
for the React app, server
for the Express apps, and public
for the HTML
and CSS
.
To launch the application in your browser, install all of the dependencies
and the devDependencies
described in package.json
, then at the command line type npm start
. Once the start process has completed, printed in your terminal you should see:
[RMA Generate PDF (API)] 5001
[RMA Generate PDF (React)] 5002
[RMA Generate PDF (Hogan)] 5003
Browse either http://localhost:5002
or http://localhost:5003
.
Running
To run Gulp and start the application with the default settings, at the command line type:
npm start
To run Gulp and start the application, then watch for changes on the file system, at the command line type:
npm run dev
To build the application without starting the servers, run Gulp. At the command line type:
gulp start
To start the API, at the command line type:
node app
To start both the API and the React UI, instead type:
node app --react [port]
Where [port] is the port number for the React server.
To start the API and the Hogan UI, instead type:
node app --hogan [port]
Where [port] is the port number for the Hogan server.
To start the API and both the React and Hogan UIs:
node app --react [port] --hogan [port]
Where [port] is the port number for the React and Hogan servers.
Change the configuration of the API
Open the .env
file in your preferred editor, change the declaration values, then close the file. Remember to save. Then, build and start.