@lupinum/nuxt-pdf 0.4.0-beta.3 → 0.4.0-beta.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/CHANGELOG.md +34 -0
- package/CONFORMANCE.md +10 -4
- package/README.md +28 -0
- package/dist/agent/AGENTS.md +48 -0
- package/dist/agent/manifest.json +215 -0
- package/dist/agent/pages/docs/examples/supported-behavior.md +75 -0
- package/dist/agent/pages/docs/examples.md +163 -0
- package/dist/agent/pages/docs/getting-started/installation.md +90 -0
- package/dist/agent/pages/docs/getting-started/quickstart.md +126 -0
- package/dist/agent/pages/docs/getting-started.md +61 -0
- package/dist/agent/pages/docs/guides/contents-links-bookmarks.md +123 -0
- package/dist/agent/pages/docs/guides/deployment.md +118 -0
- package/dist/agent/pages/docs/guides/errors-and-debugging.md +90 -0
- package/dist/agent/pages/docs/guides/images-and-fonts.md +102 -0
- package/dist/agent/pages/docs/guides/recipes.md +258 -0
- package/dist/agent/pages/docs/guides/remote-images.md +72 -0
- package/dist/agent/pages/docs/guides/reusable-components.md +81 -0
- package/dist/agent/pages/docs/guides/standalone-node.md +107 -0
- package/dist/agent/pages/docs/guides/svg-graphics.md +165 -0
- package/dist/agent/pages/docs/guides/testing.md +119 -0
- package/dist/agent/pages/docs/learn/document-tree.md +91 -0
- package/dist/agent/pages/docs/learn/runtime-and-data.md +63 -0
- package/dist/agent/pages/docs/learn/styling-and-layout.md +79 -0
- package/dist/agent/pages/docs/learn/templates-and-registry.md +96 -0
- package/dist/agent/pages/docs/reference/composables.md +55 -0
- package/dist/agent/pages/docs/reference/define-pdf.md +96 -0
- package/dist/agent/pages/docs/reference/errors-and-limits.md +89 -0
- package/dist/agent/pages/docs/reference/module-options.md +104 -0
- package/dist/agent/pages/docs/reference/page-sizes.md +54 -0
- package/dist/agent/pages/docs/reference/primitives.md +216 -0
- package/dist/agent/pages/docs/reference/registry.md +185 -0
- package/dist/agent/pages/docs/reference/standalone.md +53 -0
- package/dist/agent/pages/docs/reference/styles.md +176 -0
- package/dist/agent/pages/docs/reference/test-utilities.md +124 -0
- package/dist/build.mjs +2 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +14 -11
- package/dist/runtime/components/stubs.d.ts +22 -10
- package/dist/runtime/components/stubs.js +22 -27
- package/dist/runtime/server/assets/resolve-asset.js +2 -40
- package/dist/runtime/server/preview.js +1 -1
- package/dist/shared/{nuxt-pdf.DMC_Rdsz.mjs → nuxt-pdf.C-E8MM_K.mjs} +1 -0
- package/dist/shared/{nuxt-pdf.D3hUPqOJ.mjs → nuxt-pdf.CjVyPF31.mjs} +2 -2
- package/dist/test.mjs +1 -1
- package/package.json +17 -13
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Registry and render result"
|
|
3
|
+
description: "The typed #pdf server module, runtime dispatch, and completed render conversions."
|
|
4
|
+
url: "https://nuxt-pdf.lupinum.com/docs/reference/registry"
|
|
5
|
+
route: "/docs/reference/registry"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Registry and render result
|
|
13
|
+
|
|
14
|
+
> The typed #pdf server module, runtime dispatch, and completed render conversions.
|
|
15
|
+
|
|
16
|
+
Nuxt generates an inspectable `#pdf` module from the discovered templates. It is
|
|
17
|
+
a server-only module for Nitro code and is typed from each SFC's props. Do not
|
|
18
|
+
import it from a client component or other browser code.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { pdf, renderPdf, pdfTemplateKeys } from '#pdf'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## pdf
|
|
25
|
+
|
|
26
|
+
`pdf` is an object keyed by template name. Property access infers each SFC's
|
|
27
|
+
props. Every value has exactly three public members:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
interface PdfTemplate<Props extends object> {
|
|
31
|
+
readonly key: string
|
|
32
|
+
resolveMetadata(props: Props): {
|
|
33
|
+
title?: string
|
|
34
|
+
filename?: string
|
|
35
|
+
language?: string
|
|
36
|
+
}
|
|
37
|
+
render(props: Props): Promise<PdfRenderResult>
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The handle does not expose the compile-time definition, preview data, source
|
|
42
|
+
file, or a second preview render method.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
await pdf.invoice.render({ invoice })
|
|
46
|
+
await pdf.report.render({ report })
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Nested template paths keep their slash keys (for example
|
|
50
|
+
`pdf['reports/monthly']`).
|
|
51
|
+
|
|
52
|
+
## renderPdf
|
|
53
|
+
|
|
54
|
+
`renderPdf(name, props)` is the functional form. A **literal** template name
|
|
55
|
+
infers its props:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
await renderPdf('invoice', { invoice })
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
A runtime string cannot be type-checked, so it requires the explicit
|
|
62
|
+
unknown-props escape hatch:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
await renderPdf(templateName, untypedProps, { unsafe: true })
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
<warning icon="lucide:triangle-alert">
|
|
69
|
+
`{ unsafe: true }` does **not** validate the props; it only makes the loss of
|
|
70
|
+
static typing visible at the call site. An unknown template name still fails with
|
|
71
|
+
`PDF_TEMPLATE_NOT_FOUND`.
|
|
72
|
+
</warning>
|
|
73
|
+
|
|
74
|
+
`pdfTemplateKeys` is the array of discovered template names.
|
|
75
|
+
|
|
76
|
+
`getPdfTemplate(name)` returns that typed three-member handle for a literal name,
|
|
77
|
+
or `undefined` for an unknown name. Reach for it when you need resolved
|
|
78
|
+
`definePdf` metadata without rendering:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { getPdfTemplate } from '#pdf'
|
|
82
|
+
|
|
83
|
+
const template = getPdfTemplate('invoice')
|
|
84
|
+
const { title, filename } = template.resolveMetadata(props)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`resolveMetadata()` evaluates the `title` and `filename` declared by
|
|
88
|
+
`definePdf`, and returns its `language`. It does not mount the component, so an
|
|
89
|
+
authored `PdfDocument` fallback is not part of this result.
|
|
90
|
+
|
|
91
|
+
`NuxtPdfError` and `PDF_ERROR_CODES` are also re-exported from `#pdf`, so
|
|
92
|
+
server code can branch on render failures without extra imports. See
|
|
93
|
+
[Errors and limits](/raw/docs/reference/errors-and-limits.md).
|
|
94
|
+
|
|
95
|
+
## The render result
|
|
96
|
+
|
|
97
|
+
Both `pdf.name.render(props)` and `renderPdf(...)` resolve to a
|
|
98
|
+
`PdfRenderResult`. The document is rendered once; every conversion reuses that
|
|
99
|
+
completed, immutable byte buffer:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const result = await pdf.invoice.render({ invoice })
|
|
103
|
+
|
|
104
|
+
result.metadata // frozen title, filename, and language for this render
|
|
105
|
+
await result.toUint8Array() // Promise<Uint8Array>
|
|
106
|
+
await result.toBuffer() // Promise<Buffer>
|
|
107
|
+
await result.response() // Promise<Response>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`result.metadata` is the frozen `title`, `filename`, and `language` resolved for
|
|
111
|
+
this render: `definePdf` values win when present, otherwise authored
|
|
112
|
+
`PdfDocument` fallbacks. `title` and `language` describe PDF Info fields;
|
|
113
|
+
`filename` is response/download metadata and is not written into PDF Info.
|
|
114
|
+
`result.diagnostics` is one frozen object shared with the development preview:
|
|
115
|
+
|
|
116
|
+
| Field | Meaning |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| `durationMs` | Wall time for the whole public render, including metadata evaluation |
|
|
119
|
+
| `byteLength` | Exact output byte count |
|
|
120
|
+
| `pageCount` | Number of rendered pages |
|
|
121
|
+
| `passes` | Layout passes used (`1` normally, more for a TOC) |
|
|
122
|
+
| `layoutWarnings` | Frozen, structured warnings for layout that rendered but needs attention |
|
|
123
|
+
| `registeredFontFaces` | Frozen family/weight/style facts; never source bytes or paths |
|
|
124
|
+
|
|
125
|
+
Diagnostics contain no document content, props, or resource URLs. An
|
|
126
|
+
application can send them to its metrics layer.
|
|
127
|
+
|
|
128
|
+
`layoutWarnings` is empty for a healthy layout. Nuxt PDF currently reports
|
|
129
|
+
`PDF_UNBREAKABLE_NODE_OVERFLOW` when a non-fixed node cannot wrap and is taller
|
|
130
|
+
than the page's usable content height. The warning identifies the page and
|
|
131
|
+
primitive and includes both heights in points, without copying text or ids from
|
|
132
|
+
the document. Allow the node to wrap or make it small enough to fit on one page.
|
|
133
|
+
|
|
134
|
+
## Development and production boundary
|
|
135
|
+
|
|
136
|
+
The development preview keeps `sampleData`, named `scenarios`, and the relative
|
|
137
|
+
source file outside the public template handle. It calls the same public
|
|
138
|
+
`render(props)` method as application code and displays that completed result.
|
|
139
|
+
There is no separate preview renderer or second document render.
|
|
140
|
+
|
|
141
|
+
Editing a template rebuilds its registry and refreshes the document with its
|
|
142
|
+
diagnostics. The preview keeps the selected scenario.
|
|
143
|
+
|
|
144
|
+
In a production build the SFC transform reconstructs the render definition from
|
|
145
|
+
only `title`, `filename`, `language`, and `maxPasses`. `sampleData` and
|
|
146
|
+
`scenarios` are structurally omitted before Nitro bundles the template; they are
|
|
147
|
+
not public handle properties and are not merely hidden behind runtime checks.
|
|
148
|
+
The development preview data and routes are also absent. Packed-consumer checks
|
|
149
|
+
verify this boundary and keep the renderer and template SFCs out of the client
|
|
150
|
+
output.
|
|
151
|
+
|
|
152
|
+
Metadata is hoisted: inline values and imports are supported, while references
|
|
153
|
+
to bindings declared locally in `<script setup>` are compile errors. Imported
|
|
154
|
+
preview-data modules must be side-effect-free so their unused production imports
|
|
155
|
+
can be eliminated.
|
|
156
|
+
|
|
157
|
+
### response(init)
|
|
158
|
+
|
|
159
|
+
`response()` returns a `Response` with `content-type: application/pdf`, the
|
|
160
|
+
exact `content-length`, and a sanitized, encoded-length-bounded
|
|
161
|
+
`content-disposition` filename:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
await result.response({
|
|
165
|
+
disposition: 'inline', // 'attachment' (default) | 'inline'
|
|
166
|
+
filename: 'invoice.pdf', // sanitized before writing the header
|
|
167
|
+
headers: { 'cache-control': 'no-store' },
|
|
168
|
+
})
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
| Field | Type | Description |
|
|
172
|
+
| --- | --- | --- |
|
|
173
|
+
| `disposition` | `'attachment' \| 'inline'` | `content-disposition` type; defaults to `attachment` |
|
|
174
|
+
| `filename` | `string` | Download filename; sanitized before use; defaults to template metadata or `document.pdf` |
|
|
175
|
+
| `headers` | `HeadersInit` | Extra response headers merged in |
|
|
176
|
+
| `status`, `statusText` | `number` / `string` | Standard `ResponseInit` fields |
|
|
177
|
+
|
|
178
|
+
Caller-provided `content-type`, `content-length`, and `content-disposition`
|
|
179
|
+
headers are replaced with the truthful values from the completed result.
|
|
180
|
+
|
|
181
|
+
## Regenerating the registry
|
|
182
|
+
|
|
183
|
+
The registry is written during module setup and regenerated whenever templates
|
|
184
|
+
change in development. If `#pdf` imports appear untyped in your editor, run
|
|
185
|
+
`nuxt prepare` once to refresh the generated types.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Standalone build and runtime"
|
|
3
|
+
description: "Production registry generation and Node runtime exports outside Nitro."
|
|
4
|
+
url: "https://nuxt-pdf.lupinum.com/docs/reference/standalone"
|
|
5
|
+
route: "/docs/reference/standalone"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Standalone build and runtime
|
|
13
|
+
|
|
14
|
+
> Production registry generation and Node runtime exports outside Nitro.
|
|
15
|
+
|
|
16
|
+
## Build options
|
|
17
|
+
|
|
18
|
+
`@lupinum/nuxt-pdf/build` exports the `buildPdfRegistry` function and the
|
|
19
|
+
`BuildPdfRegistryOptions` type. The function resolves to `Promise<void>`.
|
|
20
|
+
|
|
21
|
+
| Option | Type | Behavior |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| `rootDir` | `string` | Required application root containing `pdfs/`. Relative to the working directory. |
|
|
24
|
+
| `outDir` | `string` | Required dedicated output directory inside `rootDir` and outside `pdfs/`. Relative paths use `rootDir`. An existing directory must carry Nuxt PDF's ownership marker. |
|
|
25
|
+
| `fonts` | `readonly PdfFontDeclaration[]` | Defaults to no custom fonts. Sources resolve under `pdfs/fonts/`. |
|
|
26
|
+
| `limits` | `PdfLimitsOptions` | Existing render and image-admission limits. Omitted fields use the engine defaults. |
|
|
27
|
+
| `remote` | `RemoteAssetOptions` | Existing remote-image policy. Denied by default. |
|
|
28
|
+
|
|
29
|
+
The build rejects unknown options, missing templates, invalid resources, invalid
|
|
30
|
+
templates, and type errors. A successful build emits `index.mjs`, `index.d.mts`,
|
|
31
|
+
and the declarations needed for inferred props. It uses Vue's type checker at
|
|
32
|
+
build time; consumers use ordinary TypeScript. See
|
|
33
|
+
[standalone rendering](/raw/docs/guides/standalone-node.md) for the procedure and
|
|
34
|
+
output-directory replacement rules.
|
|
35
|
+
|
|
36
|
+
## Server exports
|
|
37
|
+
|
|
38
|
+
`@lupinum/nuxt-pdf/server` is a Node-only production entry. It has no Nuxt virtual
|
|
39
|
+
imports or source compiler. Generated registries use `createPdfTemplate` to bind
|
|
40
|
+
a compiled component to its admitted resources, then `createPdfRegistry` to
|
|
41
|
+
assemble named template handles. Application code should use the generated
|
|
42
|
+
registry instead of constructing this resource configuration manually.
|
|
43
|
+
|
|
44
|
+
`PdfRegistryEntries` describes the readonly named handles. `PdfRegistry`
|
|
45
|
+
describes their runtime dispatch. `PdfComponentProps` extracts authored Vue
|
|
46
|
+
props. `PdfDefinition` describes compile-time metadata. `PdfTemplate` and
|
|
47
|
+
`PdfRenderResult` describe the public handle and completed bytes. The entry
|
|
48
|
+
also exports `usePdfPageNumbers`, `NuxtPdfError`, `PDF_ERROR_CODES`, and
|
|
49
|
+
`PdfErrorCode` with their existing behavior.
|
|
50
|
+
|
|
51
|
+
No export sends email, stores a document, authenticates a caller, or selects a
|
|
52
|
+
backend provider. The runtime preserves the current Node requirements and
|
|
53
|
+
[resource limits](/raw/docs/reference/errors-and-limits.md).
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Styles"
|
|
3
|
+
description: "Accepted style keys, values, units, inheritance, and primitive support."
|
|
4
|
+
url: "https://nuxt-pdf.lupinum.com/docs/reference/styles"
|
|
5
|
+
route: "/docs/reference/styles"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Styles
|
|
13
|
+
|
|
14
|
+
> Accepted style keys, values, units, inheritance, and primitive support.
|
|
15
|
+
|
|
16
|
+
`PdfStyle` is Nuxt PDF's framework-owned authoring contract. It
|
|
17
|
+
contains the behavior this package tests and supports; it does not re-export
|
|
18
|
+
the wider upstream stylesheet types and it is not browser CSS.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import type { PdfStyle } from '@lupinum/nuxt-pdf'
|
|
22
|
+
|
|
23
|
+
const pageStyle = {
|
|
24
|
+
color: '#18251d',
|
|
25
|
+
fontFamily: 'Invoice Sans',
|
|
26
|
+
fontSize: 10,
|
|
27
|
+
paddingHorizontal: 48,
|
|
28
|
+
paddingVertical: 40,
|
|
29
|
+
} satisfies PdfStyle
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Use `satisfies`, not a type assertion. It preserves the object's useful
|
|
33
|
+
inferred type while making `vue-tsc` reject unknown keys, invalid enums, and
|
|
34
|
+
unsupported units.
|
|
35
|
+
|
|
36
|
+
`PdfStyle` belongs to page-flow layout and text metrics. SVG paint and geometry
|
|
37
|
+
use direct props: `PdfG` and shapes do not accept `style`, `PdfSvg.style` places
|
|
38
|
+
the SVG root in page flow, and SVG `PdfText.style` controls text metrics while
|
|
39
|
+
its direct `fill` prop controls paint.
|
|
40
|
+
|
|
41
|
+
## Values and units
|
|
42
|
+
|
|
43
|
+
| Type | Accepted values | Notes |
|
|
44
|
+
| --- | --- | --- |
|
|
45
|
+
| `PdfLength` | A number, or `pt`, `px`, `in`, `mm`, `cm`, `rem`, `vh`, or `vw` (for example `12`, `'12pt'`, `'4mm'`) | A unitless number is a PDF point. `px` uses the page DPI; the default is 72. |
|
|
46
|
+
| `PdfPercentage` | A numeric percentage string such as `'50%'` | Accepted only by properties whose type includes percentages. |
|
|
47
|
+
| Color | A color string such as `'#18251d'`, `'rgb(24, 37, 29)'`, or `'red'` | There are no CSS variables or global cascade. |
|
|
48
|
+
| Enum | One of the literals listed below | Browser-only values are rejected even when a similarly named CSS property exists. |
|
|
49
|
+
|
|
50
|
+
`rem` uses the engine's 18-point base. `vw` and `vh` resolve against the PDF
|
|
51
|
+
page container, not a browser window. Percentages resolve against the relevant
|
|
52
|
+
parent dimension.
|
|
53
|
+
|
|
54
|
+
`em`, `ch`, `ex`, `calc()`, `var()`, container queries, and arbitrary CSS
|
|
55
|
+
strings are not part of the contract.
|
|
56
|
+
|
|
57
|
+
## Dimensions and positioning
|
|
58
|
+
|
|
59
|
+
These keys are valid on page-flow primitives (`PdfPage`, `PdfView`, `PdfText`,
|
|
60
|
+
`PdfImage`, `PdfLink`, and `PdfNote`). `width` and `height` also size `PdfSvg`
|
|
61
|
+
inside page flow.
|
|
62
|
+
|
|
63
|
+
| Property | Accepted value | Inherited | Behavior |
|
|
64
|
+
| --- | --- | --- | --- |
|
|
65
|
+
| `width`, `height` | `PdfLength \| PdfPercentage` | No | Sets the resolved box size. |
|
|
66
|
+
| `minWidth`, `minHeight`, `maxWidth`, `maxHeight` | `PdfLength \| PdfPercentage` | No | Constrains the resolved box size. |
|
|
67
|
+
| `position` | `'relative' \| 'absolute'` | No | Use the component `fixed` prop to repeat content across pages. Browser `fixed` and `static` values are not accepted. |
|
|
68
|
+
| `top`, `right`, `bottom`, `left` | `PdfLength \| PdfPercentage` | No | Offsets a positioned node. |
|
|
69
|
+
|
|
70
|
+
## Flex layout
|
|
71
|
+
|
|
72
|
+
Flex properties primarily belong on `PdfPage` and `PdfView`. Text, links,
|
|
73
|
+
images, notes, and SVGs can be flex items. The engine defaults to a `column`
|
|
74
|
+
direction.
|
|
75
|
+
|
|
76
|
+
| Property | Accepted value | Inherited | Behavior |
|
|
77
|
+
| --- | --- | --- | --- |
|
|
78
|
+
| `flexDirection` | `'row' \| 'row-reverse' \| 'column' \| 'column-reverse'` | No | Controls the main axis. The default is `column`. |
|
|
79
|
+
| `flex`, `flexGrow`, `flexShrink` | `number` | No | Controls available-space growth and shrinkage. |
|
|
80
|
+
| `flexBasis` | `PdfLength \| PdfPercentage \| 'auto'` | No | Sets the initial main-axis size. |
|
|
81
|
+
| `flexWrap` | `'nowrap' \| 'wrap' \| 'wrap-reverse'` | No | Controls flex-line wrapping. |
|
|
82
|
+
| `alignItems` | `'flex-start' \| 'flex-end' \| 'center' \| 'stretch' \| 'baseline'` | No | Aligns children on the cross axis. |
|
|
83
|
+
| `justifyContent` | `'flex-start' \| 'flex-end' \| 'center' \| 'space-between' \| 'space-around' \| 'space-evenly'` | No | Distributes children on the main axis. |
|
|
84
|
+
| `gap` | `PdfLength` | No | Adds space between flex children. |
|
|
85
|
+
|
|
86
|
+
There is no grid, browser block layout, floats, selectors, or CSS cascade.
|
|
87
|
+
|
|
88
|
+
## Box model
|
|
89
|
+
|
|
90
|
+
The box model is valid on page-flow primitives. Percentages resolve through
|
|
91
|
+
the engine's PDF layout container, not a browser viewport.
|
|
92
|
+
|
|
93
|
+
| Property | Accepted value | Inherited | Behavior |
|
|
94
|
+
| --- | --- | --- | --- |
|
|
95
|
+
| `margin`, `marginHorizontal`, `marginVertical`, `marginTop`, `marginRight`, `marginBottom`, `marginLeft` | `PdfLength \| PdfPercentage \| 'auto'` | No | Sets space outside the box. |
|
|
96
|
+
| `padding`, `paddingHorizontal`, `paddingVertical`, `paddingTop`, `paddingRight`, `paddingBottom`, `paddingLeft` | `PdfLength \| PdfPercentage` | No | Sets space inside the box. Padding does not accept `auto`. |
|
|
97
|
+
|
|
98
|
+
## Borders and paint
|
|
99
|
+
|
|
100
|
+
| Property | Accepted value | Inherited | Behavior |
|
|
101
|
+
| --- | --- | --- | --- |
|
|
102
|
+
| `backgroundColor` | color string | Only through nested `PdfText` runs | Paints the page-flow box background. |
|
|
103
|
+
| `color` | color string | Yes | Sets text color on text-bearing primitives. |
|
|
104
|
+
| `opacity` | `number` | Yes | Sets node opacity. |
|
|
105
|
+
| `borderWidth`, `borderTopWidth`, `borderRightWidth`, `borderBottomWidth`, `borderLeftWidth` | `PdfLength` | No | Sets all or individual border widths. |
|
|
106
|
+
| `borderColor`, `borderTopColor`, `borderRightColor`, `borderBottomColor`, `borderLeftColor` | color string | No | Sets all or individual border colors. |
|
|
107
|
+
| `borderStyle`, `borderTopStyle`, `borderRightStyle`, `borderBottomStyle`, `borderLeftStyle` | `'solid' \| 'dashed' \| 'dotted'` | No | Sets all or individual border styles. |
|
|
108
|
+
| `borderRadius` | `PdfLength` | No | Page-flow primitives. Percent shorthand is not accepted because the engine requires a resolved number. |
|
|
109
|
+
| `borderTopLeftRadius`, `borderTopRightRadius`, `borderBottomRightRadius`, `borderBottomLeftRadius` | `PdfLength \| PdfPercentage` | No | Page-flow primitives; edge radii are resolved independently. |
|
|
110
|
+
|
|
111
|
+
Full CSS border strings such as `'1px solid red'` are not public authoring
|
|
112
|
+
syntax. Use the explicit width, style, and color keys so invalid values remain
|
|
113
|
+
type-checkable.
|
|
114
|
+
|
|
115
|
+
## Text
|
|
116
|
+
|
|
117
|
+
Text keys apply to `PdfText` and `PdfLink`. They may be set on ancestor
|
|
118
|
+
`PdfPage`/`PdfView` nodes when the table marks them inherited.
|
|
119
|
+
|
|
120
|
+
| Property | Accepted value | Inherited | Behavior |
|
|
121
|
+
| --- | --- | --- | --- |
|
|
122
|
+
| `fontFamily` | `string` | Yes | Selects one registered family or a built-in engine family. |
|
|
123
|
+
| `fontSize` | `PdfLength` | Yes | Sets glyph size. |
|
|
124
|
+
| `fontStyle` | `'normal' \| 'italic' \| 'oblique'` | Yes | Selects the matching registered face. |
|
|
125
|
+
| `fontWeight` | `number` or `thin`, `hairline`, `ultralight`, `extralight`, `light`, `normal`, `medium`, `semibold`, `demibold`, `bold`, `ultrabold`, `extrabold`, `heavy`, `black` | Yes | Selects the matching registered numeric weight. |
|
|
126
|
+
| `letterSpacing` | `PdfLength` | Yes | Adds space between glyphs and affects wrapping. |
|
|
127
|
+
| `lineHeight` | unitless multiplier, `PdfLength`, or `PdfPercentage` | Yes for static text | Static text supports it. Dynamic `render` text uses font-default line spacing; see [Supported behavior](/raw/docs/examples/supported-behavior.md). |
|
|
128
|
+
| `maxLines` | `number` | No | Limits rendered lines. Use `textOverflow: 'ellipsis'` for an ellipsis. |
|
|
129
|
+
| `textAlign` | `'left' \| 'right' \| 'center' \| 'justify'` | Yes | Aligns text inside its line box. |
|
|
130
|
+
| `textDecoration` | `'none' \| 'underline' \| 'line-through'` or both decoration names separated by a space | Yes | Applies one or both decorations. |
|
|
131
|
+
| `textDecorationColor` | color string | No | `PdfText` and `PdfLink`. |
|
|
132
|
+
| `textDecorationStyle` | `'solid' \| 'dashed' \| 'dotted'` | No | `PdfText` and `PdfLink`. |
|
|
133
|
+
| `textOverflow` | `'ellipsis'` | No | Adds an ellipsis when `maxLines` truncates text. |
|
|
134
|
+
| `textTransform` | `'none' \| 'capitalize' \| 'lowercase' \| 'uppercase'` | Yes | Transforms rendered text casing. |
|
|
135
|
+
|
|
136
|
+
`wordSpacing` is not supported. The pinned text layout pipeline accepts
|
|
137
|
+
the key but does not apply it to glyph advances or wrapping. Nuxt PDF does not
|
|
138
|
+
type a no-op as supported authoring; use `letterSpacing` instead.
|
|
139
|
+
|
|
140
|
+
## Images and transforms
|
|
141
|
+
|
|
142
|
+
| Property | Accepted value | Inherited | Behavior |
|
|
143
|
+
| --- | --- | --- | --- |
|
|
144
|
+
| `objectFit` | `'contain' \| 'cover'` | No | Fits a `PdfImage` inside its resolved box. |
|
|
145
|
+
| `transform` | One to three space-separated `rotate(...)`, `scale(...)`, or `translate(...)` operations | No | Applies to page-flow primitives. Rotate accepts degrees or radians. Translate uses unitless PDF points. SVG transforms remain direct SVG props. |
|
|
146
|
+
|
|
147
|
+
## Style arrays
|
|
148
|
+
|
|
149
|
+
`PdfStyleValue` is either one `PdfStyle` or a recursively nested readonly array.
|
|
150
|
+
Arrays merge left-to-right. `false`, `null`, and `undefined` entries are ignored,
|
|
151
|
+
which makes Vue conditionals direct and type-safe:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import type { PdfStyle, PdfStyleValue } from '@lupinum/nuxt-pdf'
|
|
155
|
+
|
|
156
|
+
const base = { fontSize: 10 } satisfies PdfStyle
|
|
157
|
+
const total = { fontWeight: 700 } satisfies PdfStyle
|
|
158
|
+
|
|
159
|
+
const style = [base, [isTotal && total], null] satisfies PdfStyleValue
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Numbers, strings, `true`, functions, and arbitrary objects are not valid array
|
|
163
|
+
entries.
|
|
164
|
+
|
|
165
|
+
## Exported style types
|
|
166
|
+
|
|
167
|
+
Import these types from `@lupinum/nuxt-pdf`:
|
|
168
|
+
|
|
169
|
+
| Export | Describes |
|
|
170
|
+
| --- | --- |
|
|
171
|
+
| `PdfStyle` | One style object |
|
|
172
|
+
| `PdfStyleValue` | One style object or a nested style array |
|
|
173
|
+
| `PdfStyleEntry` | One accepted entry in a style array |
|
|
174
|
+
| `PdfLength` | A point number or supported length string |
|
|
175
|
+
| `PdfPercentage` | A numeric percentage string |
|
|
176
|
+
| `PdfLengthOrPercentage` | A supported length or percentage |
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Test utilities"
|
|
3
|
+
description: "The render, parse, assert, and snapshot exports of @lupinum/nuxt-pdf/test."
|
|
4
|
+
url: "https://nuxt-pdf.lupinum.com/docs/reference/test-utilities"
|
|
5
|
+
route: "/docs/reference/test-utilities"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Test utilities
|
|
13
|
+
|
|
14
|
+
> The render, parse, assert, and snapshot exports of @lupinum/nuxt-pdf/test.
|
|
15
|
+
|
|
16
|
+
`@lupinum/nuxt-pdf/test` exposes the helpers Nuxt PDF is tested with. They are
|
|
17
|
+
runner-agnostic (they throw `PdfAssertionError`, not Vitest/Jest matchers) and
|
|
18
|
+
load `pdfjs-dist` and `@napi-rs/canvas` lazily. See
|
|
19
|
+
[Testing your PDFs](/raw/docs/guides/testing.md) for a worked example.
|
|
20
|
+
|
|
21
|
+
## renderPdfTemplate
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
const { bytes, parsed } = await renderPdfTemplate(Component, props)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Renders a Vue PDF component through the real registry pipeline, with or without
|
|
28
|
+
a `definePdf` block. Returns the raw `bytes`, immutable `result`, and a `parsed`
|
|
29
|
+
document. Options use the same input shapes as `pdf.remote` and `pdf.limits`, so
|
|
30
|
+
partial limits and URL-prefix allowlists can be shared with `nuxt.config`.
|
|
31
|
+
|
|
32
|
+
Use `renderPdfSfc` when the document depends on discovered local images or
|
|
33
|
+
custom font files. Prepared asset maps, embedded font descriptors, and template
|
|
34
|
+
attribution are registry internals and are not public test configuration.
|
|
35
|
+
|
|
36
|
+
## renderPdfSfc
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
const rendered = await renderPdfSfc('./pdfs/invoice.vue', props, {
|
|
40
|
+
fonts: [{ family: 'Invoice Sans', src: 'InvoiceSans-Regular.ttf' }],
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Compiles a real template and its nested child SFCs with the production
|
|
45
|
+
compiler, discovers and validates `pdfs/assets`, and bundles the declared faces
|
|
46
|
+
from `pdfs/fonts`. Its options mirror `pdf.fonts`, `pdf.remote`, and `pdf.limits`;
|
|
47
|
+
the application root and template identity are inferred from the `pdfs/*.vue`
|
|
48
|
+
path. `loadPdfSfc` exposes only the compile step when a test needs the Vue
|
|
49
|
+
component separately.
|
|
50
|
+
|
|
51
|
+
## parsePdf
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const parsed = await parsePdf(input)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Accepts PDF bytes (`Uint8Array`/`ArrayBuffer`/`Buffer`) **or** a
|
|
58
|
+
`PdfRenderResult` straight from the `#pdf` registry. Returns page text, page
|
|
59
|
+
count, flattened link annotations (named destination or external URL), and the
|
|
60
|
+
outline hierarchy with its initial expanded/collapsed state.
|
|
61
|
+
|
|
62
|
+
## expectPdf
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
expectPdf(parsed)
|
|
66
|
+
.toHavePageCount(2)
|
|
67
|
+
.toContainText('Invoice for Acme Corp', { page: 1 })
|
|
68
|
+
.toHaveLink({ destination: 'terms', page: 1 })
|
|
69
|
+
.toHaveLink({ url: 'https://example.com/' })
|
|
70
|
+
.toHaveOutline([{ title: 'Terms', expanded: true }])
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
A chainable expectation that throws `PdfAssertionError` with an actionable
|
|
74
|
+
message on failure.
|
|
75
|
+
|
|
76
|
+
| Method | Checks |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `toHavePageCount(n)` | Page count equals `n` |
|
|
79
|
+
| `toContainText(text, { page? })` | Extracted text contains `text` |
|
|
80
|
+
| `toHaveLink({ destination?, url?, page? })` | A matching link annotation exists |
|
|
81
|
+
| `toHaveOutline(shape)` | The outline matches the title hierarchy and any requested expansion states |
|
|
82
|
+
|
|
83
|
+
## rasterizePdf and comparePdfSnapshot
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
await comparePdfSnapshot(bytes, './test/baselines/invoice')
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`rasterizePdf` renders pages to PNGs; `comparePdfSnapshot` implements the
|
|
90
|
+
reviewed-baseline flow. It writes per-page baselines when
|
|
91
|
+
`UPDATE_PDF_BASELINES=1` (or `{ update: true }`) and otherwise compares within a
|
|
92
|
+
pixel threshold.
|
|
93
|
+
|
|
94
|
+
`parsePdf` also returns page dimensions and normalized `textRuns` with text,
|
|
95
|
+
position, size, font name, direction, width, and height. Use tolerant numeric
|
|
96
|
+
assertions for geometry because font and raster backends can vary slightly
|
|
97
|
+
between supported environments.
|
|
98
|
+
|
|
99
|
+
When a raster comparison fails, the helper checks every page and writes
|
|
100
|
+
`expected`, `actual`, and `diff` PNGs plus a machine-readable `metrics.json` to
|
|
101
|
+
`reports/pdf-snapshots/<baseline-name>`. Pass `{ artifactDir }` to choose a
|
|
102
|
+
different CI artifact directory.
|
|
103
|
+
|
|
104
|
+
## All exports
|
|
105
|
+
|
|
106
|
+
| Export | Kind | Purpose |
|
|
107
|
+
| --- | --- | --- |
|
|
108
|
+
| `renderPdfTemplate` | function | Render a component through the real pipeline |
|
|
109
|
+
| `renderPdfSfc` | function | Compile and render a real nested `pdfs/*.vue` graph |
|
|
110
|
+
| `loadPdfSfc` | function | Compile a PDF SFC graph into a Vue component |
|
|
111
|
+
| `parsePdf` | function | Parse bytes or a render result into a `ParsedPdf` |
|
|
112
|
+
| `rasterizePdf` | function | Render pages to PNG images |
|
|
113
|
+
| `comparePdfSnapshot` | function | Reviewed-baseline raster comparison |
|
|
114
|
+
| `toPdfBytes` | function | Normalize inputs to PDF bytes |
|
|
115
|
+
| `expectPdf` | function | Fluent, runner-agnostic assertions |
|
|
116
|
+
| `PdfAssertionError` | class | Thrown by assertions on failure |
|
|
117
|
+
| `PdfComponentProps`, `RenderedPdfTemplate`, `RenderPdfTemplateOptions`, `RenderPdfSfcOptions` | types | Typed component props and render results/options |
|
|
118
|
+
| `ParsedPdf`, `ParsedPdfPage`, `ParsedPdfTextRun`, `ParsedPdfLink`, `PdfOutlineItem`, `PdfInput`, `PdfPageImage` | types | Parsed PDF structures |
|
|
119
|
+
| `PdfExpectation`, `LinkQuery`, `OutlineShape`, `ToContainTextOptions` | types | Assertion inputs and chain type |
|
|
120
|
+
| `RasterizePdfOptions`, `ComparePdfSnapshotOptions`, `PdfSnapshotResult` | types | Raster and snapshot options/results |
|
|
121
|
+
|
|
122
|
+
`pdfjs-dist` and `@napi-rs/canvas` are optional peer dependencies; install them
|
|
123
|
+
in the project under test. They are absent from the module's production
|
|
124
|
+
dependency graph.
|
package/dist/build.mjs
CHANGED
|
@@ -5,9 +5,9 @@ import { join, relative, dirname, resolve, isAbsolute } from 'node:path';
|
|
|
5
5
|
import { promisify } from 'node:util';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { build } from 'esbuild';
|
|
8
|
-
import { d as discoverPdfTemplates, a as discoverPdfImageFiles, b as bundlePdfFonts, c as compilePdfSfc } from './shared/nuxt-pdf.
|
|
8
|
+
import { d as discoverPdfTemplates, a as discoverPdfImageFiles, b as bundlePdfFonts, c as compilePdfSfc } from './shared/nuxt-pdf.C-E8MM_K.mjs';
|
|
9
9
|
import ts from 'typescript';
|
|
10
|
-
import { p as preparePdfImageAssets, g as generatePdfRuntimeRegistry, a as generateAuthoringTypes, b as generatePdfRegistryTypes } from './shared/nuxt-pdf.
|
|
10
|
+
import { p as preparePdfImageAssets, g as generatePdfRuntimeRegistry, a as generateAuthoringTypes, b as generatePdfRegistryTypes } from './shared/nuxt-pdf.CjVyPF31.mjs';
|
|
11
11
|
import { normalizeRemoteAssetPolicy } from '../dist/runtime/server/assets/remote.js';
|
|
12
12
|
import { normalizePdfLimits, DEFAULT_PDF_RENDER_LIMITS } from '../dist/runtime/server/render-limits.js';
|
|
13
13
|
import 'node:buffer';
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -2,9 +2,9 @@ import { stat } from 'node:fs/promises';
|
|
|
2
2
|
import { join, isAbsolute, resolve, relative } from 'node:path';
|
|
3
3
|
import { defineNuxtModule, createResolver, getLayerDirectories, createIsIgnored, addServerTemplate, addTemplate, addImports, addTypeTemplate, logger, addServerHandler, addComponent } from '@nuxt/kit';
|
|
4
4
|
import { joinURL } from 'ufo';
|
|
5
|
-
import { d as discoverPdfTemplates, e as discoverPdfComponentFiles, a as discoverPdfImageFiles, b as bundlePdfFonts, f as createPdfSfcPlugin, g as classifyPdfWatchEvent } from './shared/nuxt-pdf.
|
|
6
|
-
import { p as preparePdfImageAssets, g as generatePdfRuntimeRegistry, b as generatePdfRegistryTypes, a as generateAuthoringTypes, c as generatePdfPreviewConfig } from './shared/nuxt-pdf.
|
|
7
|
-
import
|
|
5
|
+
import { d as discoverPdfTemplates, e as discoverPdfComponentFiles, a as discoverPdfImageFiles, b as bundlePdfFonts, f as createPdfSfcPlugin, g as classifyPdfWatchEvent } from './shared/nuxt-pdf.C-E8MM_K.mjs';
|
|
6
|
+
import { p as preparePdfImageAssets, g as generatePdfRuntimeRegistry, b as generatePdfRegistryTypes, a as generateAuthoringTypes, c as generatePdfPreviewConfig } from './shared/nuxt-pdf.CjVyPF31.mjs';
|
|
7
|
+
import * as pdfStubs from '../dist/runtime/components/stubs.js';
|
|
8
8
|
import { normalizeRemoteAssetPolicy } from '../dist/runtime/server/assets/remote.js';
|
|
9
9
|
import { normalizePdfLimits, DEFAULT_PDF_RENDER_LIMITS } from '../dist/runtime/server/render-limits.js';
|
|
10
10
|
import 'node:buffer';
|
|
@@ -16,7 +16,7 @@ import 'unimport';
|
|
|
16
16
|
import '../dist/runtime/shared/template.js';
|
|
17
17
|
import '../dist/runtime/server/assets/resolve-asset.js';
|
|
18
18
|
|
|
19
|
-
const version = "0.4.0-beta.
|
|
19
|
+
const version = "0.4.0-beta.5";
|
|
20
20
|
|
|
21
21
|
const existingDirectories = async (directories) => {
|
|
22
22
|
const result = [];
|
|
@@ -143,6 +143,14 @@ const module$1 = defineNuxtModule({
|
|
|
143
143
|
nuxt.hook("vite:serverCreated", (viteServer, environment) => {
|
|
144
144
|
if (environment.isClient) clientViteServer = viteServer;
|
|
145
145
|
});
|
|
146
|
+
const refreshPreview = () => clientViteServer?.ws.send({
|
|
147
|
+
type: "custom",
|
|
148
|
+
event: "nuxt-pdf:update",
|
|
149
|
+
data: {}
|
|
150
|
+
});
|
|
151
|
+
nuxt.hook("nitro:init", (nitro) => {
|
|
152
|
+
nitro.hooks.hook("dev:reload", refreshPreview);
|
|
153
|
+
});
|
|
146
154
|
nuxt.hook("builder:watch", async (event, path) => {
|
|
147
155
|
const absolutePath = isAbsolute(path) ? path : resolve(nuxt.options.srcDir, path);
|
|
148
156
|
const action = classifyPdfWatchEvent(
|
|
@@ -158,12 +166,7 @@ const module$1 = defineNuxtModule({
|
|
|
158
166
|
await nuxt.callHook("restart");
|
|
159
167
|
return;
|
|
160
168
|
}
|
|
161
|
-
if (action
|
|
162
|
-
clientViteServer?.ws.send({
|
|
163
|
-
type: "custom",
|
|
164
|
-
event: "nuxt-pdf:update",
|
|
165
|
-
data: {}
|
|
166
|
-
});
|
|
169
|
+
if (action === "refresh" && /\.(?:png|jpe?g)$/i.test(absolutePath)) refreshPreview();
|
|
167
170
|
});
|
|
168
171
|
addServerTemplate({
|
|
169
172
|
filename: "#pdf-preview-config",
|
|
@@ -182,7 +185,7 @@ const module$1 = defineNuxtModule({
|
|
|
182
185
|
view: { type: "iframe", src: joinURL(nuxt.options.app.baseURL || "/", "_pdf") }
|
|
183
186
|
});
|
|
184
187
|
});
|
|
185
|
-
for (const name of
|
|
188
|
+
for (const name of Object.keys(pdfStubs)) {
|
|
186
189
|
addComponent({
|
|
187
190
|
name,
|
|
188
191
|
export: name,
|