h17-sspdf 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +409 -0
- package/cli.js +135 -0
- package/core/font-registry.js +47 -0
- package/core/pdf-core.js +853 -0
- package/core/plugin-chart.js +109 -0
- package/core/plugin-registry.js +46 -0
- package/core/render-document.js +931 -0
- package/core/shapes.js +354 -0
- package/core/units.js +39 -0
- package/core/validate.js +151 -0
- package/fonts/crimson-text.js +13 -0
- package/fonts/fira-code.js +9 -0
- package/fonts/ibm-plex-sans.js +13 -0
- package/fonts/inter.js +9 -0
- package/fonts/jetbrains-mono.js +13 -0
- package/fonts/lato.js +13 -0
- package/fonts/libre-baskerville.js +11 -0
- package/fonts/lora.js +13 -0
- package/fonts/merriweather.js +13 -0
- package/fonts/montserrat.js +13 -0
- package/fonts/nunito.js +13 -0
- package/fonts/open-sans.js +13 -0
- package/fonts/oswald.js +9 -0
- package/fonts/playfair-display.js +13 -0
- package/fonts/pt-sans.js +13 -0
- package/fonts/raleway.js +13 -0
- package/fonts/roboto.js +13 -0
- package/fonts/source-code-pro.js +13 -0
- package/fonts/source-serif-4.js +13 -0
- package/fonts/work-sans.js +13 -0
- package/index.js +24 -0
- package/package.json +62 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Built-in chart plugin for sspdf.
|
|
5
|
+
*
|
|
6
|
+
* Renders any Chart.js configuration server-side via chartjs-node-canvas
|
|
7
|
+
* and embeds the result as a PNG image in the PDF.
|
|
8
|
+
*
|
|
9
|
+
* Requires peer dependencies:
|
|
10
|
+
* npm install chart.js chartjs-node-canvas
|
|
11
|
+
*
|
|
12
|
+
* Registration:
|
|
13
|
+
* const { registerPlugin, plugins } = require('h17-sspdf');
|
|
14
|
+
* registerPlugin('chart', plugins.chart);
|
|
15
|
+
*
|
|
16
|
+
* Operation format in source JSON:
|
|
17
|
+
* {
|
|
18
|
+
* "type": "chart",
|
|
19
|
+
* "widthMm": 160,
|
|
20
|
+
* "heightMm": 80,
|
|
21
|
+
* "canvasWidth": 1600,
|
|
22
|
+
* "canvasHeight": 800,
|
|
23
|
+
* "chartType": "line",
|
|
24
|
+
* "data": {
|
|
25
|
+
* "labels": ["Jan", "Feb", "Mar"],
|
|
26
|
+
* "datasets": [
|
|
27
|
+
* {
|
|
28
|
+
* "label": "Revenue",
|
|
29
|
+
* "data": [100, 200, 150],
|
|
30
|
+
* "borderColor": "rgba(110, 158, 210, 0.88)",
|
|
31
|
+
* "backgroundColor": "rgba(110, 158, 210, 0.10)"
|
|
32
|
+
* }
|
|
33
|
+
* ]
|
|
34
|
+
* },
|
|
35
|
+
* "options": {
|
|
36
|
+
* "responsive": false,
|
|
37
|
+
* "animation": false,
|
|
38
|
+
* "scales": {
|
|
39
|
+
* "y": { "beginAtZero": true }
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
*
|
|
44
|
+
* Notes:
|
|
45
|
+
* - canvasWidth/canvasHeight control render resolution (default 1600x800).
|
|
46
|
+
* Higher values = sharper chart. widthMm/heightMm control the PDF slot size.
|
|
47
|
+
* - responsive: false and animation: false are injected automatically.
|
|
48
|
+
* - Pass any valid Chart.js config in data and options — the plugin does not
|
|
49
|
+
* modify or abstract it.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
let _ChartJSNodeCanvas = null;
|
|
53
|
+
|
|
54
|
+
function getCanvas() {
|
|
55
|
+
if (!_ChartJSNodeCanvas) {
|
|
56
|
+
try {
|
|
57
|
+
_ChartJSNodeCanvas = require('chartjs-node-canvas').ChartJSNodeCanvas;
|
|
58
|
+
} catch {
|
|
59
|
+
throw new Error(
|
|
60
|
+
'chart plugin requires chartjs-node-canvas — run: npm install chart.js chartjs-node-canvas'
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return _ChartJSNodeCanvas;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = {
|
|
68
|
+
async render(ctx) {
|
|
69
|
+
const { core, operation, bounds } = ctx;
|
|
70
|
+
const ChartJSNodeCanvas = getCanvas();
|
|
71
|
+
|
|
72
|
+
const widthMm = operation.widthMm || (bounds.right - bounds.left);
|
|
73
|
+
const heightMm = operation.heightMm || 80;
|
|
74
|
+
const canvasW = operation.canvasWidth || 1600;
|
|
75
|
+
const canvasH = operation.canvasHeight || 800;
|
|
76
|
+
const x = operation.xMm !== undefined ? operation.xMm : bounds.left;
|
|
77
|
+
|
|
78
|
+
const canvas = new ChartJSNodeCanvas({
|
|
79
|
+
width: canvasW,
|
|
80
|
+
height: canvasH,
|
|
81
|
+
backgroundColour: 'transparent',
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const buf = await canvas.renderToBuffer({
|
|
85
|
+
type: operation.chartType || 'bar',
|
|
86
|
+
data: operation.data || { labels: [], datasets: [] },
|
|
87
|
+
options: {
|
|
88
|
+
...(operation.options || {}),
|
|
89
|
+
responsive: false,
|
|
90
|
+
animation: false,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
core.drawImage({ data: buf, format: 'PNG', x, widthMm, heightMm });
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
estimateHeight(ctx) {
|
|
98
|
+
return (ctx.operation.heightMm || 80) + 4;
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
validate(operation) {
|
|
102
|
+
if (!operation.chartType) {
|
|
103
|
+
throw new Error('chart operation requires chartType (e.g. "bar", "line", "doughnut")');
|
|
104
|
+
}
|
|
105
|
+
if (!operation.data) {
|
|
106
|
+
throw new Error('chart operation requires data — pass a Chart.js data config object');
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const _plugins = new Map();
|
|
2
|
+
|
|
3
|
+
const BUILT_IN_TYPES = new Set([
|
|
4
|
+
"text", "row", "bullet", "divider", "spacer", "hiddenText",
|
|
5
|
+
"block", "section", "group", "quote",
|
|
6
|
+
]);
|
|
7
|
+
|
|
8
|
+
function registerPlugin(type, handler) {
|
|
9
|
+
if (!type || typeof type !== "string") {
|
|
10
|
+
throw new Error("registerPlugin: type must be a non-empty string");
|
|
11
|
+
}
|
|
12
|
+
if (typeof handler === "function") {
|
|
13
|
+
handler = { render: handler };
|
|
14
|
+
}
|
|
15
|
+
if (!handler || typeof handler.render !== "function") {
|
|
16
|
+
throw new Error(`registerPlugin: handler for "${type}" must have a render function`);
|
|
17
|
+
}
|
|
18
|
+
if (BUILT_IN_TYPES.has(type)) {
|
|
19
|
+
throw new Error(`registerPlugin: cannot override built-in type "${type}"`);
|
|
20
|
+
}
|
|
21
|
+
_plugins.set(type, handler);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getPlugin(type) {
|
|
25
|
+
return _plugins.get(type) || null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function hasPlugin(type) {
|
|
29
|
+
return _plugins.has(type);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function unregisterPlugin(type) {
|
|
33
|
+
_plugins.delete(type);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function clearPlugins() {
|
|
37
|
+
_plugins.clear();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
registerPlugin,
|
|
42
|
+
getPlugin,
|
|
43
|
+
hasPlugin,
|
|
44
|
+
unregisterPlugin,
|
|
45
|
+
clearPlugins,
|
|
46
|
+
};
|