Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
2809190
Guide updated
bmblb May 12, 2023
fa35180
Merge pull request #12 from bryntum/doc-update
bmblb May 12, 2023
a059e7a
different way to get chromium revision from puppeteer package
rostacik Dec 29, 2023
bb83295
Merge pull request #19 from rostacik/main
bmblb Mar 11, 2024
d5412e1
update readme
bmblb Nov 20, 2024
7641b4c
upgdrade node, puppeteer and muhammara
bmblb Jan 15, 2025
31b5e60
2.0.0
bmblb Jan 16, 2025
676c1e5
upgrade packages
bmblb Jan 16, 2025
4e1de77
cleanup
bmblb Jan 16, 2025
811976d
docker updated
bmblb Jan 16, 2025
e5a0813
disable sandbox
bmblb Jan 16, 2025
88ebc8f
Merge pull request #26 from bryntum/upgrading-packages
bmblb Jan 16, 2025
a95ee2a
compatibility note
bmblb Jan 16, 2025
23d7cc0
Merge pull request #27 from bryntum/upgrading-packages
bmblb Jan 16, 2025
30c2107
upgrade workflow
bmblb Jan 16, 2025
a5aecdf
upgrade workflow
bmblb Jan 16, 2025
48f7641
upgrade workflow
bmblb Jan 16, 2025
74773df
upgrade workflow
bmblb Jan 16, 2025
6220b2e
upgrade workflow
bmblb Jan 16, 2025
35aa8fd
upgrade workflow
bmblb Jan 16, 2025
a424e9f
add supported node versions to readme
bmblb Mar 11, 2025
93e28d9
readme update
bmblb Mar 11, 2025
9259342
WIP
bmblb Apr 17, 2025
c8b6a70
WIP
bmblb Apr 18, 2025
5d2fad7
2.0.1
bmblb Apr 18, 2025
d5d6bb7
WIP
bmblb Apr 18, 2025
f9bbab1
update package-lock
bmblb Apr 18, 2025
603fd34
fixing test
bmblb Apr 18, 2025
0564894
fixing test
bmblb Apr 18, 2025
f842e88
fixing test
bmblb Apr 18, 2025
9fd99f0
Merge pull request #30 from bryntum/feature/websocket
bmblb Apr 22, 2025
3620f1d
support custom paper format
bmblb Jun 3, 2025
568d1e5
Merge pull request #32 from bryntum/feature/custom-paper-size
bmblb Jun 3, 2025
8418533
2.1.0
bmblb Jun 3, 2025
8be5875
Merge pull request #33 from bryntum/feature/custom-paper-size
bmblb Jun 3, 2025
dfc5efe
update dependencies
bmblb Aug 8, 2025
8a9daad
update gitignore
bmblb Aug 8, 2025
e29d8f0
fix relative paths
bmblb Aug 8, 2025
e55a0ab
rename build script
bmblb Aug 8, 2025
c7d82ee
docs
bmblb Aug 10, 2025
cdddbad
fix build script for windows
bmblb Aug 11, 2025
0a85bfa
2.1.1
bmblb Aug 11, 2025
1b02c06
Apply suggestions from code review
bmblb Aug 11, 2025
ee30967
remove unused code
bmblb Aug 11, 2025
2740f59
more log points
bmblb Aug 13, 2025
e00ac8c
update build script
bmblb Aug 13, 2025
a24264f
update doc
bmblb Aug 13, 2025
3b3b37e
review
bmblb Aug 13, 2025
7fe35b1
fix vulnerabilities
bmblb Aug 13, 2025
db3ca56
Merge pull request #35 from bryntum/34-update-buildingmd-guide-to-pro…
bmblb Aug 15, 2025
5765b80
fix websocket file url
bmblb Sep 11, 2025
f91c1c9
2.1.2
bmblb Sep 11, 2025
4c02bfb
review
bmblb Sep 11, 2025
e819fcc
Merge pull request #37 from bryntum/fix-websocket-file-url
bmblb Sep 11, 2025
95800a4
update docker base image
bmblb Oct 1, 2025
46bc195
2.2.0
bmblb Oct 1, 2025
941e2a2
Merge pull request #38 from bryntum/update-docker-base-image
bmblb Oct 1, 2025
2c5046b
fix workflow
bmblb Nov 18, 2025
c937ee3
upgrade packages
bmblb Nov 20, 2025
077a4f8
2.2.1
bmblb Nov 20, 2025
f2348d4
Merge pull request #39 from bryntum/fix-vulnerabilities
bmblb Nov 20, 2025
2c45f8a
update base image
bmblb Jan 28, 2026
5f5bb17
2.3.0
bmblb Jan 28, 2026
0ade983
Merge pull request #41 from bryntum/fix/cve-issues
bmblb Jan 28, 2026
ca57aae
update tar package version to 7.5.7
bmblb Feb 2, 2026
ddcc288
Merge pull request #42 from bryntum/fix/cve
bmblb Feb 2, 2026
d342767
2.3.1
bmblb Feb 2, 2026
925b42c
Merge pull request #44
bmblb Feb 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
name: Build Docker Image

