Skip to content
This repository was archived by the owner on Feb 19, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
50 changes: 49 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,62 @@ and can use the GET .../:file urls to retrieve the files.

There are also the following additional parameters:

```
marginTop = top page margin
marginLeft = left page margin
marginRight = right page margin
marginBottom = bottom page margin
```


```
header = HTML/CSS content to be used as the document header.
if header is provided, marginTop is a required parameter.
footer = HTML/CSS content to be used as the document footer.
if footer is provided, marginBottom is a required parameter.
```

## Setting up remote debugging for PHPStorm

Modify the service definition at `/usr/lib/systemd/system/remote-pdf-printer.service` and set the `--inspect` flag, passing the listening IP and Port:

```
ExecStart=/usr/bin/node --inspect=<virtual machine IP>:<desired port> /var/lib/remote-pdf-printer/server.js
```
The default node debugging port is 9229. Example:
```
ExecStart=/usr/bin/node --inspect=192.168.100.102:9229 /var/lib/remote-pdf-printer/server.js
```

Ensure that the relevant firewall services, etc, allow for connections on this IP and Port

In PHPStorm, make sure the `Node.js Remote Interpreter` plugin is installed

Go to **Run > Edit Configurations**, click `+`, and create a new `Attach to Node.js/Chrome`. Give the new configuration a name, set the host IP and Port to the values entered in the service definition, and select `Attach to: Chrome or Node.js > 6.3 started with --inspect`.

You can now select this run configuration from the debug dropdown in the top right toolbar.

## Enable remote execution of local files

If you need to execute local workstation files in the VM's environment, you can configure the remote interpreter.

Make sure the `Node.js Remote Interpreter` is installed.

Go to **Settings > Languages and Frameworks > Node.js**, click the dropdown for `Node interpreter`, and select `Add > Add Remote`. Select `SSH`, and click `...` next to `SSH configuration`.

```
Host: prolegis.local (or your VM's name)
Username: root
Port: 22
Local port: <Dynamic>
Authentication type: OpenSSH config and authentication agent
```

Click `Test Connection` to ensure this works. Click `OK`.

In the **Configure Node.js Remote Interpreter** dialog, select the SSH configuration you just entered, and set the `Node.js interpreter path` to `/usr/bin/node` (the path to Node on the vm). Click `OK`.

In the **Settings > Languages and Frameworks > Node.js** dialog, select the `Node interpreter` you just entered, and click `OK`.

Go to **Run > Edit Configurations**, click `+`, and create a new `Node.js`. Give the configuration a name, set the `Node interpreter` to the one you just created above, set the `Working directory` to your project directory, select the `JavaScript file` you want to execute, and enter any `Environment Variables` that are needed for the project, and set up the `Path mappings`. Click `OK`.

From the dropdown in the top right menu, you can now select the remote interpreter and execute the configured file on the virtual machine.
148 changes: 148 additions & 0 deletions api/content/content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
'use strict';

import jsdom from 'jsdom';
import camelCase from "camelcase";
import CDP from "chrome-remote-interface";
import uniqueFilename from "unique-filename";
import fs from "fs";

const load = async function(html, options) {
if (options.debug) {
console.log('Load(html) called');
}

let target = undefined;
try {
if (options.debug) {
console.log(`Load using ports ${options.port}`);
}

target = await CDP.New({port: options.port});
const client = await CDP({target});
const {
Network,
Page
} = client;
await Promise.all([Network.enable(), Page.enable()]);
return new Promise(async (resolve, reject) => {
function complete(options) {
if (options.debug) {
console.log('Load(html) *actually* resolved');
}
resolve(options);
}

let resolveOptions = {
client: client,
target: target
};
let failed = false;
let completed = false;
let postResolvedRequests = [];
const url = /^(https?|file|data):/i.test(html) ? html : 'data:text/html;base64,' + Buffer.from(html).toString('base64');

Network.loadingFailed((params) => {
failed = true;

if (options.debug) {
console.log(`Load(html) Network.loadingFailed: "${params.errorText}"`);
}

reject(new Error('Load(html) unable to load remote URL'));
});

Network.requestWillBeSent((params) => {
if (completed === true) {
postResolvedRequests[params.requestId] = 1;
}
});

Network.responseReceived((params) => {
if (options.debug) {
console.log(`Load(html) Response Received: (${params.requestId}) Status: ${params.response.status}`);
}

if (completed === true) {
delete postResolvedRequests[params.requestId];
if (postResolvedRequests.length === 0) {
clearTimeout(waitForResponse);
complete(resolveOptions);
}
}
});

Page.navigate({url});
await Page.loadEventFired();
if (options.debug) {
console.log('Load(html) resolved');
}

let waitForResponse = false;

if (failed) {
await CDP.Close({
port: options.port,
id: target.id
});
}

completed = true;
waitForResponse = setTimeout(complete, 750, resolveOptions);
});
} catch (error) {
console.log(`Load(html) error: ${error}`);
if (target) {
console.log('Load(html) closing open target');
CDP.Close({
port: options.port,
id: target.id
});
}
}
}
const htmlToElement = function(document, string) {
const template = document.createElement('template');
template.innerHTML = string.trim();

return template.content.firstChild;
}

const createHeader = function(document, printOptions) {
const header = htmlToElement(document, (printOptions.headerSettings.enabled && printOptions.headerContent) ? printOptions.headerContent : '<header></header>');

if (printOptions.headerSettings.enabled) {
for (const [property, value] of Object.entries(printOptions.headerSettings.style || {})) {
const prop = camelCase(property);
header.style[prop] = value;
}
}

return header;
}

const contentToDOM = function(content, printOptions) {
const dom = new jsdom.JSDOM(content);
const document = dom.window.document;
const header = createHeader(document, printOptions);

if(header) {
document.body.prepend(header);
}

return document;
}

function dumpContentToDisk(content, options) {
if (options.debug_sources) {
const randomPrefixedHtmlFile = uniqueFilename(options.dir + '/sources/');
fs.writeFile(randomPrefixedHtmlFile, content, (error) => {
if (error) {
throw error;
}
});

console.log(`Wrote HTML file ${randomPrefixedHtmlFile} successfully`);
}
}

export {load, contentToDOM, dumpContentToDisk};
Loading