pdfn 0.8.0 → 0.8.2
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 +7 -5
- package/dist/cli.js +40 -26
- package/dist/cli.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -31,11 +31,9 @@ npx pdfn dev --open # Start and open browser
|
|
|
31
31
|
- Live preview with hot reload
|
|
32
32
|
- Inspector panel (performance, debug overlays)
|
|
33
33
|
- Accessibility checker (axe-core)
|
|
34
|
-
- PDF/A
|
|
34
|
+
- PDF/A compliance dropdown
|
|
35
35
|
|
|
36
|
-
**PDF/A
|
|
37
|
-
|
|
38
|
-
Generate archival PDFs via pdfn Cloud:
|
|
36
|
+
**PDF/A Compliance:**
|
|
39
37
|
|
|
40
38
|
| Standard | Description |
|
|
41
39
|
|----------|-------------|
|
|
@@ -43,7 +41,11 @@ Generate archival PDFs via pdfn Cloud:
|
|
|
43
41
|
| PDF/A-2b | PDF 1.7 archival, allows transparency |
|
|
44
42
|
| PDF/A-3b | Like PDF/A-2b plus embedded files |
|
|
45
43
|
|
|
46
|
-
|
|
44
|
+
> `pdfn dev` focuses on layout preview. PDF/A archival compliance (validation, metadata, color profiles) is applied by pdfn Cloud as post-processing.
|
|
45
|
+
>
|
|
46
|
+
> **Layout is identical** whether you use pdfn dev, pdfn Cloud, or self-host. Compliance does not change layout — it only adds validation and archival metadata.
|
|
47
|
+
|
|
48
|
+
To generate PDF/A-compliant PDFs, set `PDFN_API_KEY`. Without an API key, requests with a standard will fail.
|
|
47
49
|
|
|
48
50
|
**Server API:**
|
|
49
51
|
|
package/dist/cli.js
CHANGED
|
@@ -1708,7 +1708,7 @@ async function generatePdf(page, html, options = {}) {
|
|
|
1708
1708
|
contentLoadTime = performance.now() - contentStart;
|
|
1709
1709
|
const pagedStart = performance.now();
|
|
1710
1710
|
await page.waitForFunction(
|
|
1711
|
-
() => window.PDFN?.ready === true,
|
|
1711
|
+
() => typeof window.PDFN === "undefined" || window.PDFN?.ready === true,
|
|
1712
1712
|
{ timeout }
|
|
1713
1713
|
);
|
|
1714
1714
|
pagedJsTime = performance.now() - pagedStart;
|
|
@@ -1764,12 +1764,27 @@ async function generatePdf(page, html, options = {}) {
|
|
|
1764
1764
|
function createGenerateHandler(browserManager, options = {}) {
|
|
1765
1765
|
const { timeout = 3e4, onSuccess, onError } = options;
|
|
1766
1766
|
return async (req, res) => {
|
|
1767
|
-
const { html, options: pdfOptions } = req.body;
|
|
1767
|
+
const { html, standard, options: pdfOptions } = req.body;
|
|
1768
1768
|
const format = req.query.format;
|
|
1769
1769
|
if (!html) {
|
|
1770
1770
|
res.status(400).json({ error: "HTML content is required" });
|
|
1771
1771
|
return;
|
|
1772
1772
|
}
|
|
1773
|
+
if (standard) {
|
|
1774
|
+
res.status(400).json({
|
|
1775
|
+
error: `PDF/A archival compliance requires the pdfn Cloud compliance pipeline.
|
|
1776
|
+
|
|
1777
|
+
Layout is identical in local dev. Compliance does not change layout or rendering \u2014 it only adds validation and archival metadata.
|
|
1778
|
+
|
|
1779
|
+
To generate ${standard}-compliant PDFs:
|
|
1780
|
+
Set: PDFN_API_KEY=pdfn_live_...
|
|
1781
|
+
Get key at: https://console.pdfn.dev
|
|
1782
|
+
|
|
1783
|
+
To preview layout without compliance:
|
|
1784
|
+
Remove the 'standard' option`
|
|
1785
|
+
});
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1773
1788
|
if (format === "html") {
|
|
1774
1789
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
1775
1790
|
res.send(html);
|
|
@@ -2642,7 +2657,7 @@ function createPreviewHTML(templates, activeTemplate, hasCloudAccess) {
|
|
|
2642
2657
|
|
|
2643
2658
|
.btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
|
|
2644
2659
|
|
|
2645
|
-
.
|
|
2660
|
+
.standard-select {
|
|
2646
2661
|
padding: 6px 10px;
|
|
2647
2662
|
background: var(--surface-2);
|
|
2648
2663
|
border: 1px solid #333;
|
|
@@ -2652,8 +2667,8 @@ function createPreviewHTML(templates, activeTemplate, hasCloudAccess) {
|
|
|
2652
2667
|
cursor: pointer;
|
|
2653
2668
|
}
|
|
2654
2669
|
|
|
2655
|
-
.
|
|
2656
|
-
.
|
|
2670
|
+
.standard-select:hover { border-color: #444; }
|
|
2671
|
+
.standard-select:focus { outline: none; border-color: var(--primary); }
|
|
2657
2672
|
|
|
2658
2673
|
.cloud-key-message {
|
|
2659
2674
|
display: none;
|
|
@@ -2730,12 +2745,11 @@ function createPreviewHTML(templates, activeTemplate, hasCloudAccess) {
|
|
|
2730
2745
|
<div class="page-info" id="page-info"></div>
|
|
2731
2746
|
</div>
|
|
2732
2747
|
<div class="context-actions">
|
|
2733
|
-
<select id="
|
|
2748
|
+
<select id="standard-select" class="standard-select" title="PDF standard">
|
|
2734
2749
|
<option value="">Standard PDF (Local)</option>
|
|
2735
2750
|
<option value="PDF/A-1b">PDF/A-1b (Cloud)</option>
|
|
2736
2751
|
<option value="PDF/A-2b">PDF/A-2b (Cloud)</option>
|
|
2737
2752
|
<option value="PDF/A-3b">PDF/A-3b (Cloud)</option>
|
|
2738
|
-
<option value="PDF/UA">PDF/UA (Cloud)</option>
|
|
2739
2753
|
</select>
|
|
2740
2754
|
<button class="btn" id="preview-pdf" title="Preview PDF">
|
|
2741
2755
|
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
@@ -3243,23 +3257,23 @@ function createPreviewHTML(templates, activeTemplate, hasCloudAccess) {
|
|
|
3243
3257
|
|
|
3244
3258
|
document.getElementById('view-html').href = htmlUrl;
|
|
3245
3259
|
|
|
3246
|
-
// Preview button handler (local only - hidden when
|
|
3260
|
+
// Preview button handler (local only - hidden when standard selected)
|
|
3247
3261
|
document.getElementById('preview-pdf').onclick = () => {
|
|
3248
3262
|
window.open(pdfUrl, '_blank');
|
|
3249
3263
|
};
|
|
3250
3264
|
|
|
3251
3265
|
// Download button handler
|
|
3252
3266
|
document.getElementById('download-pdf').onclick = async () => {
|
|
3253
|
-
const
|
|
3267
|
+
const standard = document.getElementById('standard-select').value;
|
|
3254
3268
|
const btn = document.getElementById('download-pdf');
|
|
3255
3269
|
|
|
3256
|
-
if (
|
|
3257
|
-
// Use pdfn cloud for
|
|
3270
|
+
if (standard) {
|
|
3271
|
+
// Use pdfn cloud for PDF/A standard
|
|
3258
3272
|
btn.innerHTML = spinnerSvg + ' Generating...';
|
|
3259
3273
|
btn.disabled = true;
|
|
3260
3274
|
|
|
3261
3275
|
try {
|
|
3262
|
-
const response = await fetch('/api/template/' + templateId + '/pdf-
|
|
3276
|
+
const response = await fetch('/api/template/' + templateId + '/pdf-standard?standard=' + encodeURIComponent(standard));
|
|
3263
3277
|
if (!response.ok) {
|
|
3264
3278
|
const error = await response.json();
|
|
3265
3279
|
throw new Error(error.error || 'Failed to generate PDF');
|
|
@@ -3309,20 +3323,20 @@ function createPreviewHTML(templates, activeTemplate, hasCloudAccess) {
|
|
|
3309
3323
|
const downloadIcon = '<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg>';
|
|
3310
3324
|
const cloudDownloadIcon = '<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10"/></svg>';
|
|
3311
3325
|
|
|
3312
|
-
// Update Preview and Download buttons based on
|
|
3326
|
+
// Update Preview and Download buttons based on standard selection
|
|
3313
3327
|
function updateActionButtons() {
|
|
3314
|
-
const
|
|
3328
|
+
const standard = document.getElementById('standard-select').value;
|
|
3315
3329
|
const previewBtn = document.getElementById('preview-pdf');
|
|
3316
3330
|
const downloadBtn = document.getElementById('download-pdf');
|
|
3317
3331
|
const cloudMessage = document.getElementById('cloud-key-message');
|
|
3318
|
-
const needsCloud = !!
|
|
3332
|
+
const needsCloud = !!standard;
|
|
3319
3333
|
const cloudAvailable = hasCloudAccess;
|
|
3320
3334
|
|
|
3321
3335
|
if (needsCloud) {
|
|
3322
3336
|
// Cloud mode - hide Preview, show cloud Download
|
|
3323
3337
|
previewBtn.style.display = 'none';
|
|
3324
3338
|
downloadBtn.innerHTML = cloudDownloadIcon + ' Download';
|
|
3325
|
-
downloadBtn.title = 'Download ' +
|
|
3339
|
+
downloadBtn.title = 'Download ' + standard + ' PDF via pdfn Cloud';
|
|
3326
3340
|
|
|
3327
3341
|
if (!cloudAvailable) {
|
|
3328
3342
|
// Show message and disable Download
|
|
@@ -3353,7 +3367,7 @@ function createPreviewHTML(templates, activeTemplate, hasCloudAccess) {
|
|
|
3353
3367
|
}
|
|
3354
3368
|
|
|
3355
3369
|
// Conformance select change handler
|
|
3356
|
-
document.getElementById('
|
|
3370
|
+
document.getElementById('standard-select').onchange = updateActionButtons;
|
|
3357
3371
|
|
|
3358
3372
|
// Accessibility check handler
|
|
3359
3373
|
let a11yHasRun = false;
|
|
@@ -3756,22 +3770,22 @@ async function startDevServer(options) {
|
|
|
3756
3770
|
res.status(500).send(`Error generating PDF: ${error}`);
|
|
3757
3771
|
}
|
|
3758
3772
|
});
|
|
3759
|
-
app.get("/api/template/:id/pdf-
|
|
3773
|
+
app.get("/api/template/:id/pdf-standard", async (req, res) => {
|
|
3760
3774
|
const template = templates.find((t) => t.id === req.params.id);
|
|
3761
3775
|
if (!template) {
|
|
3762
3776
|
res.status(404).json({ error: "Template not found" });
|
|
3763
3777
|
return;
|
|
3764
3778
|
}
|
|
3765
|
-
const
|
|
3766
|
-
const
|
|
3767
|
-
if (!
|
|
3768
|
-
res.status(400).json({ error: `Invalid
|
|
3779
|
+
const standard = req.query.standard;
|
|
3780
|
+
const validStandards = ["PDF/A-1b", "PDF/A-2b", "PDF/A-3b"];
|
|
3781
|
+
if (!standard || !validStandards.includes(standard)) {
|
|
3782
|
+
res.status(400).json({ error: `Invalid standard. Must be one of: ${validStandards.join(", ")}` });
|
|
3769
3783
|
return;
|
|
3770
3784
|
}
|
|
3771
3785
|
const apiKey = process.env.PDFN_API_KEY;
|
|
3772
3786
|
if (!apiKey) {
|
|
3773
3787
|
res.status(400).json({
|
|
3774
|
-
error: "PDFN_API_KEY required for
|
|
3788
|
+
error: "PDFN_API_KEY required for PDF/A standard. Get one at https://console.pdfn.dev"
|
|
3775
3789
|
});
|
|
3776
3790
|
return;
|
|
3777
3791
|
}
|
|
@@ -3784,7 +3798,7 @@ async function startDevServer(options) {
|
|
|
3784
3798
|
"Content-Type": "application/json",
|
|
3785
3799
|
"Authorization": `Bearer ${apiKey}`
|
|
3786
3800
|
},
|
|
3787
|
-
body: JSON.stringify({ html,
|
|
3801
|
+
body: JSON.stringify({ html, standard })
|
|
3788
3802
|
});
|
|
3789
3803
|
if (!response.ok) {
|
|
3790
3804
|
const error = await response.json().catch(() => ({ message: response.statusText }));
|
|
@@ -3796,7 +3810,7 @@ async function startDevServer(options) {
|
|
|
3796
3810
|
console.log(
|
|
3797
3811
|
chalk.green(" \u2713"),
|
|
3798
3812
|
template.file,
|
|
3799
|
-
chalk.dim(`\u2192 ${
|
|
3813
|
+
chalk.dim(`\u2192 ${standard}`),
|
|
3800
3814
|
chalk.magenta("(Cloud)"),
|
|
3801
3815
|
chalk.dim("\u2022"),
|
|
3802
3816
|
chalk.cyan(`${duration}ms`),
|
|
@@ -3808,7 +3822,7 @@ async function startDevServer(options) {
|
|
|
3808
3822
|
res.send(pdfBuffer);
|
|
3809
3823
|
} catch (error) {
|
|
3810
3824
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3811
|
-
console.log(chalk.red(" \u2717"), template.file, chalk.red(`${
|
|
3825
|
+
console.log(chalk.red(" \u2717"), template.file, chalk.red(`${standard} generation failed:`), message);
|
|
3812
3826
|
res.status(500).json({ error: message });
|
|
3813
3827
|
}
|
|
3814
3828
|
});
|