@jasy/pdf 1.0.0-alpha.3 → 1.0.0-alpha.4

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Florian Heuberger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -37,7 +37,7 @@ account, no upload, nothing leaves your machine.
37
37
  ## You bring the line items. jasy does the rest.
38
38
 
39
39
  You never compute a total, a VAT breakdown or a rounding again. You hand jasy the line items - it
40
- **derives** the document totals and the EN-16931 VAT breakdown, spec-correct. That is *why* the
40
+ **derives** the document totals and the EN-16931 VAT breakdown, spec-correct. That is _why_ the
41
41
  invoices validate: the amounts are correct **by construction**, so the single biggest class of
42
42
  EN-16931 failures (the BR-CO total checks) simply cannot happen.
43
43
 
@@ -48,13 +48,23 @@ const { bytes, xml } = await renderZugferd({
48
48
  number: "RE-2026-001",
49
49
  issueDate: "2026-06-17",
50
50
  currency: "EUR",
51
- seller: { name: "Northwind Studio GmbH", vatId: "DE123456789",
52
- address: { city: "Berlin", postCode: "10115", country: "DE" } },
53
- buyer: { name: "Globex Corporation Ltd",
54
- address: { city: "Munich", postCode: "80331", country: "DE" } },
51
+ seller: {
52
+ name: "Northwind Studio GmbH",
53
+ vatId: "DE123456789",
54
+ address: { city: "Berlin", postCode: "10115", country: "DE" },
55
+ },
56
+ buyer: {
57
+ name: "Globex Corporation Ltd",
58
+ address: { city: "Munich", postCode: "80331", country: "DE" },
59
+ },
55
60
  lines: [
56
- { name: "Brand identity design", quantity: 2, unit: "HUR", netUnitPrice: 100,
57
- vat: { category: "S", ratePercent: 19 } },
61
+ {
62
+ name: "Brand identity design",
63
+ quantity: 2,
64
+ unit: "HUR",
65
+ netUnitPrice: 100,
66
+ vat: { category: "S", ratePercent: 19 },
67
+ },
58
68
  ],
59
69
  });
60
70
  // bytes -> a valid ZUGFeRD PDF/A-3 · xml -> the embedded EN-16931 XML
@@ -123,7 +133,7 @@ verify all of it:
123
133
  - **Compression.** Content streams and images are FlateDecode-compressed; the spreadsheets `jasy export`
124
134
  writes are real `.xlsx` ZIPs we deflate with our own writer and CRC32, zero dependencies.
125
135
  - **Real font metrics.** Text is laid out with the Adobe AFM metrics of the standard-14 fonts - kerning
126
- and word-wrap are *computed*, not guessed.
136
+ and word-wrap are _computed_, not guessed.
127
137
  - **PDF/A-3, matched not approximated.** The conformance graph is hand-built and **passes veraPDF**, the
128
138
  official ISO 19005 validator.
129
139
  - **Byte-exact round-trips.** Generate and parse are inverses: `generate → parse → regenerate` reproduces
@@ -135,11 +145,11 @@ verify all of it:
135
145
 
136
146
  ## Packages
137
147
 
