@t4h.framework/pdf 0.0.0

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.
@@ -0,0 +1,310 @@
1
+ ---
2
+ name: framework-pdf
3
+ description: >-
4
+ Guides correct use of @t4h.framework/pdf in T4H Framework workflows: the
5
+ `render()` helper, PDF marker components (Document, Page, View, Text, Link,
6
+ Image, PageNumber), custom fonts, Binary image sources, styling, and the
7
+ FileSystemClaim the render activity requires. Use when generating PDFs in
8
+ workflows, building PDF templates with JSX, registering fonts, or working
9
+ in packages/pdf.
10
+ ---
11
+
12
+ # @t4h.framework/pdf
13
+
14
+ Declarative PDF rendering for workflows, backed by `@react-pdf/renderer`.
15
+ Package path: `framework/packages/pdf`. Peer dependencies:
16
+ `@t4h.framework/core`, `@t4h.framework/fs`. Regular dependencies: `react`,
17
+ `@react-pdf/renderer`.
18
+
19
+ Import surface (from `src/pdf.ts` only — the `helpers/` modules are internal):
20
+
21
+ ```typescript
22
+ import {
23
+ render,
24
+ Document,
25
+ Page,
26
+ View,
27
+ Text,
28
+ Link,
29
+ Image,
30
+ PageNumber,
31
+ RenderPDFActivity,
32
+ type RenderPDFOptions,
33
+ type RenderPDFInput,
34
+ type DocumentProps,
35
+ type PageProps,
36
+ type ViewProps,
37
+ type TextProps,
38
+ type LinkProps,
39
+ type ImageProps,
40
+ type PageNumberProps,
41
+ type DocumentNode,
42
+ type PDFNode,
43
+ type PDFStyle,
44
+ } from '@t4h.framework/pdf'
45
+ ```
46
+
47
+ > Workflows use **`render()`**. The package also exports the underlying
48
+ > `RenderPDFActivity`, but workflow code never imports, instantiates, or
49
+ > extends it.
50
+
51
+ ## Architecture
52
+
53
+ `Workflow` → `render(<Document>…</Document>, options?)`
54
+ (`History.reconciler(RenderPDFActivity)`) →
55
+
56
+ 1. **`toInput`** (workflow side): the JSX tree is serialized into a plain
57
+ `DocumentNode` JSON tree that is stored in the workflow history. User
58
+ function components, fragments, arrays, and conditionals are flattened
59
+ here; only marker components and strings remain.
60
+ 2. **`run`** (activity side): fonts are registered, the `DocumentNode` tree is
61
+ converted back into real `@react-pdf/renderer` elements (`Binary` image
62
+ sources become `data:` URIs), rendered to a buffer, and written through
63
+ **`FileSystemClaim`** at `pdf/${activityId}.pdf`.
64
+ 3. The activity returns the **`Binary`** the claim produced — a reference to
65
+ the stored file, not in-memory bytes. Replay-safe by construction.
66
+
67
+ ---
68
+
69
+ ## Claims
70
+
71
+ ### FileSystemClaim (required)
72
+
73
+ The render activity persists the PDF through `fs`. Any workflow that calls
74
+ `render()` needs a `FileSystemClaim` provider:
75
+
76
+ ```typescript
77
+ { provide: FileSystemClaim, value: new MyFileSystemClaimImpl() }
78
+ ```
79
+
80
+ Tests mock its `write` to return a `Binary` (see **Testing**).
81
+
82
+ ---
83
+
84
+ ## Marker components
85
+
86
+ The components exported by this package are **markers**: they exist only as
87
+ type-safe JSX tags inside a tree passed to `render()`. JSX never invokes
88
+ them — the serializer recognizes them by reference. Calling one directly
89
+ (`Image({ src })`) or rendering them with another renderer throws.
90
+
91
+ | Component | Props (beyond `NodeProps`) | Notes |
92
+ |-----------|---------------------------|-------|
93
+ | `Document` | `title? author? subject? creator? producer? language?` | Root. Children must be `Page` elements |
94
+ | `Page` | `size? orientation? style? wrap? dpi?` | `size` defaults to A4. Overflowing content paginates automatically |
95
+ | `View` | — | Flex container, analogous to a `<div>` |
96
+ | `Text` | — | All raw strings must live inside a `Text` |
97
+ | `Link` | `href` (required) | Hyperlink; serialized `href` becomes react-pdf's `src` internally |
98
+ | `Image` | `src: string \| Binary` (required) | URL, `data:` URI, or `Binary` with an image `contentType` |
99
+ | `PageNumber` | `format?` | Resolves at layout time. Tokens: `{pageNumber}`, `{totalPages}`. Default `'{pageNumber}'`. Combine with `fixed` for running footers |
100
+
101
+ `NodeProps` (shared by every node inside a page): `style`, `fixed` (repeat on
102
+ every page — headers/footers), `break` (force page break before the node),
103
+ `wrap` (allow splitting across pages, default `true`), `minPresenceAhead`
104
+ (move to next page unless this many points remain — avoids orphaned headings).
105
+
106
+ **Tree rules enforced at serialization** (violations throw `TypeError`):
107
+
108
+ - The tree must resolve to a single `Document` root.
109
+ - `Document` children must be `Page` elements.
110
+ - Raw text must be wrapped in `Text` (`"x"` directly inside `View`/`Page` throws).
111
+ - Only `Text`, `Link`, and `PageNumber` are allowed inside `Text`.
112
+ - `Image` is not allowed inside `Text`.
113
+
114
+ User function components, `.map()` arrays, fragments, and conditional
115
+ rendering (`null` / `false` / `undefined`) work exactly like normal React —
116
+ they are evaluated and flattened during serialization.
117
+
118
+ ---
119
+
120
+ ## Styling
121
+
122
+ `PDFStyle` is a loose camelCase record (`fontSize`, `flexDirection`,
123
+ `marginTop`, …). Layout is the CSS **flex model only** — no grid, no float.
124
+ Values: numbers are points; strings accept units (`'2cm'`, `'5mm'`, `'50%'`).
125
+ `style` also accepts an array of styles.
126
+
127
+ Text styles (`color`, `fontSize`, `fontFamily`, …) **inherit** through the
128
+ tree, so defaults belong on `Page`:
129
+
130
+ ```tsx
131
+ <Page size='A4' style={{ padding: '2cm', fontSize: 12, color: '#00513a' }}>
132
+ ```
133
+
134
+ Built-in fonts (no registration needed): Helvetica, Courier, Times-Roman —
135
+ with `fontWeight: 'bold'` / `fontStyle: 'italic'` variants.
136
+
137
+ ---
138
+
139
+ ## Rendering in a workflow
140
+
141
+ ```tsx
142
+ import { Workflow } from '@t4h.framework/core'
143
+ import { Document, Page, PageNumber, Text, View, render } from '@t4h.framework/pdf'
144
+
145
+ function Header({ title }: { title: string }) {
146
+ return (
147
+ <View style={{ borderBottom: '1 solid #333', marginBottom: 16 }}>
148
+ <Text style={{ fontSize: 18 }}>{title}</Text>
149
+ </View>
150
+ )
151
+ }
152
+
153
+ export const invoice = new Workflow({ id: 'invoice' }, async () => {
154
+ const pdf = await render(
155
+ <Document title='Invoice'>
156
+ <Page size='A4' style={{ padding: '2cm', fontSize: 12 }}>
157
+ <Header title='Invoice #42' />
158
+ {items.map(item => (
159
+ <View key={item.id} style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
160
+ <Text>{item.label}</Text>
161
+ <Text>{item.total}</Text>
162
+ </View>
163
+ ))}
164
+ <PageNumber
165
+ fixed
166
+ format='{pageNumber}/{totalPages}'
167
+ style={{ position: 'absolute', bottom: 24, right: '2cm' }}
168
+ />
169
+ </Page>
170
+ </Document>,
171
+ )
172
+
173
+ // pdf is a Binary — pass it to other activities, e.g. an http form upload:
174
+ // await http.post(url, { form: { file: pdf } })
175
+ })
176
+ ```
177
+
178
+ The returned `Binary` exposes `contentType` (`application/pdf`),
179
+ `contentLength`, and `stream()`.
180
+
181
+ ---
182
+
183
+ ## Images
184
+
185
+ `Image src` accepts three shapes:
186
+
187
+ - **URL string** — fetched by react-pdf at layout time. Simple, but the fetch
188
+ happens on every render/replay and cannot authenticate.
189
+ - **`data:` URI string** — for small assets bundled with the app.
190
+ - **`Binary`** — the replay-safe choice for anything private or that must stay
191
+ byte-identical across replays. The bytes are inlined as a `data:` URI on the
192
+ activity side. Typical source: an `http` response body
193
+ (`response.body.binary`) or any other claim-produced `Binary`.
194
+
195
+ Supported formats: PNG and JPEG.
196
+
197
+ ---
198
+
199
+ ## Fonts
200
+
201
+ `render(element, { fonts })` — an object keyed by **family name**. The value
202
+ is either a bare source (registers the regular 400/normal variant) or an
203
+ array of variants:
204
+
205
+ ```tsx
206
+ const pdf = await render(<Doc />, {
207
+ fonts: {
208
+ // bare source: string URL or Binary
209
+ Inter: 'https://cdn.example.com/Inter-Regular.ttf',
210
+ Geometric: fontResponse.body.binary,
211
+
212
+ // multiple weights/styles under one family
213
+ Roboto: [
214
+ { src: robotoRegular.body.binary },
215
+ { src: robotoBold.body.binary, weight: 700 },
216
+ { src: robotoItalic.body.binary, style: 'italic' },
217
+ ],
218
+ },
219
+ })
220
+ ```
221
+
222
+ Select variants in styles with `fontFamily` + `fontWeight` + `fontStyle`.
223
+ Weights are `100–900`; styles `'normal' | 'italic'`.
224
+
225
+ Rules of thumb:
226
+
227
+ - **TTF and WOFF only** — react-pdf cannot parse WOFF2 (Google Fonts links
228
+ often point to WOFF2; pick the TTF URL).
229
+ - Prefer `Binary` for private/licensed fonts and replay stability; a URL is a
230
+ mutable external reference fetched at layout time.
231
+ - Fonts registered per render; same-family re-registration is safe.
232
+
233
+ ---
234
+
235
+ ## Testing
236
+
237
+ Test the **workflow** with `TestWorkflowEnvironment` from
238
+ `@t4h.framework/core/testing`, mocking the single claim the render activity
239
+ uses: `FileSystemClaim`. See the **framework-workflow-testing** skill for the
240
+ harness API.
241
+
242
+ ```typescript
243
+ import { Readable } from 'node:stream'
244
+ import { Binary } from '@t4h.framework/core'
245
+ import { TestWorkflowEnvironment, mockClaim } from '@t4h.framework/core/testing'
246
+ import { FileSystemClaim } from '@t4h.framework/fs'
247
+
248
+ class MockBinary extends Binary {
249
+ public readonly contentType = 'application/pdf'
250
+ public readonly contentLength: number
251
+
252
+ constructor(private readonly data: Buffer) {
253
+ super()
254
+ this.contentLength = data.byteLength
255
+ }
256
+
257
+ public stream() {
258
+ return Readable.from(this.data)
259
+ }
260
+ }
261
+
262
+ const mockWrite = vi.fn().mockImplementation(async (_path, pdf: Buffer) => {
263
+ return new MockBinary(pdf)
264
+ })
265
+
266
+ const env = TestWorkflowEnvironment.create({ now: 0 })
267
+
268
+ await env.execute(workflow, input, {
269
+ claims: [mockClaim(FileSystemClaim, { write: mockWrite, read: vi.fn() })],
270
+ })
271
+
272
+ // The activity hands the rendered buffer to fs.write — assert on it:
273
+ const [path, pdf] = mockWrite.mock.calls[0]
274
+ expect(path).toMatch(/^pdf\/.+\.pdf$/)
275
+ expect(pdf.subarray(0, 5).toString()).toBe('%PDF-')
276
+ ```
277
+
278
+ For helper-level tests (serialization, element mapping, fonts), see
279
+ `src/helpers/__tests__/` — they assert on the `DocumentNode` JSON shape and on
280
+ react-pdf element trees without rendering.
281
+
282
+ ---
283
+
284
+ ## Checklist for agents
285
+
286
+ 1. In workflows, **always** use **`render()`** — never define, extend, or
287
+ hand-instantiate `RenderPDFActivity`.
288
+ 2. Build templates with the **marker components from this package** — never
289
+ import `Document`/`Page`/etc. from `@react-pdf/renderer` in workflow code,
290
+ and never call a marker as a function.
291
+ 3. Wrap every raw string in **`Text`**; only `Text`/`Link`/`PageNumber` go
292
+ inside `Text`; `Document` children are `Page` only.
293
+ 4. Ensure the runtime provides **`FileSystemClaim`** (or mock it in tests) —
294
+ the rendered PDF is persisted through it and returned as a `Binary`.
295
+ 5. Prefer **`Binary`** sources for images and fonts that must be private or
296
+ replay-stable; URLs are fetched at layout time on every render.
297
+ 6. Fonts: object keyed by family, **TTF/WOFF only**, select variants via
298
+ `fontFamily`/`fontWeight`/`fontStyle`.
299
+ 7. Style with the **flex model** (`PDFStyle`, camelCase, points or unit
300
+ strings); put inheritable text defaults on `Page`.
301
+ 8. Use `fixed` + absolute positioning for running headers/footers and
302
+ `PageNumber` for pagination stamps.
303
+ 9. Test workflows with **`TestWorkflowEnvironment`** + **`mockClaim`**,
304
+ asserting the `%PDF-` magic bytes on the `fs.write` call.
305
+ 10. Do not document or import APIs not exported from `@t4h.framework/pdf`
306
+ (the `helpers/` modules are internal).
307
+
308
+ See also: **framework-workflow-testing** for the test harness;
309
+ **framework-fs** for `FileSystemClaim`; **framework-http** for fetching
310
+ `Binary` assets (images, fonts) used in templates.
package/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # @t4h.framework/pdf
2
+
3
+ Declarative PDF rendering for workflows. Build documents with JSX components, render them replay-safely through the workflow history, and get back a `Binary` persisted via the filesystem claim. Backed by [`@react-pdf/renderer`](https://react-pdf.org).
4
+
5
+ ## Claim
6
+
7
+ ### FileSystemClaim
8
+
9
+ The render activity persists the generated PDF through `FileSystemClaim` (from `@t4h.framework/fs`) and returns the resulting `Binary`. The runtime must provide a concrete implementation.
10
+
11
+ ```typescript
12
+ import { FileSystemClaim } from '@t4h.framework/fs'
13
+
14
+ // the runtime provides this:
15
+ // { provide: FileSystemClaim, value: new MyFileSystemClaimImpl() }
16
+ ```
17
+
18
+ ## Rendering
19
+
20
+ ### render
21
+
22
+ Renders a JSX tree to a PDF `Binary` through the workflow history. The tree is serialized into the history on the workflow side and rendered on the activity side, so the step is replay-safe.
23
+
24
+ ```tsx
25
+ import { Workflow } from '@t4h.framework/core'
26
+ import { Document, Page, Text, render } from '@t4h.framework/pdf'
27
+
28
+ export const invoice = new Workflow({ id: 'invoice' }, async () => {
29
+ const pdf = await render(
30
+ <Document title='Invoice'>
31
+ <Page size='A4' style={{ padding: '2cm', fontSize: 12 }}>
32
+ <Text>Hello world</Text>
33
+ </Page>
34
+ </Document>,
35
+ )
36
+
37
+ // pdf is a Binary — hand it to other activities, e.g. an upload:
38
+ // await http.post(url, { form: { file: pdf } })
39
+ })
40
+ ```
41
+
42
+ The returned `Binary` exposes `contentType` (`application/pdf`), `contentLength`, and `stream()`.
43
+
44
+ ## Components
45
+
46
+ The components exported by this package are **markers**: type-safe JSX tags that only exist inside a tree passed to `render()`. They are never invoked as functions — calling one directly or rendering it with another renderer throws.
47
+
48
+ | Component | Key props | Description |
49
+ |-----------|-----------|-------------|
50
+ | `Document` | `title`, `author`, `subject`, `language` | Root of the PDF. Children must be `Page` elements |
51
+ | `Page` | `size` (default `A4`), `orientation`, `style`, `dpi` | A page. Overflowing content paginates automatically |
52
+ | `View` | — | Flex container, analogous to a `<div>` |
53
+ | `Text` | — | Text content. All raw strings must live inside a `Text` |
54
+ | `Link` | `href` | Hyperlink |
55
+ | `Image` | `src` | Image from a URL, `data:` URI, or `Binary` |
56
+ | `PageNumber` | `format` | Text resolved to the current page number at layout time |
57
+
58
+ Every node inside a page also accepts: `style`, `fixed` (repeat on every page), `break` (force a page break), `wrap` (allow splitting across pages, default `true`), and `minPresenceAhead`.
59
+
60
+ User function components, `.map()` arrays, fragments, and conditional rendering work exactly like normal React:
61
+
62
+ ```tsx
63
+ function LineItem({ label, value }: { label: string; value: string }) {
64
+ return (
65
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
66
+ <Text>{label}</Text>
67
+ <Text>{value}</Text>
68
+ </View>
69
+ )
70
+ }
71
+
72
+ const pdf = await render(
73
+ <Document>
74
+ <Page size='A4'>
75
+ {items.map(item => (
76
+ <LineItem key={item.id} {...item} />
77
+ ))}
78
+ {showFooter && <Text>Footer</Text>}
79
+ </Page>
80
+ </Document>,
81
+ )
82
+ ```
83
+
84
+ Tree rules enforced at serialization (violations throw):
85
+
86
+ - The tree must resolve to a single `Document` root, and `Document` children must be `Page` elements.
87
+ - Raw text must be wrapped in `Text`.
88
+ - Only `Text`, `Link`, and `PageNumber` are allowed inside `Text`.
89
+
90
+ ## Styling
91
+
92
+ Styles are camelCase records following the CSS **flex model** (no grid, no float). Numbers are points; strings accept units (`'2cm'`, `'5mm'`, `'50%'`). Text styles inherit through the tree, so defaults belong on `Page`.
93
+
94
+ ```tsx
95
+ <Page size='A4' style={{ padding: '2cm', fontSize: 12, color: '#00513a' }}>
96
+ <View style={{ flexDirection: 'row', gap: 8 }}>
97
+ <Text style={{ fontWeight: 'bold' }}>Bold</Text>
98
+ <Text style={[{ fontSize: 10 }, { color: '#666' }]}>Small and gray</Text>
99
+ </View>
100
+ </Page>
101
+ ```
102
+
103
+ The standard PDF fonts (Helvetica, Courier, Times-Roman) are available with no registration, including `bold` and `italic` variants.
104
+
105
+ ## Images
106
+
107
+ `Image src` accepts a URL, a `data:` URI, or a `Binary` whose `contentType` is an image type (PNG or JPEG). Prefer `Binary` for anything private or that must stay byte-identical across replays — URLs are fetched at layout time on every render.
108
+
109
+ ```tsx
110
+ const logo = await http.get('https://cdn.example.com/logo.png')
111
+
112
+ <Image src={logo.body.binary} style={{ width: 120 }} />
113
+ ```
114
+
115
+ ## Fonts
116
+
117
+ Register custom fonts per render with an object keyed by family name. Values are a bare source — a URL string or a `Binary` — for the regular variant, or an array of variants with `weight` / `style`. TTF and WOFF only (no WOFF2).
118
+
119
+ ```tsx
120
+ const pdf = await render(<Doc />, {
121
+ fonts: {
122
+ Inter: 'https://cdn.example.com/Inter-Regular.ttf',
123
+ Roboto: [
124
+ { src: robotoRegular.body.binary },
125
+ { src: robotoBold.body.binary, weight: 700 },
126
+ { src: robotoItalic.body.binary, style: 'italic' },
127
+ ],
128
+ },
129
+ })
130
+ ```
131
+
132
+ Select variants in styles with `fontFamily`, `fontWeight` (`100–900`), and `fontStyle` (`'normal' | 'italic'`).
133
+
134
+ ## Page numbers
135
+
136
+ `PageNumber` resolves at layout time. The `format` string may reference `{pageNumber}` and `{totalPages}`; combine with `fixed` and absolute positioning for running footers.
137
+
138
+ ```tsx
139
+ <PageNumber
140
+ fixed
141
+ format='{pageNumber}/{totalPages}'
142
+ style={{ position: 'absolute', bottom: 24, right: '2cm' }}
143
+ />
144
+ ```
145
+
146
+ ## Testing
147
+
148
+ Test workflows with `TestWorkflowEnvironment` from `@t4h.framework/core/testing`, mocking `FileSystemClaim` — the rendered buffer is handed to `fs.write`, so assert on the `%PDF-` magic bytes there:
149
+
150
+ ```typescript
151
+ import { TestWorkflowEnvironment, mockClaim } from '@t4h.framework/core/testing'
152
+ import { FileSystemClaim } from '@t4h.framework/fs'
153
+
154
+ const mockWrite = vi.fn().mockImplementation(async (_path, pdf) => new MockBinary(pdf))
155
+
156
+ const env = TestWorkflowEnvironment.create({ now: 0 })
157
+
158
+ await env.execute(workflow, input, {
159
+ claims: [mockClaim(FileSystemClaim, { write: mockWrite, read: vi.fn() })],
160
+ })
161
+
162
+ expect(mockWrite.mock.calls[0][1].subarray(0, 5).toString()).toBe('%PDF-')
163
+ ```
@@ -0,0 +1,19 @@
1
+ import { Binary, SyncActivity } from '@t4h.framework/core';
2
+ import type { ReactElement } from 'react';
3
+ import { type Fonts } from '../helpers/register-fonts.js';
4
+ import type { DocumentNode } from '../typings/pdf.js';
5
+ export type RenderPDFOptions = {
6
+ fonts?: Fonts;
7
+ };
8
+ export type RenderPDFInput = {
9
+ document: DocumentNode;
10
+ fonts?: Fonts;
11
+ };
12
+ export declare class RenderPDFActivity extends SyncActivity<readonly [element: ReactElement, options?: RenderPDFOptions], RenderPDFInput, Binary, Binary> {
13
+ private readonly claims;
14
+ toInput(element: ReactElement, options?: RenderPDFOptions): RenderPDFInput;
15
+ run(input: RenderPDFInput): Promise<Binary>;
16
+ toOutput(output: Binary): Binary;
17
+ }
18
+ export declare function render(element: ReactElement, options?: RenderPDFOptions): Promise<Binary>;
19
+ //# sourceMappingURL=RenderPDFActivity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RenderPDFActivity.d.ts","sourceRoot":"","sources":["../../src/activities/RenderPDFActivity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAkB,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAE1E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AAIzC,OAAO,EAAiB,KAAK,KAAK,EAAE,MAAM,8BAA8B,CAAA;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,YAAY,CAAA;IACtB,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,CAAA;AAED,qBAAa,iBAAkB,SAAQ,YAAY,CACjD,SAAS,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC,EAC5D,cAAc,EACd,MAAM,EACN,MAAM,CACP;IACC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqC;IAErD,OAAO,CACZ,OAAO,EAAE,YAAY,EACrB,OAAO,CAAC,EAAE,gBAAgB,GACzB,cAAc;IAOJ,GAAG,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAUjD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;CAGxC;AAED,wBAAsB,MAAM,CAC1B,OAAO,EAAE,YAAY,EACrB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,MAAM,CAAC,CAEjB"}
@@ -0,0 +1,29 @@
1
+ import { renderToBuffer } from '@react-pdf/renderer';
2
+ import { Binary, Claim, History, SyncActivity } from '@t4h.framework/core';
3
+ import { FileSystemClaim } from '@t4h.framework/fs';
4
+ import { documentToElement } from '../helpers/document-to-element.js';
5
+ import { elementToDocument } from '../helpers/element-to-document.js';
6
+ import { registerFonts } from '../helpers/register-fonts.js';
7
+ export class RenderPDFActivity extends SyncActivity {
8
+ claims = new Claim({ fs: FileSystemClaim });
9
+ toInput(element, options) {
10
+ return {
11
+ document: elementToDocument(element),
12
+ ...(options?.fonts ? { fonts: options.fonts } : {}),
13
+ };
14
+ }
15
+ async run(input) {
16
+ if (input.fonts)
17
+ await registerFonts(input.fonts);
18
+ const element = await documentToElement(input.document);
19
+ const pdf = await renderToBuffer(element);
20
+ return await this.claims.fs.write(`pdf/${this.id}.pdf`, pdf);
21
+ }
22
+ toOutput(output) {
23
+ return output;
24
+ }
25
+ }
26
+ export async function render(element, options) {
27
+ return await History.reconciler(RenderPDFActivity, element, options);
28
+ }
29
+ //# sourceMappingURL=RenderPDFActivity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RenderPDFActivity.js","sourceRoot":"","sources":["../../src/activities/RenderPDFActivity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAGnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAA;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAA;AACrE,OAAO,EAAE,aAAa,EAAc,MAAM,8BAA8B,CAAA;AAYxE,MAAM,OAAO,iBAAkB,SAAQ,YAKtC;IACkB,MAAM,GAAG,IAAI,KAAK,CAAC,EAAE,EAAE,EAAE,eAAe,EAAE,CAAC,CAAA;IAErD,OAAO,CACZ,OAAqB,EACrB,OAA0B;QAE1B,OAAO;YACL,QAAQ,EAAE,iBAAiB,CAAC,OAAO,CAAC;YACpC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACpD,CAAA;IACH,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,KAAqB;QACpC,IAAI,KAAK,CAAC,KAAK;YAAE,MAAM,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QAEjD,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;QAEvD,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAA;QAEzC,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;IAC9D,CAAC;IAEM,QAAQ,CAAC,MAAc;QAC5B,OAAO,MAAM,CAAA;IACf,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,OAAqB,EACrB,OAA0B;IAE1B,OAAO,MAAM,OAAO,CAAC,UAAU,CAAC,iBAAiB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;AACtE,CAAC"}
@@ -0,0 +1,50 @@
1
+ import type { Binary } from '@t4h.framework/core';
2
+ import type { ReactElement, ReactNode } from 'react';
3
+ import type { DocumentNode, NodeProps, PageNode } from './typings/pdf.js';
4
+ export type DocumentProps = NonNullable<DocumentNode['props']> & {
5
+ children?: ReactNode;
6
+ };
7
+ export type PageProps = NonNullable<PageNode['props']> & {
8
+ children?: ReactNode;
9
+ };
10
+ export type ViewProps = NodeProps & {
11
+ children?: ReactNode;
12
+ };
13
+ export type TextProps = NodeProps & {
14
+ children?: ReactNode;
15
+ };
16
+ export type LinkProps = NodeProps & {
17
+ href: string;
18
+ children?: ReactNode;
19
+ };
20
+ export type ImageProps = NodeProps & {
21
+ src: string | Binary;
22
+ };
23
+ export type PageNumberProps = NodeProps & {
24
+ /**
25
+ * May reference `{pageNumber}` and `{totalPages}`. Defaults to
26
+ * `'{pageNumber}'`.
27
+ */
28
+ format?: string;
29
+ };
30
+ /** Root of a PDF. Children must be {@link Page} elements. */
31
+ export declare function Document(props: DocumentProps): ReactElement;
32
+ /** A page. Content overflowing the page height paginates automatically. */
33
+ export declare function Page(props: PageProps): ReactElement;
34
+ /** A flex container, analogous to a `<div>`. */
35
+ export declare function View(props: ViewProps): ReactElement;
36
+ /** Text content. All raw strings must live inside a {@link Text}. */
37
+ export declare function Text(props: TextProps): ReactElement;
38
+ /** A hyperlink. */
39
+ export declare function Link(props: LinkProps): ReactElement;
40
+ /**
41
+ * An image. `src` may be a URL, a `data:image/...;base64,` URI, or a
42
+ * {@link Binary} whose `contentType` is an image type.
43
+ */
44
+ export declare function Image(props: ImageProps): ReactElement;
45
+ /**
46
+ * Text resolved to the current page number at layout time. Combine with
47
+ * `fixed` for running footers.
48
+ */
49
+ export declare function PageNumber(props: PageNumberProps): ReactElement;
50
+ //# sourceMappingURL=components.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../src/components.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEpD,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAEzE,MAAM,MAAM,aAAa,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG;IAC/D,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAG;IACvD,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,CAAA;AAE5D,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,CAAA;AAE5D,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,CAAA;AAE1E,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG;IAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAA;AAE7D,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG;IACxC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAWD,6DAA6D;AAC7D,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,CAE3D;AAED,2EAA2E;AAC3E,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,CAEnD;AAED,gDAAgD;AAChD,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,CAEnD;AAED,qEAAqE;AACrE,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,CAEnD;AAED,mBAAmB;AACnB,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,CAEnD;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,CAErD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,YAAY,CAE/D"}
@@ -0,0 +1,41 @@
1
+ function marker(name) {
2
+ throw new Error(`<${name}> is a PDF marker component — it only exists inside a document ` +
3
+ 'tree passed to render() and cannot be invoked as a regular component');
4
+ }
5
+ /* eslint-disable @typescript-eslint/no-unused-vars */
6
+ /** Root of a PDF. Children must be {@link Page} elements. */
7
+ export function Document(props) {
8
+ marker('Document');
9
+ }
10
+ /** A page. Content overflowing the page height paginates automatically. */
11
+ export function Page(props) {
12
+ marker('Page');
13
+ }
14
+ /** A flex container, analogous to a `<div>`. */
15
+ export function View(props) {
16
+ marker('View');
17
+ }
18
+ /** Text content. All raw strings must live inside a {@link Text}. */
19
+ export function Text(props) {
20
+ marker('Text');
21
+ }
22
+ /** A hyperlink. */
23
+ export function Link(props) {
24
+ marker('Link');
25
+ }
26
+ /**
27
+ * An image. `src` may be a URL, a `data:image/...;base64,` URI, or a
28
+ * {@link Binary} whose `contentType` is an image type.
29
+ */
30
+ export function Image(props) {
31
+ marker('Image');
32
+ }
33
+ /**
34
+ * Text resolved to the current page number at layout time. Combine with
35
+ * `fixed` for running footers.
36
+ */
37
+ export function PageNumber(props) {
38
+ marker('PageNumber');
39
+ }
40
+ /* eslint-enable @typescript-eslint/no-unused-vars */
41
+ //# sourceMappingURL=components.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.js","sourceRoot":"","sources":["../src/components.ts"],"names":[],"mappings":"AA6BA,SAAS,MAAM,CAAC,IAAY;IAC1B,MAAM,IAAI,KAAK,CACb,IAAI,IAAI,iEAAiE;QACvE,sEAAsE,CACzE,CAAA;AACH,CAAC;AAED,sDAAsD;AAEtD,6DAA6D;AAC7D,MAAM,UAAU,QAAQ,CAAC,KAAoB;IAC3C,MAAM,CAAC,UAAU,CAAC,CAAA;AACpB,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,IAAI,CAAC,KAAgB;IACnC,MAAM,CAAC,MAAM,CAAC,CAAA;AAChB,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,IAAI,CAAC,KAAgB;IACnC,MAAM,CAAC,MAAM,CAAC,CAAA;AAChB,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,IAAI,CAAC,KAAgB;IACnC,MAAM,CAAC,MAAM,CAAC,CAAA;AAChB,CAAC;AAED,mBAAmB;AACnB,MAAM,UAAU,IAAI,CAAC,KAAgB;IACnC,MAAM,CAAC,MAAM,CAAC,CAAA;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,KAAiB;IACrC,MAAM,CAAC,OAAO,CAAC,CAAA;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,KAAsB;IAC/C,MAAM,CAAC,YAAY,CAAC,CAAA;AACtB,CAAC;AAED,qDAAqD"}
@@ -0,0 +1,5 @@
1
+ import { Document } from '@react-pdf/renderer';
2
+ import { type ComponentProps, type ReactElement } from 'react';
3
+ import type { DocumentNode } from '../typings/pdf.js';
4
+ export declare function documentToElement(document: DocumentNode): Promise<ReactElement<ComponentProps<typeof Document>>>;
5
+ //# sourceMappingURL=document-to-element.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-to-element.d.ts","sourceRoot":"","sources":["../../src/helpers/document-to-element.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAiC,MAAM,qBAAqB,CAAA;AAE7E,OAAO,EAAiB,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AAE7E,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,mBAAmB,CAAA;AAyF1B,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,YAAY,GACrB,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAMxD"}
@@ -0,0 +1,52 @@
1
+ import { buffer } from 'node:stream/consumers';
2
+ import { Document, Image, Link, Page, Text, View } from '@react-pdf/renderer';
3
+ import { createElement } from 'react';
4
+ async function resolveImageSrc(src) {
5
+ if (typeof src === 'string')
6
+ return src;
7
+ const data = await buffer(await src.stream());
8
+ return `data:${src.contentType};base64,${data.toString('base64')}`;
9
+ }
10
+ async function toTextChildren(children) {
11
+ if (children === undefined)
12
+ return [];
13
+ if (typeof children === 'string')
14
+ return [children];
15
+ return Promise.all(children.map(child => typeof child === 'string' ? child : nodeToElement(child)));
16
+ }
17
+ const DEFAULT_PAGE_NUMBER_FORMAT = '{pageNumber}';
18
+ async function nodeToElement(node) {
19
+ switch (node.type) {
20
+ case 'view':
21
+ return createElement(View, { ...node.props }, ...(await Promise.all((node.children ?? []).map(nodeToElement))));
22
+ case 'text':
23
+ return createElement(Text, { ...node.props }, ...(await toTextChildren(node.children)));
24
+ case 'link': {
25
+ const { href, ...props } = node.props;
26
+ return createElement(Link, { ...props, src: href }, ...(await toTextChildren(node.children)));
27
+ }
28
+ case 'image': {
29
+ const { src, ...props } = node.props;
30
+ return createElement(Image, {
31
+ ...props,
32
+ src: await resolveImageSrc(src),
33
+ });
34
+ }
35
+ case 'page-number': {
36
+ const { format = DEFAULT_PAGE_NUMBER_FORMAT, ...props } = node.props ?? {};
37
+ return createElement(Text, {
38
+ ...props,
39
+ render: ({ pageNumber, totalPages, }) => format
40
+ .replaceAll('{pageNumber}', String(pageNumber))
41
+ .replaceAll('{totalPages}', String(totalPages ?? '')),
42
+ });
43
+ }
44
+ }
45
+ }
46
+ async function pageToElement(page) {
47
+ return createElement(Page, { ...page.props }, ...(await Promise.all((page.children ?? []).map(nodeToElement))));
48
+ }
49
+ export async function documentToElement(document) {
50
+ return createElement(Document, { ...document.props }, ...(await Promise.all(document.children.map(pageToElement))));
51
+ }
52
+ //# sourceMappingURL=document-to-element.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-to-element.js","sourceRoot":"","sources":["../../src/helpers/document-to-element.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type ReactElement } from 'react';
2
+ import type { DocumentNode } from '../pdf.js';
3
+ export declare function elementToDocument(element: ReactElement): DocumentNode;
4
+ //# sourceMappingURL=element-to-document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-to-document.d.ts","sourceRoot":"","sources":["../../src/helpers/element-to-document.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,YAAY,EAAkB,MAAM,OAAO,CAAA;AAWnE,OAAO,KAAK,EACV,YAAY,EAMb,MAAM,WAAW,CAAA;AA8JlB,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY,CA0BrE"}
@@ -0,0 +1,135 @@
1
+ import { Fragment } from 'react';
2
+ import { Document, Image, Link, Page, PageNumber, Text, View, } from '../components.js';
3
+ const MARKERS = new Set([
4
+ Document,
5
+ Page,
6
+ View,
7
+ Text,
8
+ Link,
9
+ Image,
10
+ PageNumber,
11
+ ]);
12
+ function isElement(node) {
13
+ return (typeof node === 'object' &&
14
+ node !== null &&
15
+ 'type' in node &&
16
+ 'props' in node);
17
+ }
18
+ function resolveChildren(node) {
19
+ const resolved = [];
20
+ const visit = (child) => {
21
+ if (child === null || child === undefined || typeof child === 'boolean')
22
+ return;
23
+ if (typeof child === 'string') {
24
+ resolved.push(child);
25
+ return;
26
+ }
27
+ if (typeof child === 'number' || typeof child === 'bigint') {
28
+ resolved.push(String(child));
29
+ return;
30
+ }
31
+ if (Array.isArray(child))
32
+ return child.forEach(item => visit(item));
33
+ if (isElement(child)) {
34
+ if (child.type === Fragment) {
35
+ visit(child.props.children);
36
+ return;
37
+ }
38
+ if (typeof child.type === 'function' && !MARKERS.has(child.type)) {
39
+ visit(child.type(child.props));
40
+ return;
41
+ }
42
+ resolved.push(child);
43
+ return;
44
+ }
45
+ throw new TypeError('Unsupported node');
46
+ };
47
+ visit(node);
48
+ return resolved;
49
+ }
50
+ function toTextChildren(children) {
51
+ return resolveChildren(children).map((child) => {
52
+ if (typeof child === 'string')
53
+ return child;
54
+ const node = toPDFNode(child);
55
+ if (node.type === 'text')
56
+ return node;
57
+ if (node.type === 'link')
58
+ return node;
59
+ if (node.type === 'page-number')
60
+ return node;
61
+ throw new TypeError(`<${nameOf(child)}> is not allowed inside <Text>`);
62
+ });
63
+ }
64
+ function nameOf(element) {
65
+ return typeof element.type === 'function'
66
+ ? element.type.name
67
+ : String(element.type);
68
+ }
69
+ function toPDFNode(element) {
70
+ const { children, ...props } = element.props;
71
+ switch (element.type) {
72
+ case View:
73
+ return {
74
+ type: 'view',
75
+ props,
76
+ children: resolveChildren(children).map(child => {
77
+ if (typeof child === 'string')
78
+ throw new TypeError(`Raw text ${JSON.stringify(child)} must be wrapped in <Text>`);
79
+ return toPDFNode(child);
80
+ }),
81
+ };
82
+ case Text:
83
+ return {
84
+ type: 'text',
85
+ props,
86
+ children: toTextChildren(children),
87
+ };
88
+ case Link:
89
+ return {
90
+ type: 'link',
91
+ props: props,
92
+ children: toTextChildren(children),
93
+ };
94
+ case Image:
95
+ return { type: 'image', props };
96
+ case PageNumber:
97
+ return { type: 'page-number', props };
98
+ default:
99
+ throw new TypeError(`<${nameOf(element)}> is not allowed inside a <Page>`);
100
+ }
101
+ }
102
+ function toPageNode(element) {
103
+ if (element.type !== Page)
104
+ throw new TypeError(`<Document> children must be <Page> elements, got <${nameOf(element)}>`);
105
+ const { children, ...props } = element.props;
106
+ return {
107
+ type: 'page',
108
+ props,
109
+ children: resolveChildren(children).map(child => {
110
+ if (typeof child === 'string')
111
+ throw new TypeError(`Raw text ${JSON.stringify(child)} must be wrapped in <Text>`);
112
+ return toPDFNode(child);
113
+ }),
114
+ };
115
+ }
116
+ export function elementToDocument(element) {
117
+ const roots = resolveChildren(element);
118
+ const root = roots[0];
119
+ if (root === undefined ||
120
+ roots.length !== 1 ||
121
+ typeof root === 'string' ||
122
+ root.type !== Document)
123
+ throw new TypeError('The PDF tree must resolve to a single <Document> root');
124
+ const { children, ...props } = root.props;
125
+ return {
126
+ type: 'document',
127
+ props,
128
+ children: resolveChildren(children).map(child => {
129
+ if (typeof child === 'string')
130
+ throw new TypeError('Raw text is not allowed directly inside <Document>');
131
+ return toPageNode(child);
132
+ }),
133
+ };
134
+ }
135
+ //# sourceMappingURL=element-to-document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-to-document.js","sourceRoot":"","sources":["../../src/helpers/element-to-document.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { Binary } from '@t4h.framework/core';
2
+ export type FontWeight = 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
3
+ export type FontStyle = 'normal' | 'italic';
4
+ export type FontSource = string | Binary;
5
+ export type FontVariant = {
6
+ src: FontSource;
7
+ weight?: FontWeight;
8
+ style?: FontStyle;
9
+ };
10
+ export type Fonts = {
11
+ [name: string]: FontSource | readonly FontVariant[];
12
+ };
13
+ export declare function registerFonts(raw: Fonts): Promise<void>;
14
+ //# sourceMappingURL=register-fonts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-fonts.d.ts","sourceRoot":"","sources":["../../src/helpers/register-fonts.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAE5C,MAAM,MAAM,UAAU,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AAE5E,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE3C,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,CAAA;AAExC,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,UAAU,CAAA;IACf,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,KAAK,GAAG;IAClB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,WAAW,EAAE,CAAA;CACpD,CAAA;AAQD,wBAAsB,aAAa,CAAC,GAAG,EAAE,KAAK,iBA6C7C"}
@@ -0,0 +1,40 @@
1
+ import { buffer } from 'node:stream/consumers';
2
+ import { Font } from '@react-pdf/renderer';
3
+ import { Binary } from '@t4h.framework/core';
4
+ function toDataUrl(data) {
5
+ const bytes = Buffer.isBuffer(data) ? data : Buffer.from(data);
6
+ return `data:font/ttf;base64,${bytes.toString('base64')}`;
7
+ }
8
+ export async function registerFonts(raw) {
9
+ const fonts = await Promise.all(Object.entries(raw ?? {}).flatMap(([name, source]) => {
10
+ const variants = [];
11
+ if (typeof source === 'string' || source instanceof Binary)
12
+ variants.push({ src: source });
13
+ if (Array.isArray(source))
14
+ variants.push(...source);
15
+ return variants.map(async (variant) => {
16
+ const src = typeof variant.src === 'string'
17
+ ? variant.src
18
+ : await buffer(await variant.src.stream());
19
+ return {
20
+ name,
21
+ src,
22
+ weight: variant.weight,
23
+ style: variant.style,
24
+ };
25
+ });
26
+ }));
27
+ const families = new Map();
28
+ for (const font of fonts) {
29
+ const variants = families.get(font.name) ?? [];
30
+ variants.push({
31
+ src: typeof font.src === 'string' ? font.src : toDataUrl(font.src),
32
+ fontWeight: font.weight ?? 400,
33
+ fontStyle: font.style ?? 'normal',
34
+ });
35
+ families.set(font.name, variants);
36
+ }
37
+ for (const [family, variants] of families)
38
+ Font.register({ family, fonts: variants });
39
+ }
40
+ //# sourceMappingURL=register-fonts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-fonts.js","sourceRoot":"","sources":["../../src/helpers/register-fonts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAE9C,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAkB5C,SAAS,SAAS,CAAC,IAA0B;IAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAE9D,OAAO,wBAAwB,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAA;AAC3D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAU;IAC5C,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAC7B,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE;QACnD,MAAM,QAAQ,GAAkB,EAAE,CAAA;QAElC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,YAAY,MAAM;YACxD,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAA;QAEhC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAA;QAEnD,OAAO,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAC,OAAO,EAAC,EAAE;YAClC,MAAM,GAAG,GACP,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ;gBAC7B,CAAC,CAAC,OAAO,CAAC,GAAG;gBACb,CAAC,CAAC,MAAM,MAAM,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA;YAE9C,OAAO;gBACL,IAAI;gBACJ,GAAG;gBACH,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,KAAK,EAAE,OAAO,CAAC,KAAK;aACrB,CAAA;QACH,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CACH,CAAA;IAED,MAAM,QAAQ,GAAG,IAAI,GAAG,EAGrB,CAAA;IAEH,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;QAE9C,QAAQ,CAAC,IAAI,CAAC;YACZ,GAAG,EAAE,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;YAClE,UAAU,EAAE,IAAI,CAAC,MAAM,IAAI,GAAG;YAC9B,SAAS,EAAE,IAAI,CAAC,KAAK,IAAI,QAAQ;SAClC,CAAC,CAAA;QAEF,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IACnC,CAAC;IAED,KAAK,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,QAAQ;QACvC,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAW,CAAC,CAAA;AACvD,CAAC"}
package/dist/pdf.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './activities/RenderPDFActivity.js';
2
+ export * from './components.js';
3
+ export * from './typings/pdf.js';
4
+ //# sourceMappingURL=pdf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf.d.ts","sourceRoot":"","sources":["../src/pdf.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAA;AACjD,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA"}
package/dist/pdf.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from './activities/RenderPDFActivity.js';
2
+ export * from './components.js';
3
+ export * from './typings/pdf.js';
4
+ //# sourceMappingURL=pdf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf.js","sourceRoot":"","sources":["../src/pdf.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAA;AACjD,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA"}
@@ -0,0 +1,85 @@
1
+ import type { Binary } from '@t4h.framework/core';
2
+ export type PDFStyle = {
3
+ [property: string]: string | number | undefined;
4
+ };
5
+ export type PageSize = string | readonly [width: number, height: number];
6
+ /** Props shared by every node inside a page. */
7
+ export type NodeProps = {
8
+ style?: PDFStyle | readonly PDFStyle[];
9
+ /** Repeat on every page (headers/footers). */
10
+ fixed?: boolean;
11
+ /** Force a page break before this node. */
12
+ break?: boolean;
13
+ /** Allow the node to split across pages. Defaults to true. */
14
+ wrap?: boolean;
15
+ /**
16
+ * Move the node to the next page unless at least this many points of space
17
+ * remain below it (avoids orphaned headings).
18
+ */
19
+ minPresenceAhead?: number;
20
+ };
21
+ export type ViewNode = {
22
+ type: 'view';
23
+ props?: NodeProps;
24
+ children?: readonly PDFNode[];
25
+ };
26
+ export type TextNode = {
27
+ type: 'text';
28
+ props?: NodeProps;
29
+ children?: string | readonly TextChild[];
30
+ };
31
+ export type LinkNode = {
32
+ type: 'link';
33
+ props: NodeProps & {
34
+ href: string;
35
+ };
36
+ children?: string | readonly TextChild[];
37
+ };
38
+ /**
39
+ * An image. `src` may be a URL, a `data:image/...;base64,` URI, or a
40
+ * {@link Binary} whose `contentType` is an image type (`image/png`,
41
+ * `image/jpeg`).
42
+ */
43
+ export type ImageNode = {
44
+ type: 'image';
45
+ props: NodeProps & {
46
+ src: string | Binary;
47
+ };
48
+ };
49
+ /**
50
+ * Text that resolves to the current page number at layout time. `format`
51
+ * may reference `{pageNumber}` and `{totalPages}`; defaults to
52
+ * `'{pageNumber}'`. Combine with `fixed: true` for running footers.
53
+ */
54
+ export type PageNumberNode = {
55
+ type: 'page-number';
56
+ props?: NodeProps & {
57
+ format?: string;
58
+ };
59
+ };
60
+ export type TextChild = string | TextNode | LinkNode | PageNumberNode;
61
+ export type PDFNode = ViewNode | TextNode | LinkNode | ImageNode | PageNumberNode;
62
+ export type PageNode = {
63
+ type: 'page';
64
+ props?: {
65
+ size?: PageSize;
66
+ orientation?: 'portrait' | 'landscape';
67
+ style?: PDFStyle | readonly PDFStyle[];
68
+ wrap?: boolean;
69
+ dpi?: number;
70
+ };
71
+ children?: readonly PDFNode[];
72
+ };
73
+ export type DocumentNode = {
74
+ type: 'document';
75
+ props?: {
76
+ title?: string;
77
+ author?: string;
78
+ subject?: string;
79
+ creator?: string;
80
+ producer?: string;
81
+ language?: string;
82
+ };
83
+ children: readonly PageNode[];
84
+ };
85
+ //# sourceMappingURL=pdf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf.d.ts","sourceRoot":"","sources":["../../src/typings/pdf.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pdf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf.js","sourceRoot":"","sources":["../../src/typings/pdf.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@t4h.framework/pdf",
3
+ "version": "0.0.0",
4
+ "description": "Declarative PDF rendering module for the T4H Framework",
5
+ "homepage": "https://github.com/tech4humans-brasil/framework/tree/main/packages/pdf",
6
+ "bugs": "https://github.com/tech4humans-brasil/framework/issues",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/tech4humans-brasil/framework.git",
10
+ "directory": "packages/pdf"
11
+ },
12
+ "license": "MIT",
13
+ "author": "Tech4Humans <contact@tech4h.com.br> (https://tech4h.com.br)",
14
+ "type": "module",
15
+ "exports": {
16
+ "types": "./dist/pdf.d.ts",
17
+ "import": "./dist/pdf.js"
18
+ },
19
+ "module": "./dist/pdf.js",
20
+ "types": "./dist/pdf.d.ts",
21
+ "files": [
22
+ "dist",
23
+ "LICENSE",
24
+ ".ai"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsc --project tsconfig.build.json",
28
+ "check-types": "tsc --noEmit",
29
+ "lint": "eslint .",
30
+ "prepublishOnly": "yarn build",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest"
33
+ },
34
+ "devDependencies": {
35
+ "@t4h.framework/core": "^0.9.0",
36
+ "@t4h.framework/fs": "^0.7.0",
37
+ "@types/react": "^19.2.17",
38
+ "typescript": "^5.9.3",
39
+ "vitest": "^4.0.18"
40
+ },
41
+ "peerDependencies": {
42
+ "@t4h.framework/core": "^0.9.0",
43
+ "@t4h.framework/fs": "^0.7.0"
44
+ },
45
+ "packageManager": "yarn@4.12.0",
46
+ "engines": {
47
+ "node": ">=22"
48
+ },
49
+ "dependencies": {
50
+ "@react-pdf/renderer": "^4.5.1",
51
+ "react": "^19.2.8"
52
+ }
53
+ }