@rastr/pdf-template 0.0.0-stage → 0.1.0-beta.1

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,324 @@
1
+ type Locale = 'cs' | 'en';
2
+ type Align = 'start' | 'center' | 'end';
3
+ type Weight = 400 | 600 | 700;
4
+ interface PageSetup {
5
+ readonly size: 'A4' | 'A3' | 'Letter';
6
+ readonly orientation: 'portrait' | 'landscape';
7
+ /** Top, right, bottom, left in mm. */
8
+ readonly margins: readonly [number, number, number, number];
9
+ readonly unit: 'mm';
10
+ }
11
+ interface FontRef {
12
+ readonly family: string;
13
+ readonly src: string;
14
+ readonly weight?: 400 | 700;
15
+ }
16
+ interface TextStyle {
17
+ readonly font?: string;
18
+ /** pt */
19
+ readonly size?: number;
20
+ readonly weight?: Weight;
21
+ readonly color?: string;
22
+ readonly background?: string;
23
+ }
24
+ type StyleRef = string | TextStyle;
25
+ interface TextElement {
26
+ readonly type: 'text';
27
+ readonly text: string;
28
+ readonly x: number;
29
+ readonly y: number;
30
+ readonly w?: number;
31
+ readonly align?: Align;
32
+ readonly style?: StyleRef;
33
+ }
34
+ interface ImageElement {
35
+ readonly type: 'image';
36
+ readonly src: string;
37
+ readonly x: number;
38
+ readonly y: number;
39
+ readonly w: number;
40
+ readonly h?: number;
41
+ }
42
+ interface LineElement {
43
+ readonly type: 'line';
44
+ readonly x1: number;
45
+ readonly y1: number;
46
+ readonly x2: number;
47
+ readonly y2: number;
48
+ readonly color?: string;
49
+ /** mm */
50
+ readonly width?: number;
51
+ }
52
+ interface RectElement {
53
+ readonly type: 'rect';
54
+ readonly x: number;
55
+ readonly y: number;
56
+ readonly w: number;
57
+ readonly h: number;
58
+ readonly fill?: string;
59
+ readonly stroke?: string;
60
+ readonly strokeWidth?: number;
61
+ }
62
+ type Element = TextElement | ImageElement | LineElement | RectElement;
63
+ interface ColumnOverride {
64
+ /** mm */
65
+ readonly width?: number;
66
+ readonly align?: Align;
67
+ readonly format?: string;
68
+ readonly caption?: string;
69
+ }
70
+ interface TableBand {
71
+ readonly columns: 'fromGrid' | readonly string[];
72
+ readonly overrides?: Readonly<Record<string, ColumnOverride>>;
73
+ readonly header?: {
74
+ readonly background?: string;
75
+ readonly repeatOnEachPage?: boolean;
76
+ readonly style?: StyleRef;
77
+ };
78
+ readonly zebra?: string;
79
+ readonly rowStyles?: readonly {
80
+ readonly when: string;
81
+ readonly style: StyleRef;
82
+ }[];
83
+ /** Validated, not rendered in v1 (grouping is v2). */
84
+ readonly groupHeader?: unknown;
85
+ /** Draws ReportInput.summaryRow as the bold last row of the table. */
86
+ readonly summary?: boolean;
87
+ }
88
+ interface Bands {
89
+ readonly reportHeader?: readonly Element[];
90
+ readonly pageHeader?: readonly Element[];
91
+ readonly table?: TableBand;
92
+ readonly reportFooter?: readonly Element[];
93
+ readonly pageFooter?: readonly Element[];
94
+ }
95
+ interface ReportTemplate {
96
+ readonly version: 1;
97
+ readonly page: PageSetup;
98
+ readonly fonts: readonly FontRef[];
99
+ readonly styles: Readonly<Record<string, TextStyle>>;
100
+ readonly bands: Bands;
101
+ }
102
+
103
+ interface TemplateIssue {
104
+ /** Path in the template, e.g. "bands.pageHeader[1].text". */
105
+ readonly path: string;
106
+ readonly message: string;
107
+ }
108
+ /** An invalid template, expression, condition or font; lists every issue found. */
109
+ declare class TemplateError extends Error {
110
+ readonly issues: readonly TemplateIssue[];
111
+ constructor(issues: readonly TemplateIssue[]);
112
+ }
113
+
114
+ /** Validates a template (e.g. parsed JSON from storage); throws TemplateError listing every issue. */
115
+ declare function validateTemplate(json: unknown): ReportTemplate;
116
+
117
+ interface ExpressionFilter {
118
+ readonly name: string;
119
+ readonly args: readonly string[];
120
+ }
121
+ type ExpressionPart = {
122
+ readonly kind: 'text';
123
+ readonly value: string;
124
+ } | {
125
+ readonly kind: 'expr';
126
+ readonly path: readonly string[];
127
+ readonly filters: readonly ExpressionFilter[];
128
+ };
129
+ /** Splits text into literal parts and {{ path | filter:arg }} expressions; throws TemplateError. */
130
+ declare function parseExpression(text: string): ExpressionPart[];
131
+ declare function evaluateText(parts: readonly ExpressionPart[], scope: unknown, locale: Locale, timeZone: string): string;
132
+
133
+ /** row.field (==|!=|<|<=|>|>=) literal, joined by && (binds tighter) and ||; no parentheses. */
134
+ declare function compileCondition(source: string): (row: Readonly<Record<string, unknown>>) => boolean;
135
+
136
+ /**
137
+ * Formats a value with a spec: currency[:CODE[:decimals]], number[:decimals], percent[:decimals],
138
+ * date, datetime. Values that do not fit the spec are returned as text; null and undefined are empty.
139
+ */
140
+ declare function formatValue(value: unknown, spec: string, locale: Locale, timeZone: string): string;
141
+ /** Text of a value without a format: numbers grouped, booleans Ano/Ne (Yes/No), objects empty. */
142
+ declare function defaultText(value: unknown, locale: Locale): string;
143
+
144
+ /** Horizontal metrics of a TrueType/OpenType font, enough to measure text without shaping. */
145
+ interface FontMetrics {
146
+ readonly unitsPerEm: number;
147
+ readonly ascender: number;
148
+ readonly descender: number;
149
+ /** Glyph for a code point; 0 (.notdef) when the font has none. */
150
+ glyphId(codePoint: number): number;
151
+ advance(glyphId: number): number;
152
+ }
153
+ declare const MM_PER_PT: number;
154
+ /** Reads head, hhea, hmtx and cmap (format 12 preferred, else format 4); kerning and shaping are not used. */
155
+ declare function parseFontMetrics(data: Uint8Array): FontMetrics;
156
+ /** Width of text in mm at a size in pt; code points without a glyph are measured as .notdef and listed. */
157
+ declare function measureText(text: string, metrics: FontMetrics, sizePt: number): {
158
+ readonly widthMm: number;
159
+ readonly missing: readonly number[];
160
+ };
161
+
162
+ interface ReportColumn {
163
+ readonly field: string;
164
+ readonly caption: string;
165
+ /** Width from the grid in mm; null for a flexible column. */
166
+ readonly widthMm: number | null;
167
+ readonly align: Align;
168
+ readonly format?: string;
169
+ }
170
+ interface ReportInput {
171
+ readonly columns: readonly ReportColumn[];
172
+ readonly rows: readonly Readonly<Record<string, unknown>>[];
173
+ /** Grid texts per row and field, used when the column has no format. */
174
+ readonly texts?: readonly Readonly<Record<string, string>>[];
175
+ /** Summary texts per field, drawn as the last table row when the template asks for it (table.summary). */
176
+ readonly summaryRow?: Readonly<Record<string, string>>;
177
+ /** company, user, filter, summary, printedAtUtc (ISO) … */
178
+ readonly context: Readonly<Record<string, unknown>>;
179
+ readonly locale: Locale;
180
+ readonly timeZone: string;
181
+ readonly imageSizes?: Readonly<Record<string, {
182
+ readonly width: number;
183
+ readonly height: number;
184
+ }>>;
185
+ }
186
+ type LayoutItem = {
187
+ readonly kind: 'text';
188
+ readonly x: number;
189
+ readonly y: number;
190
+ readonly baseline: number;
191
+ readonly text: string;
192
+ readonly font: string;
193
+ readonly weight: 400 | 700;
194
+ readonly size: number;
195
+ readonly color: string;
196
+ } | {
197
+ readonly kind: 'rect';
198
+ readonly x: number;
199
+ readonly y: number;
200
+ readonly w: number;
201
+ readonly h: number;
202
+ readonly fill: string | null;
203
+ readonly stroke: string | null;
204
+ readonly strokeWidth: number;
205
+ } | {
206
+ readonly kind: 'line';
207
+ readonly x1: number;
208
+ readonly y1: number;
209
+ readonly x2: number;
210
+ readonly y2: number;
211
+ readonly color: string;
212
+ readonly width: number;
213
+ } | {
214
+ readonly kind: 'image';
215
+ readonly x: number;
216
+ readonly y: number;
217
+ readonly w: number;
218
+ readonly h: number;
219
+ readonly src: string;
220
+ };
221
+ interface LayoutPage {
222
+ readonly items: readonly LayoutItem[];
223
+ }
224
+ interface LayoutDocument {
225
+ readonly page: {
226
+ readonly width: number;
227
+ readonly height: number;
228
+ };
229
+ readonly pages: readonly LayoutPage[];
230
+ readonly fonts: readonly FontRef[];
231
+ readonly warnings: readonly string[];
232
+ }
233
+ /** Half away from zero at 0.01 after 15 significant digits (C#: same formula). */
234
+ declare function round2(value: number): number;
235
+ /** Greedy word wrap at spaces (U+00A0 does not break); a word longer than the line breaks by characters. */
236
+ declare function wrapText(text: string, widthMm: number, metrics: FontMetrics, sizePt: number): string[];
237
+ /** Lays out a report: absolute boxes per page, ready for jsPDF or PDFsharp to draw. */
238
+ declare function layoutReport(template: ReportTemplate, input: ReportInput, fonts: ReadonlyMap<string, FontMetrics>): LayoutDocument;
239
+ /** The same layout, yielding to the event loop every few hundred rows so large reports do not freeze the page. */
240
+ declare function layoutReportAsync(template: ReportTemplate, input: ReportInput, fonts: ReadonlyMap<string, FontMetrics>): Promise<LayoutDocument>;
241
+
242
+ /** A stored template without its content (lists). */
243
+ interface TemplateSummary {
244
+ readonly id: string;
245
+ readonly gridId: string;
246
+ readonly name: string;
247
+ readonly isDefault: boolean;
248
+ /** Visible to the whole company, otherwise only to its author; the server decides what that means. */
249
+ readonly shared: boolean;
250
+ /** ISO 8601 UTC. */
251
+ readonly updatedAtUtc: string;
252
+ }
253
+ interface StoredTemplate extends TemplateSummary {
254
+ /** Validate before use: it may come from an older version or be edited by hand. */
255
+ readonly template: unknown;
256
+ }
257
+ interface TemplateSave {
258
+ /** Without id a new template is created. */
259
+ readonly id?: string;
260
+ readonly gridId: string;
261
+ readonly name: string;
262
+ readonly isDefault: boolean;
263
+ readonly shared: boolean;
264
+ readonly template: ReportTemplate;
265
+ }
266
+ /** Report templates per grid; implemented over localStorage, REST (contracts/wire/pdf-templates.md) or by the application. */
267
+ interface TemplateStore {
268
+ list(gridId: string): Promise<readonly TemplateSummary[]>;
269
+ load(id: string): Promise<StoredTemplate>;
270
+ save(input: TemplateSave): Promise<TemplateSummary>;
271
+ remove(id: string): Promise<void>;
272
+ }
273
+ /** A failed store operation; status is the HTTP status (404 unknown id, 400 invalid template with issues). */
274
+ declare class TemplateStoreError extends Error {
275
+ readonly status: number;
276
+ readonly issues: readonly TemplateIssue[];
277
+ constructor(message: string, status: number, issues?: readonly TemplateIssue[]);
278
+ }
279
+ /** Templates in one localStorage key; keeps at most one default per grid. */
280
+ declare function createLocalTemplateStore(options?: {
281
+ readonly storage?: Storage;
282
+ readonly key?: string;
283
+ readonly now?: () => Date;
284
+ readonly newId?: () => string;
285
+ }): TemplateStore;
286
+ /** Store over the REST contract in contracts/wire/pdf-templates.md. */
287
+ declare function createRestTemplateStore(options: {
288
+ readonly url: string;
289
+ readonly fetch?: typeof fetch;
290
+ readonly headers?: () => HeadersInit;
291
+ }): TemplateStore;
292
+ /** For GridPdfOptions.template: the grid's default template, or null (the built-in look). */
293
+ declare function templateLoader(store: TemplateStore, gridId: string): () => Promise<unknown | null>;
294
+
295
+ /** A grid column the template can use (visible export columns, in grid order). */
296
+ interface PreviewField {
297
+ readonly field: string;
298
+ readonly caption: string;
299
+ /** Width from the grid in mm; null for a flexible column. */
300
+ readonly widthMm: number | null;
301
+ /** Summary types of the column in the grid (sum, avg, min, max, count, custom), offered as summary.<type>.<field>. */
302
+ readonly summaries?: readonly string[];
303
+ }
304
+ interface PreviewImage {
305
+ readonly dataUrl: string;
306
+ readonly width: number;
307
+ readonly height: number;
308
+ }
309
+ /** Everything a live preview needs: sample rows with the export context, fonts and images. */
310
+ interface PreviewData {
311
+ readonly input: ReportInput;
312
+ /** TrueType files by "family|400" / "family|700". */
313
+ readonly fonts: ReadonlyMap<string, Uint8Array>;
314
+ readonly metrics: ReadonlyMap<string, FontMetrics>;
315
+ /** Images by resolved src; missing ones could not be loaded. */
316
+ readonly images: ReadonlyMap<string, PreviewImage>;
317
+ }
318
+ /** Sample data for the template designer; @rastr/export-pdf builds one from a grid (gridPreviewSource). */
319
+ interface PreviewSource {
320
+ fields(): readonly PreviewField[];
321
+ load(template: ReportTemplate, rows: number): Promise<PreviewData>;
322
+ }
323
+
324
+ export { type Align, type Bands, type ColumnOverride, type Element, type ExpressionFilter, type ExpressionPart, type FontMetrics, type FontRef, type ImageElement, type LayoutDocument, type LayoutItem, type LayoutPage, type LineElement, type Locale, MM_PER_PT, type PageSetup, type PreviewData, type PreviewField, type PreviewImage, type PreviewSource, type RectElement, type ReportColumn, type ReportInput, type ReportTemplate, type StoredTemplate, type StyleRef, type TableBand, TemplateError, type TemplateIssue, type TemplateSave, type TemplateStore, TemplateStoreError, type TemplateSummary, type TextElement, type TextStyle, type Weight, compileCondition, createLocalTemplateStore, createRestTemplateStore, defaultText, evaluateText, formatValue, layoutReport, layoutReportAsync, measureText, parseExpression, parseFontMetrics, round2, templateLoader, validateTemplate, wrapText };