138
- | Package | What it is |
139
- | --- | --- |
148
+ | Package | What it is |
149
+ | --------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
140
150
  | **[@jasy/zugferd](https://npmx.dev/@jasy/zugferd)** | ZUGFeRD / XRechnung: your data → PDF/A-3 + EN-16931 XML, with local validation. **The prize.** |
141
- | **[@jasy/cli](https://npmx.dev/@jasy/cli)** | the `jasy` terminal: read · validate · export, headless **and** interactive |
142
- | **[@jasy/pdf](https://npmx.dev/@jasy/pdf)** | the declarative, Flutter-style PDF layout engine that powers them |
151
+ | **[@jasy/cli](https://npmx.dev/@jasy/cli)** | the `jasy` terminal: read · validate · export, headless **and** interactive |
152
+ | **[@jasy/pdf](https://npmx.dev/@jasy/pdf)** | the declarative, Flutter-style PDF layout engine that powers them |
143
153
 
144
154
  ---
145
155
 
@@ -158,7 +168,7 @@ verify all of it:
158
168
 
159
169
  jasy targets the documents that matter here: invoices, reports, quotes, datasheets. It is **not** a
160
170
  LaTeX / WeasyPrint replacement - no microtypography, hyphenation or bidi, and arbitrary multi-page flow
161
- of *any* content is still maturing. For e-invoices (a table, totals, a footer) it is complete - they
171
+ of _any_ content is still maturing. For e-invoices (a table, totals, a footer) it is complete - they
162
172
  even paginate. We would rather under-promise and over-deliver in the demo above.
163
173
 
164
174
  > **Status:** young and pre-1.0. The API can still shift between minor versions. Everything shown here
@@ -4,6 +4,8 @@ import { PDFElement } from "../elements/pdf-element.js";
4
4
  import { DefaultTextStyleElement } from "../elements/layout/default-text-style-element.js";
5
5
  import type { OverflowPolicy } from "../layout/fragmentation.js";
6
6
  import { PageSize } from "../constants/page-sizes.js";
7
+ import { type EncryptOptions } from "../crypto/security-handler.js";
8
+ export type { EncryptOptions, Permissions } from "../crypto/security-handler.js";
7
9
  import { StackOptions } from "./layout.js";
8
10
  import { Insets } from "./insets.js";
9
11
  import { TextDefaults } from "./text.js";
@@ -120,6 +122,9 @@ export interface RenderOptions {
120
122
  /** What to do when content is taller than a page and cannot break: `"error"` throws (default),
121
123
  * `"warn"` logs and clips, `"ignore"` clips silently. It is always clipped either way. */
122
124
  onOverflow?: OverflowPolicy;
125
+ /** Encrypt the PDF with a password (AES-256, the newest standard). NOT compatible with PDF/A
126
+ * (ZUGFeRD invoices) - encrypting one throws, since PDF/A forbids encryption. */
127
+ encrypt?: EncryptOptions;
123
128
  }
124
129
  /** Renders a `Document(...)` tree to the raw PDF string. */
125
130
  export declare function renderPdf(doc: PDFDocumentElement, options?: RenderOptions): Promise<string>;
@@ -7,6 +7,7 @@ import { Orientation } from "../renderer/pdf-config.js";
7
7
  import { PDFDocument } from "../renderer/pdf-document-class.js";
8
8
  import { FontStyle } from "../utils/pdf-object-manager.js";
9
9
  import { getArrayBuffer } from "../utils/utf8-to-windows1252-encoder.js";
10
+ import { createSecurityHandler } from "../crypto/security-handler.js";
10
11
  import { Column } from "./layout.js";
11
12
  import { toEdges } from "./insets.js";
12
13
  import { toTextStyleOverride } from "./text.js";
@@ -117,6 +118,11 @@ export async function renderPdf(doc, options) {
117
118
  ...options?.fonts,
118
119
  };
119
120
  const attachments = options?.attachments ?? [];
121
+ // The security handler's key derivation is async (WebCrypto), so build it here, before the sync
122
+ // PDFDocument constructor wires it into the object manager.
123
+ const securityHandler = options?.encrypt
124
+ ? await createSecurityHandler(options.encrypt)
125
+ : undefined;
120
126
  // A throwaway PDFDocument whose build() yields this tree, reusing the engine's standard
121
127
  // font registration + config handling (the constructor does both). Custom fonts are
122
128
  // registered here, before layout/render, so both the metrics and the backend see them.
@@ -155,6 +161,8 @@ export async function renderPdf(doc, options) {
155
161
  om.setPdfVersion(options.pdfVersion);
156
162
  if (options?.documentId)
157
163
  om.enableDocumentId();
164
+ if (securityHandler)
165
+ om.setSecurityHandler(securityHandler);
158
166
  }
159
167
  build() {
160
168
  return doc;