@jasy/pdf 1.0.0-alpha.1 → 1.0.0-alpha.10
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 +35 -21
- package/dist/api/args.d.ts +1 -1
- package/dist/api/args.js +2 -5
- package/dist/api/color.d.ts +4 -4
- package/dist/api/color.js +11 -17
- package/dist/api/content.d.ts +20 -13
- package/dist/api/content.js +44 -29
- package/dist/api/descriptor.d.ts +2 -2
- package/dist/api/descriptor.js +121 -31
- package/dist/api/dimension.d.ts +76 -0
- package/dist/api/dimension.js +43 -0
- package/dist/api/font-url.d.ts +28 -0
- package/dist/api/font-url.js +134 -0
- package/dist/api/forms.d.ts +347 -0
- package/dist/api/forms.js +189 -0
- package/dist/api/gradient.d.ts +53 -0
- package/dist/api/gradient.js +97 -0
- package/dist/api/index.d.ts +13 -8
- package/dist/api/index.js +13 -24
- package/dist/api/insets.d.ts +19 -12
- package/dist/api/insets.js +19 -12
- package/dist/api/layout.d.ts +149 -25
- package/dist/api/layout.js +182 -51
- package/dist/api/page-builder.d.ts +40 -0
- package/dist/api/page-builder.js +44 -0
- package/dist/api/structure.d.ts +100 -13
- package/dist/api/structure.js +179 -88
- package/dist/api/table.d.ts +5 -5
- package/dist/api/table.js +38 -26
- package/dist/api/text.d.ts +90 -4
- package/dist/api/text.js +116 -31
- package/dist/assets/font-data.d.ts +2 -0
- package/dist/assets/font-data.js +21 -0
- package/dist/assets/font-data.ts +37 -0
- package/dist/common/color.js +1 -5
- package/dist/constants/page-sizes.js +3 -6
- package/dist/constants/pdf-parts.js +1 -4
- package/dist/crypto/legacy-handler.d.ts +43 -0
- package/dist/crypto/legacy-handler.js +145 -0
- package/dist/crypto/security-handler.d.ts +62 -0
- package/dist/crypto/security-handler.js +155 -0
- package/dist/crypto/webcrypto.d.ts +11 -0
- package/dist/crypto/webcrypto.js +62 -0
- package/dist/edit/acroform-reader.d.ts +70 -0
- package/dist/edit/acroform-reader.js +126 -0
- package/dist/edit/appearance.d.ts +47 -0
- package/dist/edit/appearance.js +171 -0
- package/dist/edit/document.d.ts +145 -0
- package/dist/edit/document.js +642 -0
- package/dist/edit/fill.d.ts +65 -0
- package/dist/edit/fill.js +424 -0
- package/dist/edit/flatten.d.ts +54 -0
- package/dist/edit/flatten.js +462 -0
- package/dist/edit/index.d.ts +20 -0
- package/dist/edit/index.js +18 -0
- package/dist/edit/lexer.d.ts +30 -0
- package/dist/edit/lexer.js +285 -0
- package/dist/edit/objects.d.ts +68 -0
- package/dist/edit/objects.js +83 -0
- package/dist/edit/writer.d.ts +60 -0
- package/dist/edit/writer.js +283 -0
- package/dist/elements/container-element.d.ts +50 -5
- package/dist/elements/container-element.js +100 -39
- package/dist/elements/forms/checkbox-element.d.ts +51 -0
- package/dist/elements/forms/checkbox-element.js +48 -0
- package/dist/elements/forms/choice-element.d.ts +53 -0
- package/dist/elements/forms/choice-element.js +59 -0
- package/dist/elements/forms/push-button-element.d.ts +56 -0
- package/dist/elements/forms/push-button-element.js +49 -0
- package/dist/elements/forms/radio-element.d.ts +52 -0
- package/dist/elements/forms/radio-element.js +49 -0
- package/dist/elements/forms/signature-element.d.ts +54 -0
- package/dist/elements/forms/signature-element.js +47 -0
- package/dist/elements/forms/text-field-element.d.ts +61 -0
- package/dist/elements/forms/text-field-element.js +54 -0
- package/dist/elements/image-element.d.ts +58 -6
- package/dist/elements/image-element.js +145 -111
- package/dist/elements/index.d.ts +26 -10
- package/dist/elements/index.js +26 -28
- package/dist/elements/layout/anchor-element.d.ts +23 -0
- package/dist/elements/layout/anchor-element.js +27 -0
- package/dist/elements/layout/bookmark-element.d.ts +27 -0
- package/dist/elements/layout/bookmark-element.js +30 -0
- package/dist/elements/layout/default-text-style-element.d.ts +30 -0
- package/dist/elements/layout/default-text-style-element.js +47 -0
- package/dist/elements/layout/deferred-element.d.ts +3 -3
- package/dist/elements/layout/deferred-element.js +4 -8
- package/dist/elements/layout/expanded-element.d.ts +6 -4
- package/dist/elements/layout/expanded-element.js +22 -17
- package/dist/elements/layout/keep-together-element.d.ts +42 -0
- package/dist/elements/layout/keep-together-element.js +85 -0
- package/dist/elements/layout/link-element.d.ts +33 -0
- package/dist/elements/layout/link-element.js +39 -0
- package/dist/elements/layout/padding-element.d.ts +10 -5
- package/dist/elements/layout/padding-element.js +25 -16
- package/dist/elements/layout/page-break-element.d.ts +17 -0
- package/dist/elements/layout/page-break-element.js +24 -0
- package/dist/elements/layout/page-builder-element.d.ts +32 -0
- package/dist/elements/layout/page-builder-element.js +45 -0
- package/dist/elements/layout/positioned-element.d.ts +47 -0
- package/dist/elements/layout/positioned-element.js +73 -0
- package/dist/elements/layout/repeating-header-element.d.ts +3 -3
- package/dist/elements/layout/repeating-header-element.js +8 -12
- package/dist/elements/layout/rotated-box-element.d.ts +35 -0
- package/dist/elements/layout/rotated-box-element.js +54 -0
- package/dist/elements/layout/rotated-element.d.ts +32 -0
- package/dist/elements/layout/rotated-element.js +41 -0
- package/dist/elements/layout/sized-container-element.d.ts +2 -2
- package/dist/elements/layout/sized-container-element.js +6 -11
- package/dist/elements/layout/struct-group.d.ts +28 -0
- package/dist/elements/layout/struct-group.js +42 -0
- package/dist/elements/line-element.d.ts +3 -3
- package/dist/elements/line-element.js +5 -10
- package/dist/elements/page-element.d.ts +26 -6
- package/dist/elements/page-element.js +72 -27
- package/dist/elements/pdf-document-element.d.ts +10 -4
- package/dist/elements/pdf-document-element.js +11 -10
- package/dist/elements/pdf-element.d.ts +150 -4
- package/dist/elements/pdf-element.js +138 -20
- package/dist/elements/rectangle-element.d.ts +52 -11
- package/dist/elements/rectangle-element.js +129 -36
- package/dist/elements/row-element.d.ts +45 -4
- package/dist/elements/row-element.js +66 -18
- package/dist/elements/text-element.d.ts +132 -11
- package/dist/elements/text-element.js +206 -56
- package/dist/forms/acroform.d.ts +63 -0
- package/dist/forms/acroform.js +427 -0
- package/dist/forms/appearance.d.ts +58 -0
- package/dist/forms/appearance.js +204 -0
- package/dist/forms/field.d.ts +164 -0
- package/dist/forms/field.js +1 -0
- package/dist/forms/pdf.d.ts +19 -0
- package/dist/forms/pdf.js +28 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -19
- package/dist/ir/display-list.d.ts +197 -8
- package/dist/ir/display-list.js +8 -2
- package/dist/layout/alignment.d.ts +10 -0
- package/dist/layout/alignment.js +6 -0
- package/dist/layout/box-constraints.d.ts +61 -0
- package/dist/layout/box-constraints.js +88 -6
- package/dist/layout/collect-images.d.ts +9 -0
- package/dist/layout/collect-images.js +34 -0
- package/dist/layout/fragmentation.d.ts +20 -1
- package/dist/layout/fragmentation.js +74 -18
- package/dist/layout/insets.d.ts +28 -0
- package/dist/layout/insets.js +20 -0
- package/dist/platform/browser-fs.d.ts +2 -0
- package/dist/platform/browser-fs.js +9 -0
- package/dist/platform/browser-image.d.ts +5 -0
- package/dist/platform/browser-image.js +13 -0
- package/dist/platform/node-fs.d.ts +2 -0
- package/dist/platform/node-fs.js +10 -0
- package/dist/platform/node-image.d.ts +5 -0
- package/dist/platform/node-image.js +9 -0
- package/dist/renderer/anchor-renderer.d.ts +6 -0
- package/dist/renderer/anchor-renderer.js +11 -0
- package/dist/renderer/bookmark-renderer.d.ts +6 -0
- package/dist/renderer/bookmark-renderer.js +11 -0
- package/dist/renderer/container-renderer.d.ts +3 -3
- package/dist/renderer/container-renderer.js +11 -28
- package/dist/renderer/default-text-style-renderer.d.ts +6 -0
- package/dist/renderer/default-text-style-renderer.js +10 -0
- package/dist/renderer/deferred-renderer.d.ts +3 -3
- package/dist/renderer/deferred-renderer.js +8 -23
- package/dist/renderer/emoji-image.d.ts +9 -0
- package/dist/renderer/emoji-image.js +68 -0
- package/dist/renderer/expanded-renderer.d.ts +3 -3
- package/dist/renderer/expanded-renderer.js +6 -21
- package/dist/renderer/form-field-renderer.d.ts +20 -0
- package/dist/renderer/form-field-renderer.js +12 -0
- package/dist/renderer/image-renderer.d.ts +4 -4
- package/dist/renderer/image-renderer.js +86 -75
- package/dist/renderer/index.d.ts +10 -10
- package/dist/renderer/index.js +10 -26
- package/dist/renderer/keep-together-renderer.d.ts +10 -0
- package/dist/renderer/keep-together-renderer.js +12 -0
- package/dist/renderer/line-renderer.d.ts +3 -3
- package/dist/renderer/line-renderer.js +13 -28
- package/dist/renderer/link-renderer.d.ts +6 -0
- package/dist/renderer/link-renderer.js +11 -0
- package/dist/renderer/padding-renderer.d.ts +3 -3
- package/dist/renderer/padding-renderer.js +6 -21
- package/dist/renderer/page-break-renderer.d.ts +14 -0
- package/dist/renderer/page-break-renderer.js +15 -0
- package/dist/renderer/page-builder-renderer.d.ts +6 -0
- package/dist/renderer/page-builder-renderer.js +11 -0
- package/dist/renderer/page-renderer.d.ts +2 -2
- package/dist/renderer/page-renderer.js +115 -77
- package/dist/renderer/pdf-backend.d.ts +31 -4
- package/dist/renderer/pdf-backend.js +287 -49
- package/dist/renderer/pdf-config.js +4 -7
- package/dist/renderer/pdf-document-class.d.ts +5 -5
- package/dist/renderer/pdf-document-class.js +27 -41
- package/dist/renderer/pdf-document-renderer.d.ts +19 -10
- package/dist/renderer/pdf-document-renderer.js +135 -91
- package/dist/renderer/pdf-renderer.d.ts +2 -2
- package/dist/renderer/pdf-renderer.js +165 -90
- package/dist/renderer/positioned-renderer.d.ts +6 -0
- package/dist/renderer/positioned-renderer.js +10 -0
- package/dist/renderer/rectangle-renderer.d.ts +3 -3
- package/dist/renderer/rectangle-renderer.js +49 -44
- package/dist/renderer/repeating-header-renderer.d.ts +3 -3
- package/dist/renderer/repeating-header-renderer.js +10 -26
- package/dist/renderer/rotated-renderer.d.ts +7 -0
- package/dist/renderer/rotated-renderer.js +29 -0
- package/dist/renderer/row-renderer.d.ts +3 -3
- package/dist/renderer/row-renderer.js +11 -28
- 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 +9 -5
- package/dist/renderer/text-renderer.js +437 -89
- package/dist/text/advance.d.ts +34 -0
- package/dist/text/advance.js +45 -0
- package/dist/text/arabic.d.ts +20 -0
- package/dist/text/arabic.js +101 -0
- package/dist/text/bidi.d.ts +33 -0
- package/dist/text/bidi.js +94 -0
- package/dist/text/emoji-codepoints.d.ts +1 -0
- package/dist/text/emoji-codepoints.js +29 -0
- package/dist/text/font-fallback.d.ts +25 -0
- package/dist/text/font-fallback.js +38 -0
- package/dist/text/line-breaker.d.ts +50 -12
- package/dist/text/line-breaker.js +158 -37
- package/dist/text/line-metrics.d.ts +49 -0
- package/dist/text/line-metrics.js +57 -0
- package/dist/text/orphans-widows.d.ts +27 -0
- package/dist/text/orphans-widows.js +53 -0
- package/dist/text/shape.d.ts +29 -0
- package/dist/text/shape.js +67 -0
- package/dist/text/text-decoration.d.ts +50 -0
- package/dist/text/text-decoration.js +62 -0
- package/dist/text/text-style.d.ts +60 -0
- package/dist/text/text-style.js +79 -0
- package/dist/utils/afm-parser.d.ts +34 -0
- package/dist/utils/afm-parser.js +76 -17
- package/dist/utils/bytes.d.ts +24 -0
- package/dist/utils/bytes.js +76 -0
- package/dist/utils/dest-registry.d.ts +22 -0
- package/dist/utils/dest-registry.js +32 -0
- package/dist/utils/flex-layout.d.ts +36 -9
- package/dist/utils/flex-layout.js +207 -30
- package/dist/utils/font-metrics.d.ts +20 -1
- package/dist/utils/font-metrics.js +1 -2
- package/dist/utils/font-path.js +3 -6
- package/dist/utils/gsub.d.ts +36 -0
- package/dist/utils/gsub.js +178 -0
- package/dist/utils/image-helper.d.ts +6 -5
- package/dist/utils/image-helper.js +101 -111
- package/dist/utils/inflate.d.ts +16 -0
- package/dist/utils/inflate.js +52 -0
- package/dist/utils/md5.d.ts +4 -0
- package/dist/utils/md5.js +79 -0
- package/dist/utils/outline.d.ts +21 -0
- package/dist/utils/outline.js +79 -0
- package/dist/utils/pdf-object-manager.d.ts +110 -8
- package/dist/utils/pdf-object-manager.js +583 -141
- package/dist/utils/renderer-registry.d.ts +13 -1
- package/dist/utils/renderer-registry.js +23 -6
- package/dist/utils/struct-tree.d.ts +44 -0
- package/dist/utils/struct-tree.js +129 -0
- package/dist/utils/ttf-parser.d.ts +134 -2
- package/dist/utils/ttf-parser.js +873 -50
- package/dist/utils/ttf-subsetter.d.ts +1 -1
- package/dist/utils/ttf-subsetter.js +40 -42
- package/dist/utils/ua-xmp.d.ts +9 -0
- package/dist/utils/ua-xmp.js +32 -0
- package/dist/utils/utf8-to-windows1252-encoder.d.ts +6 -0
- package/dist/utils/utf8-to-windows1252-encoder.js +11 -4
- package/dist/utils/woff.d.ts +10 -0
- package/dist/utils/woff.js +116 -0
- package/dist/validators/element-validator.d.ts +2 -2
- package/dist/validators/element-validator.js +31 -26
- package/package.json +22 -2
package/dist/api/structure.d.ts
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
|
-
import { PDFDocumentElement } from "../elements/pdf-document-element";
|
|
2
|
-
import { PageElement } from "../elements/page-element";
|
|
3
|
-
import { PDFElement } from "../elements/pdf-element";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
export type
|
|
1
|
+
import { PDFDocumentElement } from "../elements/pdf-document-element.js";
|
|
2
|
+
import { PageElement } from "../elements/page-element.js";
|
|
3
|
+
import { PDFElement } from "../elements/pdf-element.js";
|
|
4
|
+
import { DefaultTextStyleElement } from "../elements/layout/default-text-style-element.js";
|
|
5
|
+
import type { OverflowPolicy } from "../layout/fragmentation.js";
|
|
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";
|
|
9
|
+
import { StackOptions } from "./layout.js";
|
|
10
|
+
import { type UrlFontSource } from "./font-url.js";
|
|
11
|
+
import { Insets } from "./insets.js";
|
|
12
|
+
import { TextDefaults } from "./text.js";
|
|
13
|
+
/** A custom page size. Use the `mm()` helper for millimetres, or pass points directly. */
|
|
14
|
+
export interface CustomSize {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
unit?: "pt" | "mm";
|
|
18
|
+
}
|
|
19
|
+
/** Millimetres → a custom page size, e.g. a 50×65 mm label: `Page({ size: mm(50, 65) }, …)`. */
|
|
20
|
+
export declare function mm(width: number, height: number): CustomSize;
|
|
21
|
+
/** A page size: a named `PageSize`/string (`"A4"`, `"letter"`, any case), or a `CustomSize`. */
|
|
22
|
+
export type PageSizeInput = PageSize | string | CustomSize;
|
|
9
23
|
export interface PageOptions extends StackOptions {
|
|
10
24
|
/** Page size (default A4). */
|
|
11
25
|
size?: PageSizeInput;
|
|
@@ -24,17 +38,67 @@ export interface PageOptions extends StackOptions {
|
|
|
24
38
|
*/
|
|
25
39
|
export declare function Page(children: PDFElement[]): PageElement;
|
|
26
40
|
export declare function Page(opts: PageOptions, children: PDFElement[]): PageElement;
|
|
27
|
-
|
|
41
|
+
/** Document options: PDF metadata plus the inheritable text defaults (font, size, color, lineHeight,
|
|
42
|
+
* align, bold/italic) every `Text` inherits unless it sets its own - Flutter's `DefaultTextStyle`. */
|
|
43
|
+
/** Where color emoji come from when a code point isn't in the current text font, so `Text("Hi 😅")`
|
|
44
|
+
* works in one string. Either a **font** (native vector, offline - recommended; register it in
|
|
45
|
+
* `fonts`) as a string name or `{ font }`, or an **image/CDN source** `{ url, format }` (react-pdf
|
|
46
|
+
* style: fetches `${url}${hexCodePoint}.${format}`, e.g. a Twemoji CDN). The font source is better
|
|
47
|
+
* (vector, no network); the image source exists for parity + as an escape hatch. */
|
|
48
|
+
export type EmojiSource = string | {
|
|
49
|
+
font: string;
|
|
50
|
+
} | {
|
|
51
|
+
url: string;
|
|
52
|
+
format?: "png";
|
|
53
|
+
};
|
|
54
|
+
export interface DocumentOptions extends TextDefaults {
|
|
28
55
|
/** PDF metadata. */
|
|
29
56
|
meta?: {
|
|
30
57
|
title?: string;
|
|
31
58
|
author?: string;
|
|
32
59
|
};
|
|
60
|
+
/** Color-emoji fallback: emoji code points not in the text font render from this source. */
|
|
61
|
+
emoji?: EmojiSource;
|
|
62
|
+
}
|
|
63
|
+
/** A single font file for `addFont`: a `.ttf` path (read on Node) or its raw bytes (e.g. a browser
|
|
64
|
+
* upload). */
|
|
65
|
+
export type FontFileSource = string | Uint8Array;
|
|
66
|
+
/** A styled font family for `addFont`: one file per style, only `normal` required. `Text({ bold,
|
|
67
|
+
* italic })` then picks the right face, falling back to `normal`. */
|
|
68
|
+
export interface FontFamilyInput {
|
|
69
|
+
normal: FontFileSource;
|
|
70
|
+
bold?: FontFileSource;
|
|
71
|
+
italic?: FontFileSource;
|
|
72
|
+
boldItalic?: FontFileSource;
|
|
73
|
+
}
|
|
74
|
+
/** What `addFont` accepts: one file (path or bytes), or a styled family. */
|
|
75
|
+
export type FontSource = FontFileSource | FontFamilyInput;
|
|
76
|
+
/** The object the `Document(...)` factory returns: the element tree plus a managed font registry. */
|
|
77
|
+
export interface JasyDocument extends PDFDocumentElement {
|
|
78
|
+
/** Register a font under `name`, then use it via `Text({ font: name })`. The source is a `.ttf`
|
|
79
|
+
* path (read now, on Node), raw bytes, or a styled family. Re-adding a name overwrites it.
|
|
80
|
+
* A registered font that no `Text` actually uses is dropped at render and costs nothing. */
|
|
81
|
+
addFont(name: string, source: FontSource): this;
|
|
82
|
+
/** The same, from a URL. Async because the fetch happens NOW, exactly as `addFont` reads a path
|
|
83
|
+
* now - so a broken link fails where you asked for the font, not inside a later render.
|
|
84
|
+
* `await doc.addFontFromUrl("Inter", "https://…/Inter-Regular.ttf")`, or a styled family of URLs. */
|
|
85
|
+
addFontFromUrl(name: string, source: UrlFontSource): Promise<this>;
|
|
86
|
+
/** The names of the registered fonts. */
|
|
87
|
+
getFonts(): string[];
|
|
88
|
+
/** Whether a font is registered under `name`. */
|
|
89
|
+
hasFont(name: string): boolean;
|
|
33
90
|
}
|
|
34
91
|
/** The document root. `Document(pages)` or `Document(opts, pages)`. */
|
|
35
|
-
export declare function Document(pages: PageElement[]):
|
|
36
|
-
export declare function Document(opts: DocumentOptions, pages: PageElement[]):
|
|
37
|
-
|
|
92
|
+
export declare function Document(pages: PageElement[]): JasyDocument;
|
|
93
|
+
export declare function Document(opts: DocumentOptions, pages: PageElement[]): JasyDocument;
|
|
94
|
+
/**
|
|
95
|
+
* Sets default text properties (font/size/color/lineHeight/align/weight) for a whole subtree -
|
|
96
|
+
* Flutter's `DefaultTextStyle`, the per-section counterpart to the `Document` defaults. Children
|
|
97
|
+
* inherit them unless they set their own, and they layer onto whatever is inherited from above.
|
|
98
|
+
* `DefaultTextStyle(opts, children)`.
|
|
99
|
+
*/
|
|
100
|
+
export declare function DefaultTextStyle(opts: TextDefaults, children: PDFElement[]): DefaultTextStyleElement;
|
|
101
|
+
export type FontBytes = Uint8Array;
|
|
38
102
|
/** A font family: one `.ttf` per style. Only `normal` is required; `bold`/`italic`/`boldItalic`
|
|
39
103
|
* are picked up automatically by `Text({ bold, italic })`, falling back to `normal` if absent. */
|
|
40
104
|
export interface FontFamily {
|
|
@@ -73,8 +137,31 @@ export interface RenderOptions {
|
|
|
73
137
|
standardFonts?: boolean;
|
|
74
138
|
/** FlateDecode-compress the streams (default true). Set false for a greppable, uncompressed PDF. */
|
|
75
139
|
compress?: boolean;
|
|
140
|
+
/** Kern the text - ON by default (still labelled experimental while we validate against react-pdf).
|
|
141
|
+
* Emits `TJ` adjustments from the font's own pairs: standard-14 (AFM), embedded `kern` table, and
|
|
142
|
+
* embedded `GPOS` Type 2, so every font path kerns. Set `false` to opt out (byte-identical to the
|
|
143
|
+
* pre-kerning output). Verified against headless Chrome and measured == drawn to the thousandth. */
|
|
144
|
+
kerning?: boolean;
|
|
145
|
+
/** Bake a form field's VALUE into its own appearance stream (`/AP`) - ON by default. Baked values
|
|
146
|
+
* render identically in every viewer and in print, and are what flattening (later) draws from. Set
|
|
147
|
+
* `false` to leave text and choice values to the viewer instead (`/NeedAppearances`, what
|
|
148
|
+
* react-pdf/pdfkit always do). Checkboxes, radios, buttons and signature boxes always carry their
|
|
149
|
+
* own appearance either way - there is no value to defer. Only meaningful with form fields present. */
|
|
150
|
+
fieldAppearances?: boolean;
|
|
151
|
+
/** What to do when content is taller than a page and cannot break: `"error"` throws (default),
|
|
152
|
+
* `"warn"` logs and clips, `"ignore"` clips silently. It is always clipped either way. */
|
|
153
|
+
onOverflow?: OverflowPolicy;
|
|
154
|
+
/** Encrypt the PDF with a password (AES-256, the newest standard). NOT compatible with PDF/A
|
|
155
|
+
* (ZUGFeRD invoices) - encrypting one throws, since PDF/A forbids encryption. */
|
|
156
|
+
encrypt?: EncryptOptions;
|
|
157
|
+
/** Emit an accessible, tagged PDF (structure tree for screen readers). */
|
|
158
|
+
accessible?: boolean;
|
|
159
|
+
/** Document language for accessibility, e.g. `"en-US"` / `"de-DE"` (default `"en-US"`). */
|
|
160
|
+
lang?: string;
|
|
161
|
+
/** Document title (used by accessible readers; also good metadata). */
|
|
162
|
+
title?: string;
|
|
76
163
|
}
|
|
77
164
|
/** Renders a `Document(...)` tree to the raw PDF string. */
|
|
78
165
|
export declare function renderPdf(doc: PDFDocumentElement, options?: RenderOptions): Promise<string>;
|
|
79
166
|
/** Renders a `Document(...)` tree to PDF bytes (e.g. for a download / save dialog). */
|
|
80
|
-
export declare function renderToBytes(doc: PDFDocumentElement, options?: RenderOptions): Promise<Uint8Array
|
|
167
|
+
export declare function renderToBytes(doc: PDFDocumentElement, options?: RenderOptions): Promise<Uint8Array<ArrayBuffer>>;
|
package/dist/api/structure.js
CHANGED
|
@@ -1,125 +1,216 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
const layout_1 = require("./layout");
|
|
24
|
-
const insets_1 = require("./insets");
|
|
25
|
-
const PAGE_SIZE_VALUES = new Set(Object.values(page_sizes_1.PageSize));
|
|
1
|
+
import { readFileBytes } from "../platform/node-fs.js";
|
|
2
|
+
import { PDFDocumentElement } from "../elements/pdf-document-element.js";
|
|
3
|
+
import { PageElement } from "../elements/page-element.js";
|
|
4
|
+
import { DefaultTextStyleElement } from "../elements/layout/default-text-style-element.js";
|
|
5
|
+
import { PageSize, pageFormats } from "../constants/page-sizes.js";
|
|
6
|
+
import { resolveEdges } from "../layout/insets.js";
|
|
7
|
+
import { Orientation } from "../renderer/pdf-config.js";
|
|
8
|
+
import { PDFDocument } from "../renderer/pdf-document-class.js";
|
|
9
|
+
import { FontStyle } from "../utils/pdf-object-manager.js";
|
|
10
|
+
import { getArrayBuffer } from "../utils/utf8-to-windows1252-encoder.js";
|
|
11
|
+
import { createSecurityHandler } from "../crypto/security-handler.js";
|
|
12
|
+
import { uaXmp } from "../utils/ua-xmp.js";
|
|
13
|
+
import { Column } from "./layout.js";
|
|
14
|
+
import { loadFontFromUrl } from "./font-url.js";
|
|
15
|
+
import { toEdges } from "./insets.js";
|
|
16
|
+
import { toTextStyleOverride } from "./text.js";
|
|
17
|
+
const MM_TO_PT = 72 / 25.4; // 1 mm in PDF points
|
|
18
|
+
/** Millimetres → a custom page size, e.g. a 50×65 mm label: `Page({ size: mm(50, 65) }, …)`. */
|
|
19
|
+
export function mm(width, height) {
|
|
20
|
+
return { width, height, unit: "mm" };
|
|
21
|
+
}
|
|
22
|
+
const PAGE_SIZE_VALUES = new Set(Object.values(PageSize));
|
|
26
23
|
function toPageSize(input) {
|
|
27
24
|
const v = String(input).toLowerCase();
|
|
28
25
|
if (!PAGE_SIZE_VALUES.has(v))
|
|
29
26
|
throw new Error(`Unknown page size: "${input}"`);
|
|
30
27
|
return v;
|
|
31
28
|
}
|
|
29
|
+
/** Resolve `size` into config fields: a named `pageSize`, or an explicit [width, height] in points. */
|
|
30
|
+
function resolveSize(input) {
|
|
31
|
+
if (input !== undefined && typeof input === "object") {
|
|
32
|
+
const f = input.unit === "mm" ? MM_TO_PT : 1;
|
|
33
|
+
return { pageSize: PageSize.A4, customSize: [input.width * f, input.height * f] };
|
|
34
|
+
}
|
|
35
|
+
return { pageSize: input !== undefined ? toPageSize(input) : PageSize.A4 };
|
|
36
|
+
}
|
|
32
37
|
/** Default page margin (all sides, points) when a `Page` doesn't set one. */
|
|
33
38
|
const DEFAULT_MARGIN = 56;
|
|
34
|
-
function Page(a, b) {
|
|
35
|
-
var _a;
|
|
39
|
+
export function Page(a, b) {
|
|
36
40
|
const isOpts = !Array.isArray(a);
|
|
37
41
|
const opts = (isOpts ? a : {});
|
|
38
|
-
const children = (isOpts ? (b
|
|
39
|
-
const
|
|
42
|
+
const children = (isOpts ? (b ?? []) : a);
|
|
43
|
+
const size = resolveSize(opts.size);
|
|
44
|
+
const landscape = opts.orientation === "landscape";
|
|
45
|
+
// A percentage margin resolves against the page WIDTH (see `Insets`), which is known right here -
|
|
46
|
+
// the page is the one box whose geometry does not depend on a parent.
|
|
47
|
+
const [w, h] = size.customSize ?? pageFormats[size.pageSize ?? PageSize.A4];
|
|
48
|
+
const pageWidth = landscape ? h : w;
|
|
49
|
+
const [top, right, bottom, left] = resolveEdges(toEdges(opts.margin ?? DEFAULT_MARGIN), pageWidth);
|
|
40
50
|
const config = {
|
|
41
|
-
|
|
42
|
-
orientation:
|
|
51
|
+
...size,
|
|
52
|
+
orientation: landscape ? Orientation.landscape : Orientation.portrait,
|
|
43
53
|
margin: { top, right, bottom, left },
|
|
44
54
|
};
|
|
45
|
-
return new
|
|
55
|
+
return new PageElement({
|
|
46
56
|
config,
|
|
47
57
|
header: opts.header,
|
|
48
58
|
footer: opts.footer,
|
|
49
|
-
children: [
|
|
59
|
+
children: [Column({ gap: opts.gap, justify: opts.justify, align: opts.align }, children)],
|
|
50
60
|
});
|
|
51
61
|
}
|
|
52
62
|
// Document metadata is a document-render concern, not part of the element tree, so it is
|
|
53
63
|
// kept beside the returned element and picked up by `renderPdf`.
|
|
54
64
|
const docMeta = new WeakMap();
|
|
55
|
-
|
|
65
|
+
// Fonts registered via doc.addFont(...), kept beside the element (like meta) and registered on the
|
|
66
|
+
// object manager at render time. Path sources are read to bytes in addFont, so the map holds bytes.
|
|
67
|
+
const docFonts = new WeakMap();
|
|
68
|
+
// The document's color-emoji fallback source, kept beside the element and applied at render time.
|
|
69
|
+
const docEmoji = new WeakMap();
|
|
70
|
+
/** Reads any path sources to bytes, leaving bytes / families as-is. */
|
|
71
|
+
function resolveFontSource(source) {
|
|
72
|
+
const read = (s) => (typeof s === "string" ? readFileBytes(s) : s);
|
|
73
|
+
if (typeof source === "string" || source instanceof Uint8Array) {
|
|
74
|
+
return read(source);
|
|
75
|
+
}
|
|
76
|
+
const family = { normal: read(source.normal) };
|
|
77
|
+
if (source.bold)
|
|
78
|
+
family.bold = read(source.bold);
|
|
79
|
+
if (source.italic)
|
|
80
|
+
family.italic = read(source.italic);
|
|
81
|
+
if (source.boldItalic)
|
|
82
|
+
family.boldItalic = read(source.boldItalic);
|
|
83
|
+
return family;
|
|
84
|
+
}
|
|
85
|
+
export function Document(a, b) {
|
|
56
86
|
const isOpts = !Array.isArray(a);
|
|
57
87
|
const opts = (isOpts ? a : {});
|
|
58
|
-
const pages = (isOpts ? (b
|
|
59
|
-
const doc = new
|
|
88
|
+
const pages = (isOpts ? (b ?? []) : a);
|
|
89
|
+
const doc = new PDFDocumentElement({
|
|
90
|
+
children: pages,
|
|
91
|
+
defaultTextStyle: toTextStyleOverride(opts),
|
|
92
|
+
});
|
|
60
93
|
if (opts.meta)
|
|
61
94
|
docMeta.set(doc, opts.meta);
|
|
95
|
+
if (opts.emoji)
|
|
96
|
+
docEmoji.set(doc, opts.emoji);
|
|
97
|
+
// Managed font registry: addFont registers, getFonts/hasFont query, render reads it (below).
|
|
98
|
+
const registry = new Map();
|
|
99
|
+
docFonts.set(doc, registry);
|
|
100
|
+
doc.addFont = (name, source) => {
|
|
101
|
+
registry.set(name, resolveFontSource(source));
|
|
102
|
+
return doc;
|
|
103
|
+
};
|
|
104
|
+
doc.addFontFromUrl = async (name, source) => {
|
|
105
|
+
registry.set(name, await loadFontFromUrl(source));
|
|
106
|
+
return doc;
|
|
107
|
+
};
|
|
108
|
+
doc.getFonts = () => [...registry.keys()];
|
|
109
|
+
doc.hasFont = (name) => registry.has(name);
|
|
62
110
|
return doc;
|
|
63
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Sets default text properties (font/size/color/lineHeight/align/weight) for a whole subtree -
|
|
114
|
+
* Flutter's `DefaultTextStyle`, the per-section counterpart to the `Document` defaults. Children
|
|
115
|
+
* inherit them unless they set their own, and they layer onto whatever is inherited from above.
|
|
116
|
+
* `DefaultTextStyle(opts, children)`.
|
|
117
|
+
*/
|
|
118
|
+
export function DefaultTextStyle(opts, children) {
|
|
119
|
+
return new DefaultTextStyleElement({ style: toTextStyleOverride(opts), child: Column(children) });
|
|
120
|
+
}
|
|
64
121
|
function isFontBytes(v) {
|
|
65
|
-
return
|
|
122
|
+
return v instanceof Uint8Array;
|
|
66
123
|
}
|
|
67
124
|
/** Renders a `Document(...)` tree to the raw PDF string. */
|
|
68
|
-
function renderPdf(doc, options) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
125
|
+
export async function renderPdf(doc, options) {
|
|
126
|
+
const meta = docMeta.get(doc);
|
|
127
|
+
const config = {
|
|
128
|
+
...(meta ? { metaData: { title: meta.title, author: meta.author, keywords: [] } } : {}),
|
|
129
|
+
...(options?.standardFonts === false ? { registerStandardFonts: false } : {}),
|
|
130
|
+
};
|
|
131
|
+
// Fonts registered via doc.addFont(...) plus any passed in options (options win on a name clash).
|
|
132
|
+
const registered = docFonts.get(doc);
|
|
133
|
+
const fonts = {
|
|
134
|
+
...Object.fromEntries(registered ?? []),
|
|
135
|
+
...options?.fonts,
|
|
136
|
+
};
|
|
137
|
+
const attachments = options?.attachments ?? [];
|
|
138
|
+
// The security handler's key derivation is async (WebCrypto), so build it here, before the sync
|
|
139
|
+
// PDFDocument constructor wires it into the object manager.
|
|
140
|
+
const securityHandler = options?.encrypt
|
|
141
|
+
? await createSecurityHandler(options.encrypt)
|
|
142
|
+
: undefined;
|
|
143
|
+
// A throwaway PDFDocument whose build() yields this tree, reusing the engine's standard
|
|
144
|
+
// font registration + config handling (the constructor does both). Custom fonts are
|
|
145
|
+
// registered here, before layout/render, so both the metrics and the backend see them.
|
|
146
|
+
const Anon = class extends PDFDocument {
|
|
147
|
+
constructor() {
|
|
148
|
+
super(config);
|
|
149
|
+
const om = this.objectManager;
|
|
150
|
+
om.setCompress(options?.compress !== false); // FlateDecode streams by default
|
|
151
|
+
om.setOverflowPolicy(options?.onOverflow ?? "error");
|
|
152
|
+
om.setKerning(options?.kerning !== false); // on by default (experimental); `kerning:false` opts out
|
|
153
|
+
om.acroform.setBakeAppearances(options?.fieldAppearances !== false); // baked unless opted out
|
|
154
|
+
for (const [name, value] of Object.entries(fonts)) {
|
|
155
|
+
if (isFontBytes(value)) {
|
|
156
|
+
om.registerCustomFont(name, value);
|
|
96
157
|
}
|
|
97
|
-
|
|
98
|
-
om.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
158
|
+
else {
|
|
159
|
+
om.registerCustomFont(name, value.normal, FontStyle.Normal);
|
|
160
|
+
if (value.bold)
|
|
161
|
+
om.registerCustomFont(name, value.bold, FontStyle.Bold);
|
|
162
|
+
if (value.italic)
|
|
163
|
+
om.registerCustomFont(name, value.italic, FontStyle.Italic);
|
|
164
|
+
if (value.boldItalic)
|
|
165
|
+
om.registerCustomFont(name, value.boldItalic, FontStyle.BoldItalic);
|
|
103
166
|
}
|
|
104
|
-
if (options === null || options === void 0 ? void 0 : options.xmp)
|
|
105
|
-
om.setXmpMetadata(options.xmp);
|
|
106
|
-
if (options === null || options === void 0 ? void 0 : options.outputIntent)
|
|
107
|
-
om.setOutputIntent(Buffer.from(options.outputIntent));
|
|
108
|
-
if (options === null || options === void 0 ? void 0 : options.pdfVersion)
|
|
109
|
-
om.setPdfVersion(options.pdfVersion);
|
|
110
|
-
if (options === null || options === void 0 ? void 0 : options.documentId)
|
|
111
|
-
om.enableDocumentId();
|
|
112
167
|
}
|
|
113
|
-
|
|
114
|
-
|
|
168
|
+
for (const a of attachments) {
|
|
169
|
+
om.attachFile(a.name, a.data, {
|
|
170
|
+
relationship: a.relationship,
|
|
171
|
+
mimeType: a.mimeType,
|
|
172
|
+
description: a.description,
|
|
173
|
+
});
|
|
115
174
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
175
|
+
// Color-emoji fallback (Document({ emoji })): a font (native vector) or a CDN/image source.
|
|
176
|
+
const emoji = docEmoji.get(doc);
|
|
177
|
+
if (typeof emoji === "string")
|
|
178
|
+
om.setEmojiFont(emoji);
|
|
179
|
+
else if (emoji && "font" in emoji)
|
|
180
|
+
om.setEmojiFont(emoji.font);
|
|
181
|
+
else if (emoji && "url" in emoji)
|
|
182
|
+
om.setEmojiImageSource(emoji.url, emoji.format ?? "png");
|
|
183
|
+
if (options?.xmp)
|
|
184
|
+
om.setXmpMetadata(options.xmp);
|
|
185
|
+
if (options?.outputIntent)
|
|
186
|
+
om.setOutputIntent(options.outputIntent);
|
|
187
|
+
if (options?.pdfVersion)
|
|
188
|
+
om.setPdfVersion(options.pdfVersion);
|
|
189
|
+
if (options?.documentId)
|
|
190
|
+
om.enableDocumentId();
|
|
191
|
+
if (securityHandler)
|
|
192
|
+
om.setSecurityHandler(securityHandler);
|
|
193
|
+
if (options?.accessible) {
|
|
194
|
+
om.struct.enabled = true;
|
|
195
|
+
if (options.lang)
|
|
196
|
+
om.struct.lang = options.lang;
|
|
197
|
+
// Fall back to the document's own title (Document({ meta })) when no explicit title is given.
|
|
198
|
+
const title = options.title ?? meta?.title;
|
|
199
|
+
if (title)
|
|
200
|
+
om.struct.title = title;
|
|
201
|
+
// Declare PDF/UA-1 in the XMP metadata, unless the caller supplied their own packet.
|
|
202
|
+
if (!options.xmp)
|
|
203
|
+
om.setXmpMetadata(uaXmp({ title }));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
build() {
|
|
207
|
+
return doc;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
return Anon.render();
|
|
119
211
|
}
|
|
120
212
|
/** Renders a `Document(...)` tree to PDF bytes (e.g. for a download / save dialog). */
|
|
121
|
-
function renderToBytes(doc, options) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
});
|
|
213
|
+
export async function renderToBytes(doc, options) {
|
|
214
|
+
// Plain ArrayBuffer-backed, so the bytes drop straight into a Blob/Response (BlobPart needs <ArrayBuffer>).
|
|
215
|
+
return new Uint8Array(getArrayBuffer(await renderPdf(doc, options)));
|
|
125
216
|
}
|
package/dist/api/table.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { PDFElement } from "../elements/pdf-element";
|
|
2
|
-
import { Insets } from "./insets";
|
|
3
|
-
import { ColorInput } from "./color";
|
|
1
|
+
import { PDFElement } from "../elements/pdf-element.js";
|
|
2
|
+
import { Insets } from "./insets.js";
|
|
3
|
+
import { ColorInput } from "./color.js";
|
|
4
4
|
/**
|
|
5
5
|
* A column width: a `number` of points (fixed), an `"Nfr"` fraction (splits the leftover
|
|
6
6
|
* width), or `"auto"` (as wide as the widest cell in that column - measured at layout time).
|
|
@@ -21,11 +21,11 @@ export interface TableOptions {
|
|
|
21
21
|
colGap?: number;
|
|
22
22
|
/** Padding inside every cell (default none). */
|
|
23
23
|
cellPadding?: Insets;
|
|
24
|
-
/** Grid line
|
|
24
|
+
/** Grid line color around every cell. Draws the complete grid once - don't add your own
|
|
25
25
|
* frame Box, or the outer edges double up. */
|
|
26
26
|
cellBorder?: ColorInput;
|
|
27
27
|
/** A single thin horizontal rule under the header row and along the foot of the table (e.g. a
|
|
28
|
-
* light-
|
|
28
|
+
* light-gray separator). Independent of `cellBorder`, which draws the full grid. */
|
|
29
29
|
rule?: ColorInput;
|
|
30
30
|
}
|
|
31
31
|
/**
|
package/dist/api/table.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const layout_1 = require("./layout");
|
|
8
|
-
const text_1 = require("./text");
|
|
1
|
+
import { BoxConstraints } from "../layout/box-constraints.js";
|
|
2
|
+
import { RepeatingHeaderElement } from "../elements/layout/repeating-header-element.js";
|
|
3
|
+
import { DeferredElement } from "../elements/layout/deferred-element.js";
|
|
4
|
+
import { StructGroup } from "../elements/layout/struct-group.js";
|
|
5
|
+
import { Column, Row, Box, Expanded, Padding } from "./layout.js";
|
|
6
|
+
import { Text } from "./text.js";
|
|
9
7
|
function asElement(c) {
|
|
10
|
-
return typeof c === "string" ?
|
|
8
|
+
return typeof c === "string" ? Text(c) : c;
|
|
11
9
|
}
|
|
12
10
|
/** `"2fr"` → 2, `"fr"` → 1; not a fraction → null. */
|
|
13
11
|
function fractionOf(col) {
|
|
@@ -18,8 +16,8 @@ function fractionOf(col) {
|
|
|
18
16
|
}
|
|
19
17
|
/** The cell's unbounded (max-content) width - what an `"auto"` column sizes to. */
|
|
20
18
|
function naturalWidth(cell, cellPadding, ctx) {
|
|
21
|
-
const el = cellPadding !== undefined ?
|
|
22
|
-
return el.calculateLayout(
|
|
19
|
+
const el = cellPadding !== undefined ? Padding(cellPadding, asElement(cell)) : asElement(cell);
|
|
20
|
+
return el.calculateLayout(BoxConstraints.loose(Infinity, Infinity), { x: 0, y: 0 }, ctx).width;
|
|
23
21
|
}
|
|
24
22
|
/** Replaces every `"auto"` column with the widest cell width measured down that column. */
|
|
25
23
|
function resolveAutoColumns(opts, rows, ctx) {
|
|
@@ -40,38 +38,52 @@ function resolveAutoColumns(opts, rows, ctx) {
|
|
|
40
38
|
}
|
|
41
39
|
/** Builds the table tree for already-resolved column widths (no `"auto"`). */
|
|
42
40
|
function composeTable(opts, rows, columns) {
|
|
43
|
-
var _a, _b, _c, _d;
|
|
44
41
|
const { cellPadding } = opts;
|
|
45
|
-
const colGap =
|
|
46
|
-
const rowGap =
|
|
42
|
+
const colGap = opts.colGap ?? opts.gap ?? 0;
|
|
43
|
+
const rowGap = opts.rowGap ?? opts.gap ?? 0;
|
|
47
44
|
const cb = opts.cellBorder;
|
|
48
45
|
const rule = opts.rule;
|
|
49
46
|
// The complete grid, once: every cell bottom+right, first row also top, first col also left.
|
|
50
47
|
const borderFor = (firstRow, firstCol) => cb === undefined
|
|
51
48
|
? {}
|
|
52
|
-
:
|
|
49
|
+
: {
|
|
50
|
+
borderBottom: cb,
|
|
51
|
+
borderRight: cb,
|
|
52
|
+
...(firstRow ? { borderTop: cb } : {}),
|
|
53
|
+
...(firstCol ? { borderLeft: cb } : {}),
|
|
54
|
+
};
|
|
53
55
|
// `ruled` rows get just a bottom line (the header underline / the table foot), without the full grid.
|
|
54
|
-
const wrap = (cell, col, firstRow, firstCol, ruled) => {
|
|
55
|
-
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 });
|
|
56
61
|
// The border lives on the wrapper, which stretches to the row height (crisp lines).
|
|
57
|
-
const border =
|
|
62
|
+
const border = {
|
|
63
|
+
...borderFor(firstRow, firstCol),
|
|
64
|
+
...(ruled && rule !== undefined ? { borderBottom: rule } : {}),
|
|
65
|
+
};
|
|
58
66
|
const boxed = cb !== undefined || (ruled && rule !== undefined);
|
|
59
67
|
if (typeof col === "number")
|
|
60
|
-
return
|
|
68
|
+
return Box({ width: col, ...border }, [inner]);
|
|
61
69
|
const fr = fractionOf(col);
|
|
62
70
|
if (fr !== null)
|
|
63
|
-
return
|
|
71
|
+
return Expanded({ flex: fr }, boxed ? Box({ ...border }, [inner]) : inner);
|
|
64
72
|
throw new Error(`Unsupported column width "${col}" - use a number of points or "<n>fr"`);
|
|
65
73
|
};
|
|
66
74
|
// align:stretch → equal-height cells, so a wrapping cell keeps the row's bottom rule straight.
|
|
67
|
-
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
|
+
});
|
|
68
79
|
// The first row (which gets the top border) is the header if present, else body row 0.
|
|
69
80
|
// `rule` underlines the header and the last body row (the table foot).
|
|
70
81
|
const last = rows.length - 1;
|
|
71
|
-
const body =
|
|
72
|
-
|
|
73
|
-
? new
|
|
82
|
+
const body = Column({ gap: rowGap }, rows.map((cells, idx) => buildRow(cells, !opts.header && idx === 0, rule !== undefined && idx === last)));
|
|
83
|
+
const table = opts.header
|
|
84
|
+
? new RepeatingHeaderElement(buildRow(opts.header, true, rule !== undefined, true), body, rowGap)
|
|
74
85
|
: body;
|
|
86
|
+
return new StructGroup({ role: "Table", child: table });
|
|
75
87
|
}
|
|
76
88
|
/**
|
|
77
89
|
* A `Column` of `Row`s. Fixed-point columns sit in a fixed-width `Box`, `"Nfr"` fractions
|
|
@@ -79,9 +91,9 @@ function composeTable(opts, rows, columns) {
|
|
|
79
91
|
* paginates at row boundaries for free (a row that doesn't fit moves whole). `"auto"`
|
|
80
92
|
* columns are resolved at layout time (a `DeferredElement`), once metrics are available.
|
|
81
93
|
*/
|
|
82
|
-
function Table(opts, rows) {
|
|
94
|
+
export function Table(opts, rows) {
|
|
83
95
|
if (opts.columns.some((c) => c === "auto")) {
|
|
84
|
-
return new
|
|
96
|
+
return new DeferredElement((ctx) => composeTable(opts, rows, resolveAutoColumns(opts, rows, ctx)));
|
|
85
97
|
}
|
|
86
98
|
return composeTable(opts, rows, opts.columns);
|
|
87
99
|
}
|