h17-sspdf 0.5.0 → 1.0.1
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/README.md +4 -1
- package/core/pdf-core.js +47 -3
- package/core/render-document.js +1 -0
- package/package.json +4 -2
- package/vendor/jspdf/jspdf.umd.js +33085 -0
- package/vendor/jspdf/jspdf.node.js +0 -32458
package/README.md
CHANGED
|
@@ -20,6 +20,8 @@ Source JSON + Theme = PDF
|
|
|
20
20
|
npm install h17-sspdf
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
Requires **Node.js 18 or newer**. The engine vendors a single self-contained UMD build of jsPDF (which bundles fflate, fast-png, iobuffer internally) plus Chart.js and chartjs-node-canvas. The only install-time dependency is [`canvas`](https://www.npmjs.com/package/canvas) (native C++ addon, used by the chart plugin) — no transitive dependency tree beyond that.
|
|
24
|
+
|
|
23
25
|
## The problem it solves
|
|
24
26
|
|
|
25
27
|
Generating PDFs imperatively means tracking the cursor yourself. Every element you place shifts everything below it. Line wrapping, page breaks, font resets, all manual.
|
|
@@ -471,6 +473,7 @@ Claude Code skills for generating PDFs and themes are available in the `skills/`
|
|
|
471
473
|
- Single-line `row` cells, no multi-line column pairs
|
|
472
474
|
- `{{page}}` gives the current page number; `{{pages}}` (total page count) is not supported because keep-together rules make the final page count unpredictable until the last operation is laid out
|
|
473
475
|
- Charts require the `canvas` npm package (native C++ addon) for server-side rendering; everything else is zero native dependencies
|
|
476
|
+
- A `jspdf.umd.js` build is vendored for client-side/browser use. It bundles all dependencies internally but requires wiring up your own entry point — `pdf-core.js` uses the Node build by default
|
|
474
477
|
|
|
475
478
|
---
|
|
476
479
|
|
|
@@ -480,7 +483,7 @@ Hugo Palma, 2026
|
|
|
480
483
|
|
|
481
484
|
This project vendors the following MIT-licensed libraries:
|
|
482
485
|
|
|
483
|
-
- [jsPDF](https://github.com/parallax/jsPDF) - PDF generation. Copyright (c) 2010-2025 James Hall, yWorks GmbH.
|
|
486
|
+
- [jsPDF](https://github.com/parallax/jsPDF) - PDF generation (UMD build, bundles fflate, fast-png, iobuffer internally). Copyright (c) 2010-2025 James Hall, yWorks GmbH.
|
|
484
487
|
- [Chart.js](https://github.com/chartjs/Chart.js) - Chart rendering. Copyright (c) 2014-2024 Chart.js Contributors.
|
|
485
488
|
- [chartjs-node-canvas](https://github.com/SeanSobey/ChartjsNodeCanvas) - Server-side Chart.js rendering. Copyright (c) 2018 Sean Sobey.
|
|
486
489
|
|
package/core/pdf-core.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
|
-
const { jsPDF } = require("../vendor/jspdf/jspdf.
|
|
2
|
+
const { jsPDF } = require("../vendor/jspdf/jspdf.umd.js");
|
|
3
3
|
const { pxToMm, ptToMm, resolveLineHeightMm } = require("./units");
|
|
4
4
|
|
|
5
5
|
// Style math helpers, shared between core rendering and height estimation.
|
|
@@ -123,6 +123,8 @@ class PDFCore {
|
|
|
123
123
|
this.pageHeight = this.doc.internal.pageSize.getHeight();
|
|
124
124
|
this.backgroundColor = this._resolveColor(this.page.backgroundColor);
|
|
125
125
|
this.lastDrawnBounds = null;
|
|
126
|
+
this.documentStateDepth = 0;
|
|
127
|
+
this.hasDeferredInitialRenderState = Array.isArray(theme.customFonts) && theme.customFonts.length > 0;
|
|
126
128
|
this.defaultRenderState = this._buildDefaultRenderState(this.page);
|
|
127
129
|
this.contentTopY = this.marginTopMm + this.headerHeightMm;
|
|
128
130
|
this.contentBottomY = this.pageHeight - this.marginBottomMm - this.footerHeightMm;
|
|
@@ -138,7 +140,12 @@ class PDFCore {
|
|
|
138
140
|
});
|
|
139
141
|
|
|
140
142
|
this.paintBackground();
|
|
141
|
-
|
|
143
|
+
// Custom fonts are registered by renderDocument() after core construction.
|
|
144
|
+
// Deferring the first setFont() avoids jsPDF warnings when page.defaultText
|
|
145
|
+
// uses a custom family that is not yet in the font map.
|
|
146
|
+
if (!this.hasDeferredInitialRenderState) {
|
|
147
|
+
this.applyDefaultRenderState();
|
|
148
|
+
}
|
|
142
149
|
}
|
|
143
150
|
|
|
144
151
|
/**
|
|
@@ -178,9 +185,11 @@ class PDFCore {
|
|
|
178
185
|
* Force a new page and reset cursor to top margin.
|
|
179
186
|
*/
|
|
180
187
|
addPage() {
|
|
188
|
+
const reopenedStateDepth = this._closeDocumentStatesForPageBreak();
|
|
181
189
|
this.doc.addPage();
|
|
182
190
|
this.paintBackground();
|
|
183
191
|
this.applyDefaultRenderState();
|
|
192
|
+
this._reopenDocumentStatesAfterPageBreak(reopenedStateDepth);
|
|
184
193
|
this.cursorY = this.contentTopY;
|
|
185
194
|
}
|
|
186
195
|
|
|
@@ -198,6 +207,7 @@ class PDFCore {
|
|
|
198
207
|
&& typeof this.doc.restoreGraphicsState === "function";
|
|
199
208
|
if (canSaveGraphicsState) {
|
|
200
209
|
this.doc.saveGraphicsState();
|
|
210
|
+
this.documentStateDepth += 1;
|
|
201
211
|
}
|
|
202
212
|
|
|
203
213
|
try {
|
|
@@ -205,6 +215,7 @@ class PDFCore {
|
|
|
205
215
|
} finally {
|
|
206
216
|
if (canSaveGraphicsState) {
|
|
207
217
|
this.doc.restoreGraphicsState();
|
|
218
|
+
this.documentStateDepth = Math.max(0, this.documentStateDepth - 1);
|
|
208
219
|
}
|
|
209
220
|
this.applyDefaultRenderState();
|
|
210
221
|
}
|
|
@@ -498,11 +509,21 @@ class PDFCore {
|
|
|
498
509
|
const baseline = y + baselineOffsetMm;
|
|
499
510
|
|
|
500
511
|
if (markerStyle.shape) {
|
|
501
|
-
// Vector shape marker: renders via core/shapes.js, no text encoding needed
|
|
512
|
+
// Vector shape marker: renders via core/shapes.js, no text encoding needed.
|
|
513
|
+
// Wrapped in saveGraphicsState/restoreGraphicsState to isolate draw state
|
|
514
|
+
// mutations (setLineCap, setFillColor, etc.) from the main content stream.
|
|
515
|
+
// Without this, accumulated state operators can cause print rendering issues
|
|
516
|
+
// where printer RIPs interpret the stacked state differently than screen viewers.
|
|
502
517
|
const { renderShape, getShapeWidth } = require("./shapes");
|
|
503
518
|
const shapeColor = markerStyle.shapeColor || markerStyle.color || [0, 0, 0];
|
|
504
519
|
const shapeSize = markerStyle.shapeSize || 1;
|
|
520
|
+
if (typeof this.doc.saveGraphicsState === "function") {
|
|
521
|
+
this.doc.saveGraphicsState();
|
|
522
|
+
}
|
|
505
523
|
renderShape(markerStyle.shape, this.doc, x, baseline, shapeColor, shapeSize, textFontSize);
|
|
524
|
+
if (typeof this.doc.restoreGraphicsState === "function") {
|
|
525
|
+
this.doc.restoreGraphicsState();
|
|
526
|
+
}
|
|
506
527
|
this.applyDefaultRenderState();
|
|
507
528
|
} else {
|
|
508
529
|
// Text-based marker (existing behavior)
|
|
@@ -1070,6 +1091,29 @@ class PDFCore {
|
|
|
1070
1091
|
fillColor: this._resolveColor(page.defaultFillColor),
|
|
1071
1092
|
};
|
|
1072
1093
|
}
|
|
1094
|
+
|
|
1095
|
+
_closeDocumentStatesForPageBreak() {
|
|
1096
|
+
if (this.documentStateDepth <= 0) {
|
|
1097
|
+
return 0;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
for (let i = 0; i < this.documentStateDepth; i += 1) {
|
|
1101
|
+
this.doc.restoreGraphicsState();
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
return this.documentStateDepth;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
_reopenDocumentStatesAfterPageBreak(depth) {
|
|
1108
|
+
const count = Number(depth) || 0;
|
|
1109
|
+
if (count <= 0) {
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
for (let i = 0; i < count; i += 1) {
|
|
1114
|
+
this.doc.saveGraphicsState();
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1073
1117
|
}
|
|
1074
1118
|
|
|
1075
1119
|
module.exports = {
|
package/core/render-document.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "h17-sspdf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Declarative PDF engine - define layout once, feed it JSON, get consistent PDFs",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"author": "Hugo Palma",
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
"files": [
|
|
21
21
|
"core",
|
|
22
22
|
"fonts",
|
|
23
|
-
"vendor",
|
|
23
|
+
"vendor/jspdf",
|
|
24
|
+
"vendor/chart.js",
|
|
25
|
+
"vendor/chartjs-node-canvas",
|
|
24
26
|
"examples/themes",
|
|
25
27
|
"examples/sources",
|
|
26
28
|
"index.js",
|