@jasy/pdf 1.0.0-alpha.3 → 1.0.0-alpha.5
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 +21 -0
- package/README.md +28 -15
- package/dist/api/content.d.ts +3 -0
- package/dist/api/content.js +1 -0
- package/dist/api/structure.d.ts +24 -0
- package/dist/api/structure.js +33 -0
- package/dist/api/table.js +13 -5
- package/dist/api/text.d.ts +4 -1
- package/dist/api/text.js +1 -0
- package/dist/assets/font-data.js +17 -2
- package/dist/assets/font-data.ts +32 -2
- package/dist/crypto/security-handler.d.ts +46 -0
- package/dist/crypto/security-handler.js +129 -0
- package/dist/crypto/webcrypto.d.ts +11 -0
- package/dist/crypto/webcrypto.js +62 -0
- package/dist/elements/image-element.d.ts +5 -1
- package/dist/elements/image-element.js +3 -1
- package/dist/elements/layout/struct-group.d.ts +28 -0
- package/dist/elements/layout/struct-group.js +42 -0
- package/dist/elements/pdf-element.d.ts +4 -0
- package/dist/elements/pdf-element.js +15 -0
- package/dist/elements/text-element.d.ts +7 -1
- package/dist/elements/text-element.js +5 -2
- package/dist/ir/display-list.d.ts +79 -1
- package/dist/layout/fragmentation.js +9 -2
- package/dist/renderer/emoji-image.d.ts +9 -0
- package/dist/renderer/emoji-image.js +68 -0
- package/dist/renderer/image-renderer.d.ts +1 -1
- package/dist/renderer/image-renderer.js +10 -2
- package/dist/renderer/page-renderer.js +19 -4
- package/dist/renderer/pdf-backend.d.ts +6 -2
- package/dist/renderer/pdf-backend.js +59 -3
- package/dist/renderer/pdf-renderer.js +13 -0
- package/dist/renderer/struct-group-renderer.d.ts +10 -0
- package/dist/renderer/struct-group-renderer.js +24 -0
- package/dist/renderer/text-renderer.d.ts +3 -0
- package/dist/renderer/text-renderer.js +182 -2
- package/dist/text/emoji-codepoints.d.ts +1 -0
- package/dist/text/emoji-codepoints.js +29 -0
- package/dist/text/line-breaker.js +6 -3
- package/dist/utils/md5.js +4 -5
- package/dist/utils/pdf-object-manager.d.ts +28 -1
- package/dist/utils/pdf-object-manager.js +0 -0
- package/dist/utils/struct-tree.d.ts +44 -0
- package/dist/utils/struct-tree.js +130 -0
- package/dist/utils/ttf-parser.d.ts +73 -0
- package/dist/utils/ttf-parser.js +456 -16
- package/dist/utils/ttf-subsetter.js +1 -1
- package/dist/utils/ua-xmp.d.ts +9 -0
- package/dist/utils/ua-xmp.js +32 -0
- package/package.json +1 -1
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
|
|
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: {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
{
|
|
57
|
-
|
|
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
|
|
@@ -107,8 +117,8 @@ const pdf = await renderToBytes(
|
|
|
107
117
|
```
|
|
108
118
|
|
|
109
119
|
Real text layout (Adobe AFM metrics, kerning, word-wrap), flexbox-style `gap` / `justify` / `align`,
|
|
110
|
-
boxes with radius and alpha, images, custom TrueType fonts,
|
|
111
|
-
overflows flows onto the next page, headers and footers repeat.
|
|
120
|
+
boxes with radius and alpha, images, custom TrueType fonts, **AES-256 password encryption**, and **real
|
|
121
|
+
pagination** - content that overflows flows onto the next page, headers and footers repeat.
|
|
112
122
|
|
|
113
123
|
---
|
|
114
124
|
|
|
@@ -122,8 +132,11 @@ verify all of it:
|
|
|
122
132
|
740 KB font ships as a ~76 KB subset, and the text stays copy- and searchable.
|
|
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.
|
|
135
|
+
- **AES-256 encryption, pure JS.** Lock a PDF with `renderToBytes(doc, { encrypt: { userPassword } })` - the
|
|
136
|
+
newest standard handler (AES-256, R6 / ISO 32000-2) via the platform WebCrypto: no native crypto, no deps,
|
|
137
|
+
the same code in Node **and** the browser. Owner password + permissions optional.
|
|
125
138
|
- **Real font metrics.** Text is laid out with the Adobe AFM metrics of the standard-14 fonts - kerning
|
|
126
|
-
and word-wrap are
|
|
139
|
+
and word-wrap are _computed_, not guessed.
|
|
127
140
|
- **PDF/A-3, matched not approximated.** The conformance graph is hand-built and **passes veraPDF**, the
|
|
128
141
|
official ISO 19005 validator.
|
|
129
142
|
- **Byte-exact round-trips.** Generate and parse are inverses: `generate → parse → regenerate` reproduces
|
|
@@ -135,11 +148,11 @@ verify all of it:
|
|
|
135
148
|
|
|
136
149
|
## Packages
|
|
137
150
|
|
|
138
|
-
| Package
|
|
139
|
-
|
|
|
151
|
+
| Package | What it is |
|
|
152
|
+
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
140
153
|
| **[@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)**
|
|
142
|
-
| **[@jasy/pdf](https://npmx.dev/@jasy/pdf)**
|
|
154
|
+
| **[@jasy/cli](https://npmx.dev/@jasy/cli)** | the `jasy` terminal: read · validate · export, headless **and** interactive |
|
|
155
|
+
| **[@jasy/pdf](https://npmx.dev/@jasy/pdf)** | the declarative, Flutter-style PDF layout engine that powers them |
|
|
143
156
|
|
|
144
157
|
---
|
|
145
158
|
|
|
@@ -158,7 +171,7 @@ verify all of it:
|
|
|
158
171
|
|
|
159
172
|
jasy targets the documents that matter here: invoices, reports, quotes, datasheets. It is **not** a
|
|
160
173
|
LaTeX / WeasyPrint replacement - no microtypography, hyphenation or bidi, and arbitrary multi-page flow
|
|
161
|
-
of
|
|
174
|
+
of _any_ content is still maturing. For e-invoices (a table, totals, a footer) it is complete - they
|
|
162
175
|
even paginate. We would rather under-promise and over-deliver in the demo above.
|
|
163
176
|
|
|
164
177
|
> **Status:** young and pre-1.0. The API can still shift between minor versions. Everything shown here
|
package/dist/api/content.d.ts
CHANGED
|
@@ -28,6 +28,9 @@ export interface ImageOptions {
|
|
|
28
28
|
fit?: ImageFit;
|
|
29
29
|
/** Corner radius in points (rounds the image box). */
|
|
30
30
|
radius?: number;
|
|
31
|
+
/** Alternate text for accessibility (tagged PDF): describes the image for screen readers. With `alt`
|
|
32
|
+
* the image is a `Figure`; without it (and when rendered `accessible`) it counts as decoration. */
|
|
33
|
+
alt?: string;
|
|
31
34
|
}
|
|
32
35
|
/**
|
|
33
36
|
* An image. `src` is a local file path (wrapped in a `CustomLocalImage`) or a ready
|
package/dist/api/content.js
CHANGED
package/dist/api/structure.d.ts
CHANGED
|
@@ -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";
|
|
@@ -37,12 +39,25 @@ export declare function Page(children: PDFElement[]): PageElement;
|
|
|
37
39
|
export declare function Page(opts: PageOptions, children: PDFElement[]): PageElement;
|
|
38
40
|
/** Document options: PDF metadata plus the inheritable text defaults (font, size, color, lineHeight,
|
|
39
41
|
* align, bold/italic) every `Text` inherits unless it sets its own - Flutter's `DefaultTextStyle`. */
|
|
42
|
+
/** Where color emoji come from when a code point isn't in the current text font, so `Text("Hi 😅")`
|
|
43
|
+
* works in one string. Either a **font** (native vector, offline - recommended; register it in
|
|
44
|
+
* `fonts`) as a string name or `{ font }`, or an **image/CDN source** `{ url, format }` (react-pdf
|
|
45
|
+
* style: fetches `${url}${hexCodePoint}.${format}`, e.g. a Twemoji CDN). The font source is better
|
|
46
|
+
* (vector, no network); the image source exists for parity + as an escape hatch. */
|
|
47
|
+
export type EmojiSource = string | {
|
|
48
|
+
font: string;
|
|
49
|
+
} | {
|
|
50
|
+
url: string;
|
|
51
|
+
format?: "png";
|
|
52
|
+
};
|
|
40
53
|
export interface DocumentOptions extends TextDefaults {
|
|
41
54
|
/** PDF metadata. */
|
|
42
55
|
meta?: {
|
|
43
56
|
title?: string;
|
|
44
57
|
author?: string;
|
|
45
58
|
};
|
|
59
|
+
/** Color-emoji fallback: emoji code points not in the text font render from this source. */
|
|
60
|
+
emoji?: EmojiSource;
|
|
46
61
|
}
|
|
47
62
|
/** A single font file for `addFont`: a `.ttf` path (read on Node) or its raw bytes (e.g. a browser
|
|
48
63
|
* upload). */
|
|
@@ -120,6 +135,15 @@ export interface RenderOptions {
|
|
|
120
135
|
/** What to do when content is taller than a page and cannot break: `"error"` throws (default),
|
|
121
136
|
* `"warn"` logs and clips, `"ignore"` clips silently. It is always clipped either way. */
|
|
122
137
|
onOverflow?: OverflowPolicy;
|
|
138
|
+
/** Encrypt the PDF with a password (AES-256, the newest standard). NOT compatible with PDF/A
|
|
139
|
+
* (ZUGFeRD invoices) - encrypting one throws, since PDF/A forbids encryption. */
|
|
140
|
+
encrypt?: EncryptOptions;
|
|
141
|
+
/** Emit an accessible, tagged PDF (structure tree for screen readers). */
|
|
142
|
+
accessible?: boolean;
|
|
143
|
+
/** Document language for accessibility, e.g. `"en-US"` / `"de-DE"` (default `"en-US"`). */
|
|
144
|
+
lang?: string;
|
|
145
|
+
/** Document title (used by accessible readers; also good metadata). */
|
|
146
|
+
title?: string;
|
|
123
147
|
}
|
|
124
148
|
/** Renders a `Document(...)` tree to the raw PDF string. */
|
|
125
149
|
export declare function renderPdf(doc: PDFDocumentElement, options?: RenderOptions): Promise<string>;
|
package/dist/api/structure.js
CHANGED
|
@@ -7,6 +7,8 @@ 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";
|
|
11
|
+
import { uaXmp } from "../utils/ua-xmp.js";
|
|
10
12
|
import { Column } from "./layout.js";
|
|
11
13
|
import { toEdges } from "./insets.js";
|
|
12
14
|
import { toTextStyleOverride } from "./text.js";
|
|
@@ -55,6 +57,8 @@ const docMeta = new WeakMap();
|
|
|
55
57
|
// Fonts registered via doc.addFont(...), kept beside the element (like meta) and registered on the
|
|
56
58
|
// object manager at render time. Path sources are read to bytes in addFont, so the map holds bytes.
|
|
57
59
|
const docFonts = new WeakMap();
|
|
60
|
+
// The document's color-emoji fallback source, kept beside the element and applied at render time.
|
|
61
|
+
const docEmoji = new WeakMap();
|
|
58
62
|
/** Reads any path sources to bytes, leaving bytes / families as-is. */
|
|
59
63
|
function resolveFontSource(source) {
|
|
60
64
|
const read = (s) => (typeof s === "string" ? readFileBytes(s) : s);
|
|
@@ -80,6 +84,8 @@ export function Document(a, b) {
|
|
|
80
84
|
});
|
|
81
85
|
if (opts.meta)
|
|
82
86
|
docMeta.set(doc, opts.meta);
|
|
87
|
+
if (opts.emoji)
|
|
88
|
+
docEmoji.set(doc, opts.emoji);
|
|
83
89
|
// Managed font registry: addFont registers, getFonts/hasFont query, render reads it (below).
|
|
84
90
|
const registry = new Map();
|
|
85
91
|
docFonts.set(doc, registry);
|
|
@@ -117,6 +123,11 @@ export async function renderPdf(doc, options) {
|
|
|
117
123
|
...options?.fonts,
|
|
118
124
|
};
|
|
119
125
|
const attachments = options?.attachments ?? [];
|
|
126
|
+
// The security handler's key derivation is async (WebCrypto), so build it here, before the sync
|
|
127
|
+
// PDFDocument constructor wires it into the object manager.
|
|
128
|
+
const securityHandler = options?.encrypt
|
|
129
|
+
? await createSecurityHandler(options.encrypt)
|
|
130
|
+
: undefined;
|
|
120
131
|
// A throwaway PDFDocument whose build() yields this tree, reusing the engine's standard
|
|
121
132
|
// font registration + config handling (the constructor does both). Custom fonts are
|
|
122
133
|
// registered here, before layout/render, so both the metrics and the backend see them.
|
|
@@ -147,6 +158,14 @@ export async function renderPdf(doc, options) {
|
|
|
147
158
|
description: a.description,
|
|
148
159
|
});
|
|
149
160
|
}
|
|
161
|
+
// Color-emoji fallback (Document({ emoji })): a font (native vector) or a CDN/image source.
|
|
162
|
+
const emoji = docEmoji.get(doc);
|
|
163
|
+
if (typeof emoji === "string")
|
|
164
|
+
om.setEmojiFont(emoji);
|
|
165
|
+
else if (emoji && "font" in emoji)
|
|
166
|
+
om.setEmojiFont(emoji.font);
|
|
167
|
+
else if (emoji && "url" in emoji)
|
|
168
|
+
om.setEmojiImageSource(emoji.url, emoji.format ?? "png");
|
|
150
169
|
if (options?.xmp)
|
|
151
170
|
om.setXmpMetadata(options.xmp);
|
|
152
171
|
if (options?.outputIntent)
|
|
@@ -155,6 +174,20 @@ export async function renderPdf(doc, options) {
|
|
|
155
174
|
om.setPdfVersion(options.pdfVersion);
|
|
156
175
|
if (options?.documentId)
|
|
157
176
|
om.enableDocumentId();
|
|
177
|
+
if (securityHandler)
|
|
178
|
+
om.setSecurityHandler(securityHandler);
|
|
179
|
+
if (options?.accessible) {
|
|
180
|
+
om.struct.enabled = true;
|
|
181
|
+
if (options.lang)
|
|
182
|
+
om.struct.lang = options.lang;
|
|
183
|
+
// Fall back to the document's own title (Document({ meta })) when no explicit title is given.
|
|
184
|
+
const title = options.title ?? meta?.title;
|
|
185
|
+
if (title)
|
|
186
|
+
om.struct.title = title;
|
|
187
|
+
// Declare PDF/UA-1 in the XMP metadata, unless the caller supplied their own packet.
|
|
188
|
+
if (!options.xmp)
|
|
189
|
+
om.setXmpMetadata(uaXmp({ title }));
|
|
190
|
+
}
|
|
158
191
|
}
|
|
159
192
|
build() {
|
|
160
193
|
return doc;
|
package/dist/api/table.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BoxConstraints } from "../layout/box-constraints.js";
|
|
2
2
|
import { RepeatingHeaderElement } from "../elements/layout/repeating-header-element.js";
|
|
3
3
|
import { DeferredElement } from "../elements/layout/deferred-element.js";
|
|
4
|
+
import { StructGroup } from "../elements/layout/struct-group.js";
|
|
4
5
|
import { Column, Row, Box, Expanded, Padding } from "./layout.js";
|
|
5
6
|
import { Text } from "./text.js";
|
|
6
7
|
function asElement(c) {
|
|
@@ -52,8 +53,11 @@ function composeTable(opts, rows, columns) {
|
|
|
52
53
|
...(firstCol ? { borderLeft: cb } : {}),
|
|
53
54
|
};
|
|
54
55
|
// `ruled` rows get just a bottom line (the header underline / the table foot), without the full grid.
|
|
55
|
-
const wrap = (cell, col, firstRow, firstCol, ruled) => {
|
|
56
|
-
const
|
|
56
|
+
const wrap = (cell, col, firstRow, firstCol, ruled, isHeader) => {
|
|
57
|
+
const content = cellPadding !== undefined ? Padding(cellPadding, asElement(cell)) : asElement(cell);
|
|
58
|
+
// Tag the cell (TH for a header cell, else TD). StructGroup is layout-transparent - visually nothing
|
|
59
|
+
// changes; it only nests the cell's content under a table-cell element in the accessible structure tree.
|
|
60
|
+
const inner = new StructGroup({ role: isHeader ? "TH" : "TD", child: content });
|
|
57
61
|
// The border lives on the wrapper, which stretches to the row height (crisp lines).
|
|
58
62
|
const border = {
|
|
59
63
|
...borderFor(firstRow, firstCol),
|
|
@@ -68,14 +72,18 @@ function composeTable(opts, rows, columns) {
|
|
|
68
72
|
throw new Error(`Unsupported column width "${col}" - use a number of points or "<n>fr"`);
|
|
69
73
|
};
|
|
70
74
|
// align:stretch → equal-height cells, so a wrapping cell keeps the row's bottom rule straight.
|
|
71
|
-
const buildRow = (cells, firstRow, ruled = false
|
|
75
|
+
const buildRow = (cells, firstRow, ruled = false, isHeader = false) => new StructGroup({
|
|
76
|
+
role: "TR",
|
|
77
|
+
child: Row({ gap: colGap, align: "stretch" }, cells.map((cell, i) => wrap(cell, columns[i] ?? "1fr", firstRow, i === 0, ruled, isHeader))),
|
|
78
|
+
});
|
|
72
79
|
// The first row (which gets the top border) is the header if present, else body row 0.
|
|
73
80
|
// `rule` underlines the header and the last body row (the table foot).
|
|
74
81
|
const last = rows.length - 1;
|
|
75
82
|
const body = Column({ gap: rowGap }, rows.map((cells, idx) => buildRow(cells, !opts.header && idx === 0, rule !== undefined && idx === last)));
|
|
76
|
-
|
|
77
|
-
? new RepeatingHeaderElement(buildRow(opts.header, true, rule !== undefined), body, rowGap)
|
|
83
|
+
const table = opts.header
|
|
84
|
+
? new RepeatingHeaderElement(buildRow(opts.header, true, rule !== undefined, true), body, rowGap)
|
|
78
85
|
: body;
|
|
86
|
+
return new StructGroup({ role: "Table", child: table });
|
|
79
87
|
}
|
|
80
88
|
/**
|
|
81
89
|
* A `Column` of `Row`s. Fixed-point columns sit in a fixed-width `Box`, `"Nfr"` fractions
|
package/dist/api/text.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TextElement, TextSegment } from "../elements/text-element.js";
|
|
1
|
+
import { TextElement, TextRole, TextSegment } from "../elements/text-element.js";
|
|
2
2
|
import { TextOverflow } from "../text/line-breaker.js";
|
|
3
3
|
import { ResolvedTextStyle } from "../text/text-style.js";
|
|
4
4
|
import { ColorInput } from "./color.js";
|
|
@@ -23,6 +23,9 @@ export interface TextOptions extends TextStyle {
|
|
|
23
23
|
/** Line-height multiplier (default `1`). `1.4` gives roomier body copy; each line is
|
|
24
24
|
* `size * lineHeight` tall. */
|
|
25
25
|
lineHeight?: number;
|
|
26
|
+
/** Accessibility role for the tagged structure tree (only when rendered with `accessible`): a heading
|
|
27
|
+
* level `"h1"`..`"h6"` or `"p"` (default). Purely semantic - it does not change the visual style. */
|
|
28
|
+
role?: TextRole;
|
|
26
29
|
}
|
|
27
30
|
/**
|
|
28
31
|
* An inline run for mixed-style `Text` (`Text([span("a", {bold:true}), span("b")])`). Each
|
package/dist/api/text.js
CHANGED