@formepdf/react 0.4.4 → 0.5.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.
- package/dist/serialize.d.ts +1 -1
- package/dist/serialize.js +105 -8
- package/dist/types.d.ts +25 -3
- package/package.json +1 -1
package/dist/serialize.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare function serialize(element: ReactElement): FormeDocument;
|
|
|
8
8
|
export declare function mapStyle(style?: Style): FormeStyle;
|
|
9
9
|
export declare function mapDimension(val: number | string): FormeDimension;
|
|
10
10
|
export declare function parseColor(hex: string): FormeColor;
|
|
11
|
-
export declare function expandEdges(val: number | Edges): FormeEdges;
|
|
11
|
+
export declare function expandEdges(val: number | string | number[] | Edges): FormeEdges;
|
|
12
12
|
export declare function expandCorners(val: number | Corners): FormeCornerValues;
|
|
13
13
|
/**
|
|
14
14
|
* Serialize a React element tree into a Forme template JSON document.
|
package/dist/serialize.js
CHANGED
|
@@ -89,6 +89,8 @@ export function serialize(element) {
|
|
|
89
89
|
metadata.subject = props.subject;
|
|
90
90
|
if (props.creator !== undefined)
|
|
91
91
|
metadata.creator = props.creator;
|
|
92
|
+
if (props.lang !== undefined)
|
|
93
|
+
metadata.lang = props.lang;
|
|
92
94
|
// Merge global + document fonts (document fonts override on conflict)
|
|
93
95
|
const mergedFonts = mergeFonts(Font.getRegistered(), props.fonts);
|
|
94
96
|
const result = {
|
|
@@ -296,12 +298,17 @@ function serializeImage(element) {
|
|
|
296
298
|
kind.width = props.width;
|
|
297
299
|
if (props.height !== undefined)
|
|
298
300
|
kind.height = props.height;
|
|
299
|
-
|
|
301
|
+
const node = {
|
|
300
302
|
kind,
|
|
301
303
|
style: mapStyle(props.style),
|
|
302
304
|
children: [],
|
|
303
305
|
sourceLocation: extractSourceLocation(element),
|
|
304
306
|
};
|
|
307
|
+
if (props.href)
|
|
308
|
+
node.href = props.href;
|
|
309
|
+
if (props.alt)
|
|
310
|
+
node.alt = props.alt;
|
|
311
|
+
return node;
|
|
305
312
|
}
|
|
306
313
|
function serializeTable(element, _parent = null) {
|
|
307
314
|
const props = element.props;
|
|
@@ -364,12 +371,17 @@ function serializeSvg(element) {
|
|
|
364
371
|
};
|
|
365
372
|
if (props.viewBox)
|
|
366
373
|
kind.view_box = props.viewBox;
|
|
367
|
-
|
|
374
|
+
const node = {
|
|
368
375
|
kind,
|
|
369
376
|
style: mapStyle(props.style),
|
|
370
377
|
children: [],
|
|
371
378
|
sourceLocation: extractSourceLocation(element),
|
|
372
379
|
};
|
|
380
|
+
if (props.href)
|
|
381
|
+
node.href = props.href;
|
|
382
|
+
if (props.alt)
|
|
383
|
+
node.alt = props.alt;
|
|
384
|
+
return node;
|
|
373
385
|
}
|
|
374
386
|
// ─── Children helpers ────────────────────────────────────────────────
|
|
375
387
|
function flattenChildren(children) {
|
|
@@ -594,9 +606,40 @@ export function mapStyle(style) {
|
|
|
594
606
|
result.backgroundColor = parseColor(style.backgroundColor);
|
|
595
607
|
if (style.opacity !== undefined)
|
|
596
608
|
result.opacity = style.opacity;
|
|
597
|
-
// Border
|
|
598
|
-
|
|
599
|
-
|
|
609
|
+
// Border — cascade: border < borderTop/Right/Bottom/Left < borderWidth/borderColor < borderTopWidth/borderTopColor
|
|
610
|
+
// Step 1: Parse string shorthands into intermediate per-side values
|
|
611
|
+
let shortWidth = { top: undefined, right: undefined, bottom: undefined, left: undefined };
|
|
612
|
+
let shortColor = { top: undefined, right: undefined, bottom: undefined, left: undefined };
|
|
613
|
+
if (style.border !== undefined) {
|
|
614
|
+
const parsed = parseBorderString(style.border);
|
|
615
|
+
if (parsed.width !== undefined)
|
|
616
|
+
shortWidth = { top: parsed.width, right: parsed.width, bottom: parsed.width, left: parsed.width };
|
|
617
|
+
if (parsed.color !== undefined)
|
|
618
|
+
shortColor = { top: parsed.color, right: parsed.color, bottom: parsed.color, left: parsed.color };
|
|
619
|
+
}
|
|
620
|
+
// Per-side string shorthands override all-side shorthand
|
|
621
|
+
for (const [side, prop] of [['top', 'borderTop'], ['right', 'borderRight'], ['bottom', 'borderBottom'], ['left', 'borderLeft']]) {
|
|
622
|
+
const val = style[prop];
|
|
623
|
+
if (val === undefined)
|
|
624
|
+
continue;
|
|
625
|
+
if (typeof val === 'number') {
|
|
626
|
+
shortWidth[side] = val;
|
|
627
|
+
}
|
|
628
|
+
else {
|
|
629
|
+
const parsed = parseBorderString(val);
|
|
630
|
+
if (parsed.width !== undefined)
|
|
631
|
+
shortWidth[side] = parsed.width;
|
|
632
|
+
if (parsed.color !== undefined)
|
|
633
|
+
shortColor[side] = parsed.color;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
// Step 2: Build borderWidth — existing borderWidth/borderTopWidth override shorthands
|
|
637
|
+
const hasBorderWidth = style.borderWidth !== undefined || style.borderTopWidth !== undefined || style.borderRightWidth !== undefined || style.borderBottomWidth !== undefined || style.borderLeftWidth !== undefined;
|
|
638
|
+
const hasShortWidth = shortWidth.top !== undefined || shortWidth.right !== undefined || shortWidth.bottom !== undefined || shortWidth.left !== undefined;
|
|
639
|
+
if (hasBorderWidth || hasShortWidth) {
|
|
640
|
+
const base = style.borderWidth !== undefined
|
|
641
|
+
? expandEdgeValues(style.borderWidth)
|
|
642
|
+
: { top: shortWidth.top ?? 0, right: shortWidth.right ?? 0, bottom: shortWidth.bottom ?? 0, left: shortWidth.left ?? 0 };
|
|
600
643
|
result.borderWidth = {
|
|
601
644
|
top: style.borderTopWidth ?? base.top,
|
|
602
645
|
right: style.borderRightWidth ?? base.right,
|
|
@@ -604,14 +647,22 @@ export function mapStyle(style) {
|
|
|
604
647
|
left: style.borderLeftWidth ?? base.left,
|
|
605
648
|
};
|
|
606
649
|
}
|
|
607
|
-
|
|
650
|
+
// Step 3: Build borderColor — existing borderColor/borderTopColor override shorthands
|
|
651
|
+
const hasBorderColor = style.borderColor !== undefined || style.borderTopColor !== undefined || style.borderRightColor !== undefined || style.borderBottomColor !== undefined || style.borderLeftColor !== undefined;
|
|
652
|
+
const hasShortColor = shortColor.top !== undefined || shortColor.right !== undefined || shortColor.bottom !== undefined || shortColor.left !== undefined;
|
|
653
|
+
if (hasBorderColor || hasShortColor) {
|
|
608
654
|
const defaultColor = parseColor('#000000');
|
|
609
|
-
let base = {
|
|
655
|
+
let base = {
|
|
656
|
+
top: shortColor.top ?? defaultColor,
|
|
657
|
+
right: shortColor.right ?? defaultColor,
|
|
658
|
+
bottom: shortColor.bottom ?? defaultColor,
|
|
659
|
+
left: shortColor.left ?? defaultColor,
|
|
660
|
+
};
|
|
610
661
|
if (typeof style.borderColor === 'string') {
|
|
611
662
|
const c = parseColor(style.borderColor);
|
|
612
663
|
base = { top: c, right: c, bottom: c, left: c };
|
|
613
664
|
}
|
|
614
|
-
else if (style.borderColor) {
|
|
665
|
+
else if (style.borderColor && typeof style.borderColor === 'object') {
|
|
615
666
|
base = {
|
|
616
667
|
top: parseColor(style.borderColor.top),
|
|
617
668
|
right: parseColor(style.borderColor.right),
|
|
@@ -699,10 +750,54 @@ export function parseColor(hex) {
|
|
|
699
750
|
// Fallback: black
|
|
700
751
|
return { r: 0, g: 0, b: 0, a: 1 };
|
|
701
752
|
}
|
|
753
|
+
/**
|
|
754
|
+
* Parse a CSS-style 1-4 value edge shorthand.
|
|
755
|
+
* Accepts: `"8"`, `"8 16"`, `"8 16 24"`, `"8 16 24 32"` (with optional `px` suffix).
|
|
756
|
+
* Also accepts number arrays: `[8]`, `[8, 16]`, `[8, 16, 24]`, `[8, 16, 24, 32]`.
|
|
757
|
+
*/
|
|
758
|
+
function parseCSSEdges(val) {
|
|
759
|
+
const values = Array.isArray(val)
|
|
760
|
+
? val
|
|
761
|
+
: val.trim().split(/\s+/).map(s => parseFloat(s.replace(/px$/i, '')));
|
|
762
|
+
switch (values.length) {
|
|
763
|
+
case 1: return { top: values[0], right: values[0], bottom: values[0], left: values[0] };
|
|
764
|
+
case 2: return { top: values[0], right: values[1], bottom: values[0], left: values[1] };
|
|
765
|
+
case 3: return { top: values[0], right: values[1], bottom: values[2], left: values[1] };
|
|
766
|
+
default: return { top: values[0], right: values[1], bottom: values[2], left: values[3] };
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
const BORDER_STYLE_KEYWORDS = new Set([
|
|
770
|
+
'solid', 'dashed', 'dotted', 'double', 'groove', 'ridge', 'inset', 'outset', 'none', 'hidden',
|
|
771
|
+
]);
|
|
772
|
+
/**
|
|
773
|
+
* Parse a CSS border shorthand string like `"1px solid #000"`.
|
|
774
|
+
* Returns extracted width and/or color. Style keywords are recognized but ignored.
|
|
775
|
+
*/
|
|
776
|
+
function parseBorderString(val) {
|
|
777
|
+
const tokens = val.trim().split(/\s+/);
|
|
778
|
+
let width;
|
|
779
|
+
let color;
|
|
780
|
+
for (const token of tokens) {
|
|
781
|
+
const lower = token.toLowerCase();
|
|
782
|
+
if (BORDER_STYLE_KEYWORDS.has(lower))
|
|
783
|
+
continue;
|
|
784
|
+
const num = parseFloat(lower.replace(/px$/i, ''));
|
|
785
|
+
if (!isNaN(num) && /^[\d.]/.test(lower)) {
|
|
786
|
+
width = num;
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
color = parseColor(token);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
return { width, color };
|
|
793
|
+
}
|
|
702
794
|
export function expandEdges(val) {
|
|
703
795
|
if (typeof val === 'number') {
|
|
704
796
|
return { top: val, right: val, bottom: val, left: val };
|
|
705
797
|
}
|
|
798
|
+
if (typeof val === 'string' || Array.isArray(val)) {
|
|
799
|
+
return parseCSSEdges(val);
|
|
800
|
+
}
|
|
706
801
|
return { top: val.top, right: val.right, bottom: val.bottom, left: val.left };
|
|
707
802
|
}
|
|
708
803
|
function expandEdgeValues(val) {
|
|
@@ -809,6 +904,8 @@ export function serializeTemplate(element) {
|
|
|
809
904
|
metadata.subject = processTemplateValue(props.subject);
|
|
810
905
|
if (props.creator !== undefined)
|
|
811
906
|
metadata.creator = processTemplateValue(props.creator);
|
|
907
|
+
if (props.lang !== undefined)
|
|
908
|
+
metadata.lang = processTemplateValue(props.lang);
|
|
812
909
|
const mergedFonts = mergeFonts(Font.getRegistered(), props.fonts);
|
|
813
910
|
const result = {
|
|
814
911
|
children,
|
package/dist/types.d.ts
CHANGED
|
@@ -42,14 +42,14 @@ export interface Style {
|
|
|
42
42
|
gap?: number;
|
|
43
43
|
rowGap?: number;
|
|
44
44
|
columnGap?: number;
|
|
45
|
-
padding?: number | Edges;
|
|
45
|
+
padding?: number | string | number[] | Edges;
|
|
46
46
|
paddingTop?: number;
|
|
47
47
|
paddingRight?: number;
|
|
48
48
|
paddingBottom?: number;
|
|
49
49
|
paddingLeft?: number;
|
|
50
50
|
paddingHorizontal?: number;
|
|
51
51
|
paddingVertical?: number;
|
|
52
|
-
margin?: number | Edges;
|
|
52
|
+
margin?: number | string | number[] | Edges;
|
|
53
53
|
marginTop?: number;
|
|
54
54
|
marginRight?: number;
|
|
55
55
|
marginBottom?: number;
|
|
@@ -83,6 +83,16 @@ export interface Style {
|
|
|
83
83
|
borderTopRightRadius?: number;
|
|
84
84
|
borderBottomRightRadius?: number;
|
|
85
85
|
borderBottomLeftRadius?: number;
|
|
86
|
+
/** CSS border shorthand, e.g. `"1px solid #000"` */
|
|
87
|
+
border?: string;
|
|
88
|
+
/** Per-side border shorthand: string parses as CSS, number sets width */
|
|
89
|
+
borderTop?: string | number;
|
|
90
|
+
/** Per-side border shorthand: string parses as CSS, number sets width */
|
|
91
|
+
borderRight?: string | number;
|
|
92
|
+
/** Per-side border shorthand: string parses as CSS, number sets width */
|
|
93
|
+
borderBottom?: string | number;
|
|
94
|
+
/** Per-side border shorthand: string parses as CSS, number sets width */
|
|
95
|
+
borderLeft?: string | number;
|
|
86
96
|
position?: 'relative' | 'absolute';
|
|
87
97
|
top?: number;
|
|
88
98
|
right?: number;
|
|
@@ -98,6 +108,8 @@ export interface DocumentProps {
|
|
|
98
108
|
author?: string;
|
|
99
109
|
subject?: string;
|
|
100
110
|
creator?: string;
|
|
111
|
+
/** Document language (BCP 47 tag, e.g. "en-US"). Emitted as /Lang in the PDF Catalog. */
|
|
112
|
+
lang?: string;
|
|
101
113
|
fonts?: FontRegistration[];
|
|
102
114
|
children?: ReactNode;
|
|
103
115
|
}
|
|
@@ -106,7 +118,7 @@ export interface PageProps {
|
|
|
106
118
|
width: number;
|
|
107
119
|
height: number;
|
|
108
120
|
};
|
|
109
|
-
margin?: number | Edges;
|
|
121
|
+
margin?: number | string | number[] | Edges;
|
|
110
122
|
children?: ReactNode;
|
|
111
123
|
}
|
|
112
124
|
export interface ViewProps {
|
|
@@ -127,6 +139,10 @@ export interface ImageProps {
|
|
|
127
139
|
width?: number;
|
|
128
140
|
height?: number;
|
|
129
141
|
style?: Style;
|
|
142
|
+
/** Optional hyperlink URL — makes the image clickable. */
|
|
143
|
+
href?: string;
|
|
144
|
+
/** Alt text for accessibility. */
|
|
145
|
+
alt?: string;
|
|
130
146
|
}
|
|
131
147
|
export interface ColumnDef {
|
|
132
148
|
width: {
|
|
@@ -163,6 +179,10 @@ export interface SvgProps {
|
|
|
163
179
|
viewBox?: string;
|
|
164
180
|
content: string;
|
|
165
181
|
style?: Style;
|
|
182
|
+
/** Optional hyperlink URL — makes the SVG clickable. */
|
|
183
|
+
href?: string;
|
|
184
|
+
/** Alt text for accessibility. */
|
|
185
|
+
alt?: string;
|
|
166
186
|
}
|
|
167
187
|
/** A styled text segment within a <Text> element */
|
|
168
188
|
export interface TextRun {
|
|
@@ -187,6 +207,7 @@ export interface FormeMetadata {
|
|
|
187
207
|
author?: string;
|
|
188
208
|
subject?: string;
|
|
189
209
|
creator?: string;
|
|
210
|
+
lang?: string;
|
|
190
211
|
}
|
|
191
212
|
export interface FormePageConfig {
|
|
192
213
|
size: FormePageSize;
|
|
@@ -211,6 +232,7 @@ export interface FormeNode {
|
|
|
211
232
|
children: FormeNode[];
|
|
212
233
|
bookmark?: string;
|
|
213
234
|
href?: string;
|
|
235
|
+
alt?: string;
|
|
214
236
|
sourceLocation?: {
|
|
215
237
|
file: string;
|
|
216
238
|
line: number;
|