on:
workflow_dispatch:
inputs:
tag:
description: 'Tag'
required: true
default: 'latest'
release:
types:
- published
Expand All @@ -12,23 +18,25 @@ jobs:
-
name: Set up labels
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@v5
with:
images: bryntum/pdf-export-server
tags: |
bryntum/pdf-export-server:${{ github.event.release.tag_name }}
bryntum/pdf-export-server:latest
${{ github.event.release.tag_name }}
${{ inputs.tag }}
latest
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
-
name: Login to DockerHub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v3
uses: docker/build-push-action@v6
with:
push: true
tags: ${{ steps.meta.output.tags }}
tags: ${{ steps.meta.outputs.tags }}
2 changes: 1 addition & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:

strategy:
matrix:
node-version: [14.x, 16.x]
node-version: [20.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
Expand Down
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
.idea

dist
dist/
cert
cert/
log
log/
tmp
tmp/
bin/linux
bin/win
bin/mac
node_modules/
!log/config.json
!.gitkeep
36 changes: 36 additions & 0 deletions .run/Build pdf server image.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Build pdf server image" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="pdf-export-server:test" />
<option name="buildCliOptions" value="--platform=linux/amd64" />
<option name="containerName" value="pdf-export-server" />
<option name="entrypoint" value="node ./src/server.js -H 8081 -r host_resources" />
<option name="portBindings">
<list>
<DockerPortBindingImpl>
<option name="containerPort" value="8080" />
<option name="hostPort" value="8080" />
</DockerPortBindingImpl>
<DockerPortBindingImpl>
<option name="containerPort" value="8081" />
<option name="hostPort" value="8081" />
</DockerPortBindingImpl>
</list>
</option>
<option name="commandLineOptions" value="--platform=linux/amd64 -d" />
<option name="sourceFilePath" value="Dockerfile" />
<option name="volumeBindings">
<list>
<DockerVolumeBindingImpl>
<option name="containerPath" value="/home/pptruser/host_resources" />
<option name="hostPath" value="$PROJECT_DIR$/.." />
<option name="readOnly" value="true" />
</DockerVolumeBindingImpl>
</list>
</option>
</settings>
</deployment>
<method v="2" />
</configuration>
</component>
6 changes: 2 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Based on https://developers.google.com/web/tools/puppeteer/troubleshooting#running_puppeteer_in_docker

FROM node:16.15.0
FROM node:25-slim

RUN apt-get update \
&& apt-get install -y wget gnupg ca-certificates \
&& apt-get install -y wget gnupg ca-certificates build-essential python3 make gcc g++ \
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
&& apt-get update \
Expand Down Expand Up @@ -51,5 +51,3 @@ RUN npm i
EXPOSE 8080 8081

ENTRYPOINT [ "node", "./src/server.js", "-H", "8081" ]

CMD ["bash"]
163 changes: 81 additions & 82 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,126 +1,125 @@
# Standalone export server
# PDF export server

This package contains the sources for an executable service which builds PDF and PNG files out of HTML fragments.
This repository contains sources for a server which builds PDF and PNG files out of HTML fragments.
Server is designed to work with [Bryntum PDF Export feature](https://bryntum.com/products/grid/docs/api/Grid/feature/export/PdfExport).
See compatibility table between Export server and other Bryntum products in this [table](docs/compatibility.md).

Live demos available here:
- [Gantt](https://bryntum.com/products/gantt/examples/export/)
- [Scheduler](https://bryntum.com/products/scheduler/examples/export/)
- [Grid](https://bryntum.com/products/grid/examples/export/)

#### Features
- Uses headless chromium browser.
- Easy OS (Linux, Windows, Mac) independent build/install into binary or runnable as NodeJS instance.
- Runnable as NodeJS instance.
- Docker container available at [Docker Hub](https://hub.docker.com/r/bryntum/pdf-export-server)
- Can be assembled to a single executable file
- Written in JavaScript and fully adaptable.
- Can be used as standalone service or as an intermediary between your (C#, Java, PHP) frontend and backend. Just catch
the HTML fragments, call the service and serve the binary.

## Compatibility
- Can be used as standalone service or as an intermediary between your (C#, Java, PHP) frontend and backend.

| pdf-export-server | ExtScheduler/ExtGantt | Bryntum Grid/Scheduler/Gantt |
|---|---|---|
| 1.0.0 | * | * |
## Getting started

## Usage
### Using NodeJS

To start PDF export server you only need to install packages and run a node command:
Supported Node versions:
```
1.0.0+: Node >=8 <14
2.0.0+: Node >= 20
```

1. Check out this repository
```shell
git clone git@github.com:bryntum/pdf-export-server.git
cd pdf-export-server
```
2. Install packages
```shell
pdf-export-server$ npm i
pdf-export-server$ node ./src/server.js
```
3. Start the server
```shell
pdf-export-server$ npm run start
Access-Control-Allow-Origin: *
Http server started on port 8080
```

<a name="cli"></a>
Multiple configuration options are available as you can see in the [configuration](docs/configuration.md) guide.


## Configuration
### Using image from Docker Hub

You can specify application options in the app.config.js or by passing them from the CLI.
For your convenience we have pre-built container available on
[Docker Hub](https://hub.docker.com/r/bryntum/pdf-export-server).

1. Pull it
```shell
pdf-export-server$ node ./src/server.js --help

Usage: ./server [OPTION]

-h, --http=PORT Start http server on port
-H, --https=PORT Start https server on port
-c, --cors=HOST CORS origin, default value "*". Set to "false" to disable CORS
-m, --maximum=SIZE Maximum upload size (default 50mb)
-r, --resources=PATH The absolute path to the resource directory. This path will be accessible via the webserver
--max-workers=WORKERS Maximum amount of workers (puppeteer instances) (default: 5)
--level=LEVEL Specify log level (error, warn, verbose). Default "error"
--timeout=TIMEOUT Request timeout time in seconds
--quick Provide to only wait for page load event
--no-sandbox Provide to pass no-sandbox argument to chromium
--no-config Provide to ignore app.config.js
--verbose Alias for --level=verbose
--help Show help message
docker pull bryntum/pdf-export-server
```

The following command starts a server with HTTP and HTTPS on ports 8080 and 8081 respectively:

2. Create `docker-compose.yml` and configure image/port forwarding
```yaml
version: "3.9"
services:
web:
image: "bryntum/pdf-export-server:latest"
ports:
- "8080:8080"
```
3. Start container
```shell
pdf-export-server$ node ./src/server.js -h 8080 -H 8081 -m 100mb
docker compose -f docker-compose.yml up
```

The flag -m above extends the upload capacity to 100 MB.

##### Workers

To speed up the export we parallelize it using puppeteer instances (workers). It is slower than using tabs, but much
easier to restart the export if browser or tab fails. By default, there are 5 workers which feel fine on machines with
as much as 1 GB RAM. In general, it takes about 2-3 seconds to generate one PDF page, depending on network speed and
overall system performance. Workers amount is not limited.
You can also build image from source as described in the [Docker guide](docs/docker.md)

##### Resources
<a name="CORS"></a>
When sending HTML fragments to the server, the server launches puppeteer and tries to generate PDF-files based on the
provided input. In case the CSS stylesheets are not accessible to the server (for example the resources are protected
by a login session), you can make use of the built-in web-server to serve resources.
## Building Executable

In this case configure the export feature with `translateURLsToAbsolute`.
To create a standalone executable file for the PDF Export Server, follow the detailed instructions provided in
the [Building executable guide](docs/building.md). This process ensures that the server can run independently without
needing a Node.js environment.

```javascript
new Grid({
features : {
pdfExport : {
exportServer : 'http://export-host:8081',
translateURLsToAbsolute : 'http://export-host:8081/resources'
}
}
})
```
### Steps to Build

This tells the export plugin to change all the used stylesheet URLs to be fetched from
`http://export-host:8081/resources`. Then copy all the resources your application uses to the export server keeping the
folder hierarchy. After this map the virtual `http://export-host:8081/resources` to the real folder on your export
server:
1. **Install Prerequisites**
- Ensure you have the required Node.js version (as mentioned in the [Getting Started](#using-nodejs) section).
- Make sure all dependencies are installed:
```shell
npm i
```

```shell
pdf-export-server$ node ./src/server.js -r /web/application/styles
```
2. **Run the Build Command**
Execute the following command to create the executable:
```shell
npm run build
```

The path can be either absolute (`/web/application/styles`) or relative (`web/application/styles`),
for example when you start the export server with the export demo locally.

So if you're running the export demo from the localhost, for example `http://lh/bryntum-suite/grid/examples/export/`,
you need to copy the folders starting from the `bryntum-suite` to the `examples/_shared/server/web/application/styles`,
keeping only resources the demo uses (css files, fonts etc.).

##### Security

Be careful which folder to set open with the -r option; php, aspx/cs, config files won't be interpreted but served as
download when hit. Only point folders which contain resources needed for generating the page, like fonts, CSS or image
files.
3. **Run the Executable**
Once the executable is built, you can launch the server directly without any external dependencies:
```shell
./dist/pdf-export-server*
```

## Links
- [Architecture](docs/architecture.md)
- [Server protocol](docs/protocol.md)
- [Building executable](docs/building.md)
- [Docker](docs/docker.md)
- [Compatibility table](docs/compatibility.md)
- [Configuration options](docs/configuration.md)
- [Troubleshooting](docs/troubleshooting.md)

## FAQ

### Exported PDF/PNG doesn't look correct

Most likely server couldn't get access to the resources. See [architecture](docs/architecture.md) guide for detailed
information or [resources section](#CORS) for short summary.
information, [resources section](#CORS) for short summary and [troubleshooting](docs/troubleshooting.md) guide for
debugging tips.

### PDF/PNG file is not generated

Most likely there is a problem on the server, see [troubleshooting](docs/troubleshooting.md) guide for help.

### Cannot export using HTTPS

You can see errors like `NET::ERR_CERT_AUTHORITY_INVALID` or CORS exception (in Firefox). See
[Make browser to accept self-signed certificate](#self-signed-certificate) section for more info.
[Make browser to accept self-signed certificate](docs/building.md#self-signed-certificate) section for more info.
6 changes: 3 additions & 3 deletions __tests__/assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ async function getFile(json, protocol, fileFormat, host, port, timeout) {
});

request.on('timeout', () => {
request.abort();
request.destroy();

reject(new Error('timeout'));
});
Expand All @@ -61,10 +61,10 @@ async function assertExportedFile({ protocol, host, port, fileFormat }) {
let result = false;

if (fileFormat === 'png') {
result = await assertImage(path.join(__dirname, 'samples', 'smoke', 'base_https.png'), exportedFile);
result = await assertImage(path.join(process.cwd(), '__tests__', 'samples', 'smoke', 'base_https.png'), exportedFile);
}
else {
let baseSize = fs.statSync(path.join(__dirname, 'samples', 'smoke', `base_https.pdf`)).size;
let baseSize = fs.statSync(path.join(process.cwd(), '__tests__', 'samples', 'smoke', `base_https.pdf`)).size;

const gotSize = Math.abs(baseSize - exportedFile.length);
const expectedSize = baseSize * 0.05;
Expand Down
2 changes: 1 addition & 1 deletion __tests__/samples/fileprotocol/data.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"html": [
{ "html" : "<!DOCTYPE html><html class=\"\" style=\"width: 8.25in; height: 11.69in;\"><head><meta content=\"text/html; charset=UTF-8\" http-equiv=\"Content-Type\"/><title></title><link rel=\"stylesheet\" href=\"file:///C:/foo.css\"><link rel=\"stylesheet\" href=\"file:///home/bar.css\"><style>body {background-image: url(\"file:///home/buz.css\");}</style><link rel=\"stylesheet\" href=\"https://bryntum.com/examples/build/grid.stockholm.css?455383\"></head><body></body></html>" }
{ "html" : "<!DOCTYPE html><html class=\"\" style=\"width: 8.25in; height: 11.69in;\"><head><meta content=\"text/html; charset=UTF-8\" http-equiv=\"Content-Type\"/><title></title><link rel=\"stylesheet\" href=\"file:///C:/foo.css\"><link rel=\"stylesheet\" href=\"file:///home/bar.css\"><style>body {background-image: url(\"file:///home/buz.css\");}</style><link rel=\"stylesheet\" href=\"https://bryntum.com/products/grid/build/grid.stockholm.css?455383\"></head><body></body></html>" }
],
"orientation": "portrait",
"format": "A4",
Expand Down
2 changes: 1 addition & 1 deletion __tests__/samples/parallel/parallel2.json

Large diffs are not rendered by default.

Binary file modified __tests__/samples/smoke/base_https.pdf
Binary file not shown.
2 changes: 1 addition & 1 deletion __tests__/samples/smoke/base_https.pdf.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion __tests__/samples/smoke/base_https.png.json

Large diffs are not rendered by default.

Loading