@ahmedrowaihi/pdf-forge-primitive 1.0.0-canary.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/LICENSE.md +8 -0
- package/dist/index.d.mts +184 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.d.ts +184 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +313 -0
- package/dist/index.mjs +282 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +25 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Copyright 2024 Plus Five Five, Inc
|
|
2
|
+
Copyright 2025 ahmedrowaihi (fork modifications)
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
5
|
+
|
|
6
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/body.d.ts
|
|
4
|
+
type BodyProps = Readonly<React.HtmlHTMLAttributes<HTMLBodyElement>>;
|
|
5
|
+
declare const Body: React.ForwardRefExoticComponent<Readonly<React.HtmlHTMLAttributes<HTMLBodyElement>> & React.RefAttributes<HTMLBodyElement>>;
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/document.d.ts
|
|
8
|
+
interface DocumentProps {
|
|
9
|
+
/** Document language */
|
|
10
|
+
lang?: string;
|
|
11
|
+
/** Text direction */
|
|
12
|
+
dir?: 'ltr' | 'rtl';
|
|
13
|
+
/** Head content */
|
|
14
|
+
head?: React.ReactNode;
|
|
15
|
+
/** Body content */
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
/** Additional HTML attributes */
|
|
18
|
+
htmlProps?: React.HtmlHTMLAttributes<HTMLHtmlElement>;
|
|
19
|
+
/** Additional head attributes */
|
|
20
|
+
headProps?: React.HtmlHTMLAttributes<HTMLHeadElement>;
|
|
21
|
+
/** Additional body attributes */
|
|
22
|
+
bodyProps?: React.HtmlHTMLAttributes<HTMLBodyElement>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Document component for PDF generation.
|
|
26
|
+
* Convenience wrapper that provides proper HTML5 document structure.
|
|
27
|
+
*/
|
|
28
|
+
declare const Document: React.FC<Readonly<DocumentProps>>;
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/font.d.ts
|
|
31
|
+
type FallbackFont = 'Arial' | 'Helvetica' | 'Verdana' | 'Georgia' | 'Times New Roman' | 'serif' | 'sans-serif' | 'monospace' | 'cursive' | 'fantasy';
|
|
32
|
+
type FontFormat = 'woff' | 'woff2' | 'truetype' | 'opentype' | 'embedded-opentype' | 'svg';
|
|
33
|
+
type FontWeight = React.CSSProperties['fontWeight'];
|
|
34
|
+
type FontStyle = React.CSSProperties['fontStyle'];
|
|
35
|
+
interface FontProps {
|
|
36
|
+
/** The font you want to use. NOTE: Do not insert multiple fonts here, use fallbackFontFamily for that */
|
|
37
|
+
fontFamily: string;
|
|
38
|
+
/** An array is possible, but the order of the array is the priority order */
|
|
39
|
+
fallbackFontFamily: FallbackFont | FallbackFont[];
|
|
40
|
+
/**
|
|
41
|
+
* Font source - must be a URL:
|
|
42
|
+
* - Absolute URL path (e.g., "/fonts/sans/Thmanyahsans12-Regular.otf")
|
|
43
|
+
* - Relative URL path (e.g., "fonts/sans/Thmanyahsans12-Regular.otf")
|
|
44
|
+
* - HTTP/HTTPS URL (e.g., "https://example.com/font.woff2")
|
|
45
|
+
* - Base64 data URI (e.g., "data:font/woff2;base64,ABC123...")
|
|
46
|
+
*
|
|
47
|
+
* The URL is used as-is - no automatic path conversion.
|
|
48
|
+
* Templates are responsible for providing the correct path.
|
|
49
|
+
*/
|
|
50
|
+
src?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Web font URL or base64 data URI (legacy prop, use src instead).
|
|
53
|
+
* Format examples:
|
|
54
|
+
* - URL: "https://example.com/font.woff2" or "/fonts/font.woff2"
|
|
55
|
+
* - Base64: "data:font/woff2;base64,ABC123..."
|
|
56
|
+
*/
|
|
57
|
+
webFont?: {
|
|
58
|
+
url: string;
|
|
59
|
+
format: FontFormat;
|
|
60
|
+
};
|
|
61
|
+
/** Font format - auto-detected from file extension if not provided */
|
|
62
|
+
format?: FontFormat;
|
|
63
|
+
/** Default: 'normal' */
|
|
64
|
+
fontStyle?: FontStyle;
|
|
65
|
+
/** Default: 400 */
|
|
66
|
+
fontWeight?: FontWeight;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Font Component
|
|
70
|
+
*
|
|
71
|
+
* Uses fonts from URLs (public paths, HTTP/HTTPS, or data URIs).
|
|
72
|
+
* No filesystem loading or base64 conversion - fonts must be served as static files.
|
|
73
|
+
*
|
|
74
|
+
* Usage:
|
|
75
|
+
* ```tsx
|
|
76
|
+
* // Absolute public URL
|
|
77
|
+
* <Font
|
|
78
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
79
|
+
* src="/fonts/sans/Thmanyahsans12-Regular.otf"
|
|
80
|
+
* fontWeight={400}
|
|
81
|
+
* />
|
|
82
|
+
*
|
|
83
|
+
* // Relative URL (relative to current page)
|
|
84
|
+
* <Font
|
|
85
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
86
|
+
* src="fonts/sans/Thmanyahsans12-Regular.otf"
|
|
87
|
+
* fontWeight={400}
|
|
88
|
+
* />
|
|
89
|
+
*
|
|
90
|
+
* // HTTP/HTTPS URL
|
|
91
|
+
* <Font
|
|
92
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
93
|
+
* src="https://example.com/font.otf"
|
|
94
|
+
* fontWeight={400}
|
|
95
|
+
* />
|
|
96
|
+
*
|
|
97
|
+
* // Base64 data URI
|
|
98
|
+
* <Font
|
|
99
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
100
|
+
* src="data:font/otf;base64,ABC123..."
|
|
101
|
+
* fontWeight={400}
|
|
102
|
+
* />
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* The component MUST be placed inside the <head> tag.
|
|
106
|
+
*/
|
|
107
|
+
declare function Font({
|
|
108
|
+
fontFamily,
|
|
109
|
+
fallbackFontFamily,
|
|
110
|
+
src,
|
|
111
|
+
webFont,
|
|
112
|
+
format,
|
|
113
|
+
fontStyle,
|
|
114
|
+
fontWeight
|
|
115
|
+
}: FontProps): React.JSX.Element;
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/head.d.ts
|
|
118
|
+
type HeadProps = Readonly<React.ComponentPropsWithoutRef<'head'>>;
|
|
119
|
+
declare const Head: React.ForwardRefExoticComponent<Readonly<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadElement>, HTMLHeadElement>, "ref">> & React.RefAttributes<HTMLHeadElement>>;
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/html.d.ts
|
|
122
|
+
type HtmlProps = Readonly<React.ComponentPropsWithoutRef<'html'>>;
|
|
123
|
+
declare const Html: React.ForwardRefExoticComponent<Readonly<Omit<React.DetailedHTMLProps<React.HtmlHTMLAttributes<HTMLHtmlElement>, HTMLHtmlElement>, "ref">> & React.RefAttributes<HTMLHtmlElement>>;
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/page-break.d.ts
|
|
126
|
+
interface PageBreakProps {
|
|
127
|
+
/** Type of page break */
|
|
128
|
+
type?: 'after' | 'before' | 'avoid';
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* PageBreak component adds page break CSS for PDF generation.
|
|
132
|
+
* Can be used as a self-closing element or wrapper.
|
|
133
|
+
*/
|
|
134
|
+
declare const PageBreak: React.FC<Readonly<PageBreakProps>>;
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region src/print-styles.d.ts
|
|
137
|
+
interface PrintStylesProps {
|
|
138
|
+
/** Preserve colors when printing */
|
|
139
|
+
preserveColors?: boolean;
|
|
140
|
+
/** Remove shadows from elements */
|
|
141
|
+
removeShadows?: boolean;
|
|
142
|
+
/** Selectors that should have page-break-after: always */
|
|
143
|
+
pageBreakAfter?: string[];
|
|
144
|
+
/** Selectors that should have page-break-before: avoid */
|
|
145
|
+
pageBreakBefore?: string[];
|
|
146
|
+
/** Selectors that should have page-break-inside: avoid */
|
|
147
|
+
pageBreakInside?: string[];
|
|
148
|
+
/** Custom print styles */
|
|
149
|
+
children?: React.ReactNode;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* PrintStyles component wraps styles in @media print for PDF generation.
|
|
153
|
+
* Should be placed inside the <Head> component.
|
|
154
|
+
*/
|
|
155
|
+
declare const PrintStyles: React.FC<Readonly<PrintStylesProps>>;
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region src/theme.d.ts
|
|
158
|
+
interface ThemeProps {
|
|
159
|
+
/** CSS variables and styles for this theme variant */
|
|
160
|
+
css: string;
|
|
161
|
+
/** Theme variant name (e.g., 'light', 'dark', 'blue', 'green', etc.) */
|
|
162
|
+
variant: string;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Theme component that applies CSS variables and styles based on theme variant.
|
|
166
|
+
* Uses :root CSS variables scoped to the variant class for theme switching.
|
|
167
|
+
* Should be placed inside the <Head> component.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```tsx
|
|
171
|
+
* <Theme variant="light" css={`
|
|
172
|
+
* --color-bg: #ffffff;
|
|
173
|
+
* --color-text: #000000;
|
|
174
|
+
* `} />
|
|
175
|
+
* <Theme variant="dark" css={`
|
|
176
|
+
* --color-bg: #1a1a1a;
|
|
177
|
+
* --color-text: #ffffff;
|
|
178
|
+
* `} />
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
declare const Theme: React.FC<Readonly<ThemeProps>>;
|
|
182
|
+
//#endregion
|
|
183
|
+
export { Body, BodyProps, Document, DocumentProps, Font, FontProps, Head, HeadProps, Html, HtmlProps, PageBreak, PageBreakProps, PrintStyles, PrintStylesProps, Theme, ThemeProps };
|
|
184
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/body.tsx","../src/document.tsx","../src/font.tsx","../src/head.tsx","../src/html.tsx","../src/page-break.tsx","../src/print-styles.tsx","../src/theme.tsx"],"sourcesContent":[],"mappings":";;;KAEY,SAAA,GAAY,SAAS,KAAA,CAAM,mBAAmB;cAE7C,MAAI,KAAA,CAAA,0BAAA,SAAA,KAAA,CAAA,mBAAA,oBAAA,KAAA,CAAA,cAAA;;;UCCA,aAAA;;EDHL,IAAA,CAAA,EAAA,MAAS;EAAqC;EAAzB,GAAM,CAAA,EAAA,KAAA,GAAA,KAAA;EAAf;EAAQ,IAAA,CAAA,ECSvB,KAAA,CAAM,SDTiB;EAEnB;EAAI,QAAA,ECSL,KAAA,CAAM,SDTD;EAAA;EAAA,SAAA,CAAA,ECWH,KAAA,CAAM,kBDXH,CCWsB,eDXtB,CAAA;EAAA;EAAA,SAAA,CAAA,ECaH,KAAA,CAAM,kBDbH,CCasB,eDbtB,CAAA;EAAA;EAAA,SAAA,CAAA,ECeH,KAAA,CAAM,kBDfH,CCesB,eDftB,CAAA;;;;ACCjB;;AAQkB,cAaL,QAbK,EAaK,KAAA,CAAM,EAbX,CAac,QAbd,CAauB,aAbvB,CAAA,CAAA;;;KCXb,YAAA;KAYA,UAAA;AFZL,KEoBK,UAAA,GAAa,KAAA,CAAM,aFpBH,CAAA,YAAA,CAAA;KEqBhB,SAAA,GAAY,KAAA,CAAM,aFrBmC,CAAA,WAAA,CAAA;AAAnB,UEuBtB,SAAA,CFvBsB;EAAf;EAAQ,UAAA,EAAA,MAAA;EAEnB;EAAI,kBAAA,EEyBK,YFzBL,GEyBoB,YFzBpB,EAAA;EAAA;;;;;;;;;ACCjB;EAMS,GAAM,CAAA,EAAA,MAAA;EAEH;;;;;;EAME,OAAM,CAAA,EAAA;IAAkB,GAAA,EAAA,MAAA;IAOzB,MAAA,ECuBD,UDJX;EAnBwC,CAAA;EAAT;EAAT,MAAM,CAAA,EC0BlB,UD1BkB;EAAE;cC4BjB;;eAEC;AAxDqB;AAEnB;AAYF;AAQsB;AAGrC;;;;;;;;AA8GA;;;;;;;;;;;;;;ACrIA;AAEA;;;;;;;;;;;;;iBDmIgB,IAAA;;;;;;;;GAQb,YAAY,KAAA,CAAM,GAAA,CAAI;;;KC7Ib,SAAA,GAAY,SAAS,KAAA,CAAM;cAE1B,MAAI,KAAA,CAAA,0BAAA,SAAA,KAAA,KAAA,CAAA,kBAAA,KAAA,CAAA,eAAA,kBAAA,4BAAA,KAAA,CAAA,cAAA;;;KCFL,SAAA,GAAY,SAAS,KAAA,CAAM;cAE1B,MAAI,KAAA,CAAA,0BAAA,SAAA,KAAA,KAAA,CAAA,kBAAA,KAAA,CAAA,mBAAA,kBAAA,4BAAA,KAAA,CAAA,cAAA;;;UCFA,cAAA;;ELAL,IAAA,CAAA,EAAA,OAAS,GAAA,QAAA,GAAA,OAAA;;;;;AAErB;AAAiB,cKOJ,SLPI,EKOO,KAAA,CAAM,ELPb,CKOgB,QLPhB,CKOyB,cLPzB,CAAA,CAAA;;;UMFA,gBAAA;;ENAL,cAAS,CAAA,EAAA,OAAA;EAAqC;EAAzB,aAAM,CAAA,EAAA,OAAA;EAAf;EAAQ,cAAA,CAAA,EAAA,MAAA,EAAA;EAEnB;EAAI,eAAA,CAAA,EAAA,MAAA,EAAA;EAAA;EAAA,eAAA,CAAA,EAAA,MAAA,EAAA;EAAA;EAAA,QAAA,CAAA,EMUJ,KAAA,CAAM,SNVF;;;;;;ACCA,cKgBJ,WLhBiB,EKgBJ,KAAA,CAAM,ELhBF,CKgBK,QLhBL,CKgBc,gBLhBd,CAAA,CAAA;;;UMHb,UAAA;;EPAL,GAAA,EAAA,MAAA;EAA8C;EAAzB,OAAM,EAAA,MAAA;;;AAEvC;;;;;;;;;;;ACCA;;;;;AAYuC,cMS1B,KNT0B,EMSnB,KAAA,CAAM,ENTa,CMSV,QNTU,CMSD,UNTC,CAAA,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/body.d.ts
|
|
4
|
+
type BodyProps = Readonly<React.HtmlHTMLAttributes<HTMLBodyElement>>;
|
|
5
|
+
declare const Body: React.ForwardRefExoticComponent<Readonly<React.HtmlHTMLAttributes<HTMLBodyElement>> & React.RefAttributes<HTMLBodyElement>>;
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/document.d.ts
|
|
8
|
+
interface DocumentProps {
|
|
9
|
+
/** Document language */
|
|
10
|
+
lang?: string;
|
|
11
|
+
/** Text direction */
|
|
12
|
+
dir?: 'ltr' | 'rtl';
|
|
13
|
+
/** Head content */
|
|
14
|
+
head?: React.ReactNode;
|
|
15
|
+
/** Body content */
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
/** Additional HTML attributes */
|
|
18
|
+
htmlProps?: React.HtmlHTMLAttributes<HTMLHtmlElement>;
|
|
19
|
+
/** Additional head attributes */
|
|
20
|
+
headProps?: React.HtmlHTMLAttributes<HTMLHeadElement>;
|
|
21
|
+
/** Additional body attributes */
|
|
22
|
+
bodyProps?: React.HtmlHTMLAttributes<HTMLBodyElement>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Document component for PDF generation.
|
|
26
|
+
* Convenience wrapper that provides proper HTML5 document structure.
|
|
27
|
+
*/
|
|
28
|
+
declare const Document: React.FC<Readonly<DocumentProps>>;
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/font.d.ts
|
|
31
|
+
type FallbackFont = 'Arial' | 'Helvetica' | 'Verdana' | 'Georgia' | 'Times New Roman' | 'serif' | 'sans-serif' | 'monospace' | 'cursive' | 'fantasy';
|
|
32
|
+
type FontFormat = 'woff' | 'woff2' | 'truetype' | 'opentype' | 'embedded-opentype' | 'svg';
|
|
33
|
+
type FontWeight = React.CSSProperties['fontWeight'];
|
|
34
|
+
type FontStyle = React.CSSProperties['fontStyle'];
|
|
35
|
+
interface FontProps {
|
|
36
|
+
/** The font you want to use. NOTE: Do not insert multiple fonts here, use fallbackFontFamily for that */
|
|
37
|
+
fontFamily: string;
|
|
38
|
+
/** An array is possible, but the order of the array is the priority order */
|
|
39
|
+
fallbackFontFamily: FallbackFont | FallbackFont[];
|
|
40
|
+
/**
|
|
41
|
+
* Font source - must be a URL:
|
|
42
|
+
* - Absolute URL path (e.g., "/fonts/sans/Thmanyahsans12-Regular.otf")
|
|
43
|
+
* - Relative URL path (e.g., "fonts/sans/Thmanyahsans12-Regular.otf")
|
|
44
|
+
* - HTTP/HTTPS URL (e.g., "https://example.com/font.woff2")
|
|
45
|
+
* - Base64 data URI (e.g., "data:font/woff2;base64,ABC123...")
|
|
46
|
+
*
|
|
47
|
+
* The URL is used as-is - no automatic path conversion.
|
|
48
|
+
* Templates are responsible for providing the correct path.
|
|
49
|
+
*/
|
|
50
|
+
src?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Web font URL or base64 data URI (legacy prop, use src instead).
|
|
53
|
+
* Format examples:
|
|
54
|
+
* - URL: "https://example.com/font.woff2" or "/fonts/font.woff2"
|
|
55
|
+
* - Base64: "data:font/woff2;base64,ABC123..."
|
|
56
|
+
*/
|
|
57
|
+
webFont?: {
|
|
58
|
+
url: string;
|
|
59
|
+
format: FontFormat;
|
|
60
|
+
};
|
|
61
|
+
/** Font format - auto-detected from file extension if not provided */
|
|
62
|
+
format?: FontFormat;
|
|
63
|
+
/** Default: 'normal' */
|
|
64
|
+
fontStyle?: FontStyle;
|
|
65
|
+
/** Default: 400 */
|
|
66
|
+
fontWeight?: FontWeight;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Font Component
|
|
70
|
+
*
|
|
71
|
+
* Uses fonts from URLs (public paths, HTTP/HTTPS, or data URIs).
|
|
72
|
+
* No filesystem loading or base64 conversion - fonts must be served as static files.
|
|
73
|
+
*
|
|
74
|
+
* Usage:
|
|
75
|
+
* ```tsx
|
|
76
|
+
* // Absolute public URL
|
|
77
|
+
* <Font
|
|
78
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
79
|
+
* src="/fonts/sans/Thmanyahsans12-Regular.otf"
|
|
80
|
+
* fontWeight={400}
|
|
81
|
+
* />
|
|
82
|
+
*
|
|
83
|
+
* // Relative URL (relative to current page)
|
|
84
|
+
* <Font
|
|
85
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
86
|
+
* src="fonts/sans/Thmanyahsans12-Regular.otf"
|
|
87
|
+
* fontWeight={400}
|
|
88
|
+
* />
|
|
89
|
+
*
|
|
90
|
+
* // HTTP/HTTPS URL
|
|
91
|
+
* <Font
|
|
92
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
93
|
+
* src="https://example.com/font.otf"
|
|
94
|
+
* fontWeight={400}
|
|
95
|
+
* />
|
|
96
|
+
*
|
|
97
|
+
* // Base64 data URI
|
|
98
|
+
* <Font
|
|
99
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
100
|
+
* src="data:font/otf;base64,ABC123..."
|
|
101
|
+
* fontWeight={400}
|
|
102
|
+
* />
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* The component MUST be placed inside the <head> tag.
|
|
106
|
+
*/
|
|
107
|
+
declare function Font({
|
|
108
|
+
fontFamily,
|
|
109
|
+
fallbackFontFamily,
|
|
110
|
+
src,
|
|
111
|
+
webFont,
|
|
112
|
+
format,
|
|
113
|
+
fontStyle,
|
|
114
|
+
fontWeight
|
|
115
|
+
}: FontProps): React.JSX.Element;
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/head.d.ts
|
|
118
|
+
type HeadProps = Readonly<React.ComponentPropsWithoutRef<'head'>>;
|
|
119
|
+
declare const Head: React.ForwardRefExoticComponent<Readonly<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadElement>, HTMLHeadElement>, "ref">> & React.RefAttributes<HTMLHeadElement>>;
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/html.d.ts
|
|
122
|
+
type HtmlProps = Readonly<React.ComponentPropsWithoutRef<'html'>>;
|
|
123
|
+
declare const Html: React.ForwardRefExoticComponent<Readonly<Omit<React.DetailedHTMLProps<React.HtmlHTMLAttributes<HTMLHtmlElement>, HTMLHtmlElement>, "ref">> & React.RefAttributes<HTMLHtmlElement>>;
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/page-break.d.ts
|
|
126
|
+
interface PageBreakProps {
|
|
127
|
+
/** Type of page break */
|
|
128
|
+
type?: 'after' | 'before' | 'avoid';
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* PageBreak component adds page break CSS for PDF generation.
|
|
132
|
+
* Can be used as a self-closing element or wrapper.
|
|
133
|
+
*/
|
|
134
|
+
declare const PageBreak: React.FC<Readonly<PageBreakProps>>;
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region src/print-styles.d.ts
|
|
137
|
+
interface PrintStylesProps {
|
|
138
|
+
/** Preserve colors when printing */
|
|
139
|
+
preserveColors?: boolean;
|
|
140
|
+
/** Remove shadows from elements */
|
|
141
|
+
removeShadows?: boolean;
|
|
142
|
+
/** Selectors that should have page-break-after: always */
|
|
143
|
+
pageBreakAfter?: string[];
|
|
144
|
+
/** Selectors that should have page-break-before: avoid */
|
|
145
|
+
pageBreakBefore?: string[];
|
|
146
|
+
/** Selectors that should have page-break-inside: avoid */
|
|
147
|
+
pageBreakInside?: string[];
|
|
148
|
+
/** Custom print styles */
|
|
149
|
+
children?: React.ReactNode;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* PrintStyles component wraps styles in @media print for PDF generation.
|
|
153
|
+
* Should be placed inside the <Head> component.
|
|
154
|
+
*/
|
|
155
|
+
declare const PrintStyles: React.FC<Readonly<PrintStylesProps>>;
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region src/theme.d.ts
|
|
158
|
+
interface ThemeProps {
|
|
159
|
+
/** CSS variables and styles for this theme variant */
|
|
160
|
+
css: string;
|
|
161
|
+
/** Theme variant name (e.g., 'light', 'dark', 'blue', 'green', etc.) */
|
|
162
|
+
variant: string;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Theme component that applies CSS variables and styles based on theme variant.
|
|
166
|
+
* Uses :root CSS variables scoped to the variant class for theme switching.
|
|
167
|
+
* Should be placed inside the <Head> component.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```tsx
|
|
171
|
+
* <Theme variant="light" css={`
|
|
172
|
+
* --color-bg: #ffffff;
|
|
173
|
+
* --color-text: #000000;
|
|
174
|
+
* `} />
|
|
175
|
+
* <Theme variant="dark" css={`
|
|
176
|
+
* --color-bg: #1a1a1a;
|
|
177
|
+
* --color-text: #ffffff;
|
|
178
|
+
* `} />
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
declare const Theme: React.FC<Readonly<ThemeProps>>;
|
|
182
|
+
//#endregion
|
|
183
|
+
export { Body, BodyProps, Document, DocumentProps, Font, FontProps, Head, HeadProps, Html, HtmlProps, PageBreak, PageBreakProps, PrintStyles, PrintStylesProps, Theme, ThemeProps };
|
|
184
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/body.tsx","../src/document.tsx","../src/font.tsx","../src/head.tsx","../src/html.tsx","../src/page-break.tsx","../src/print-styles.tsx","../src/theme.tsx"],"sourcesContent":[],"mappings":";;;KAEY,SAAA,GAAY,SAAS,KAAA,CAAM,mBAAmB;cAE7C,MAAI,KAAA,CAAA,0BAAA,SAAA,KAAA,CAAA,mBAAA,oBAAA,KAAA,CAAA,cAAA;;;UCCA,aAAA;;EDHL,IAAA,CAAA,EAAA,MAAS;EAAqC;EAAzB,GAAM,CAAA,EAAA,KAAA,GAAA,KAAA;EAAf;EAAQ,IAAA,CAAA,ECSvB,KAAA,CAAM,SDTiB;EAEnB;EAAI,QAAA,ECSL,KAAA,CAAM,SDTD;EAAA;EAAA,SAAA,CAAA,ECWH,KAAA,CAAM,kBDXH,CCWsB,eDXtB,CAAA;EAAA;EAAA,SAAA,CAAA,ECaH,KAAA,CAAM,kBDbH,CCasB,eDbtB,CAAA;EAAA;EAAA,SAAA,CAAA,ECeH,KAAA,CAAM,kBDfH,CCesB,eDftB,CAAA;;;;ACCjB;;AAQkB,cAaL,QAbK,EAaK,KAAA,CAAM,EAbX,CAac,QAbd,CAauB,aAbvB,CAAA,CAAA;;;KCXb,YAAA;KAYA,UAAA;AFZL,KEoBK,UAAA,GAAa,KAAA,CAAM,aFpBH,CAAA,YAAA,CAAA;KEqBhB,SAAA,GAAY,KAAA,CAAM,aFrBmC,CAAA,WAAA,CAAA;AAAnB,UEuBtB,SAAA,CFvBsB;EAAf;EAAQ,UAAA,EAAA,MAAA;EAEnB;EAAI,kBAAA,EEyBK,YFzBL,GEyBoB,YFzBpB,EAAA;EAAA;;;;;;;;;ACCjB;EAMS,GAAM,CAAA,EAAA,MAAA;EAEH;;;;;;EAME,OAAM,CAAA,EAAA;IAAkB,GAAA,EAAA,MAAA;IAOzB,MAAA,ECuBD,UDJX;EAnBwC,CAAA;EAAT;EAAT,MAAM,CAAA,EC0BlB,UD1BkB;EAAE;cC4BjB;;eAEC;AAxDqB;AAEnB;AAYF;AAQsB;AAGrC;;;;;;;;AA8GA;;;;;;;;;;;;;;ACrIA;AAEA;;;;;;;;;;;;;iBDmIgB,IAAA;;;;;;;;GAQb,YAAY,KAAA,CAAM,GAAA,CAAI;;;KC7Ib,SAAA,GAAY,SAAS,KAAA,CAAM;cAE1B,MAAI,KAAA,CAAA,0BAAA,SAAA,KAAA,KAAA,CAAA,kBAAA,KAAA,CAAA,eAAA,kBAAA,4BAAA,KAAA,CAAA,cAAA;;;KCFL,SAAA,GAAY,SAAS,KAAA,CAAM;cAE1B,MAAI,KAAA,CAAA,0BAAA,SAAA,KAAA,KAAA,CAAA,kBAAA,KAAA,CAAA,mBAAA,kBAAA,4BAAA,KAAA,CAAA,cAAA;;;UCFA,cAAA;;ELAL,IAAA,CAAA,EAAA,OAAS,GAAA,QAAA,GAAA,OAAA;;;;;AAErB;AAAiB,cKOJ,SLPI,EKOO,KAAA,CAAM,ELPb,CKOgB,QLPhB,CKOyB,cLPzB,CAAA,CAAA;;;UMFA,gBAAA;;ENAL,cAAS,CAAA,EAAA,OAAA;EAAqC;EAAzB,aAAM,CAAA,EAAA,OAAA;EAAf;EAAQ,cAAA,CAAA,EAAA,MAAA,EAAA;EAEnB;EAAI,eAAA,CAAA,EAAA,MAAA,EAAA;EAAA;EAAA,eAAA,CAAA,EAAA,MAAA,EAAA;EAAA;EAAA,QAAA,CAAA,EMUJ,KAAA,CAAM,SNVF;;;;;;ACCA,cKgBJ,WLhBiB,EKgBJ,KAAA,CAAM,ELhBF,CKgBK,QLhBL,CKgBc,gBLhBd,CAAA,CAAA;;;UMHb,UAAA;;EPAL,GAAA,EAAA,MAAA;EAA8C;EAAzB,OAAM,EAAA,MAAA;;;AAEvC;;;;;;;;;;;ACCA;;;;;AAYuC,cMS1B,KNT0B,EMSnB,KAAA,CAAM,ENTa,CMSV,QNTU,CMSD,UNTC,CAAA,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
let react = require("react");
|
|
25
|
+
react = __toESM(react);
|
|
26
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
27
|
+
react_jsx_runtime = __toESM(react_jsx_runtime);
|
|
28
|
+
|
|
29
|
+
//#region src/body.tsx
|
|
30
|
+
const Body = react.forwardRef(({ children,...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("body", {
|
|
31
|
+
...props,
|
|
32
|
+
ref,
|
|
33
|
+
children
|
|
34
|
+
}));
|
|
35
|
+
Body.displayName = "Body";
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/head.tsx
|
|
39
|
+
const Head = react.forwardRef(({ children,...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("head", {
|
|
40
|
+
...props,
|
|
41
|
+
ref,
|
|
42
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", {
|
|
43
|
+
content: "text/html; charset=UTF-8",
|
|
44
|
+
httpEquiv: "Content-Type"
|
|
45
|
+
}), children]
|
|
46
|
+
}));
|
|
47
|
+
Head.displayName = "Head";
|
|
48
|
+
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/html.tsx
|
|
51
|
+
const Html = react.forwardRef(({ children, lang = "en", dir = "ltr",...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("html", {
|
|
52
|
+
...props,
|
|
53
|
+
dir,
|
|
54
|
+
lang,
|
|
55
|
+
ref,
|
|
56
|
+
children
|
|
57
|
+
}));
|
|
58
|
+
Html.displayName = "Html";
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/document.tsx
|
|
62
|
+
/**
|
|
63
|
+
* Document component for PDF generation.
|
|
64
|
+
* Convenience wrapper that provides proper HTML5 document structure.
|
|
65
|
+
*/
|
|
66
|
+
const Document = ({ lang = "en", dir = "ltr", head, children, htmlProps, headProps, bodyProps }) => {
|
|
67
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Html, {
|
|
68
|
+
lang,
|
|
69
|
+
dir,
|
|
70
|
+
...htmlProps,
|
|
71
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Head, {
|
|
72
|
+
...headProps,
|
|
73
|
+
children: [
|
|
74
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", { charSet: "UTF-8" }),
|
|
75
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", {
|
|
76
|
+
name: "viewport",
|
|
77
|
+
content: "width=device-width, initial-scale=1.0"
|
|
78
|
+
}),
|
|
79
|
+
head
|
|
80
|
+
]
|
|
81
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Body, {
|
|
82
|
+
...bodyProps,
|
|
83
|
+
children
|
|
84
|
+
})]
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
Document.displayName = "Document";
|
|
88
|
+
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/font.tsx
|
|
91
|
+
/**
|
|
92
|
+
* Detect font format from file extension or URL
|
|
93
|
+
*/
|
|
94
|
+
function detectFontFormat(src, format) {
|
|
95
|
+
if (format) return {
|
|
96
|
+
format,
|
|
97
|
+
mimeType: {
|
|
98
|
+
woff: "font/woff",
|
|
99
|
+
woff2: "font/woff2",
|
|
100
|
+
truetype: "font/ttf",
|
|
101
|
+
opentype: "font/otf",
|
|
102
|
+
"embedded-opentype": "font/eot",
|
|
103
|
+
svg: "image/svg+xml"
|
|
104
|
+
}[format]
|
|
105
|
+
};
|
|
106
|
+
const lastDot = src.lastIndexOf(".");
|
|
107
|
+
return {
|
|
108
|
+
woff2: {
|
|
109
|
+
format: "woff2",
|
|
110
|
+
mimeType: "font/woff2"
|
|
111
|
+
},
|
|
112
|
+
woff: {
|
|
113
|
+
format: "woff",
|
|
114
|
+
mimeType: "font/woff"
|
|
115
|
+
},
|
|
116
|
+
otf: {
|
|
117
|
+
format: "opentype",
|
|
118
|
+
mimeType: "font/otf"
|
|
119
|
+
},
|
|
120
|
+
ttf: {
|
|
121
|
+
format: "truetype",
|
|
122
|
+
mimeType: "font/ttf"
|
|
123
|
+
},
|
|
124
|
+
eot: {
|
|
125
|
+
format: "embedded-opentype",
|
|
126
|
+
mimeType: "font/eot"
|
|
127
|
+
},
|
|
128
|
+
svg: {
|
|
129
|
+
format: "svg",
|
|
130
|
+
mimeType: "image/svg+xml"
|
|
131
|
+
}
|
|
132
|
+
}[lastDot !== -1 ? src.slice(lastDot + 1).toLowerCase() : ""] || {
|
|
133
|
+
format: "woff2",
|
|
134
|
+
mimeType: "font/woff2"
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Font Component
|
|
139
|
+
*
|
|
140
|
+
* Uses fonts from URLs (public paths, HTTP/HTTPS, or data URIs).
|
|
141
|
+
* No filesystem loading or base64 conversion - fonts must be served as static files.
|
|
142
|
+
*
|
|
143
|
+
* Usage:
|
|
144
|
+
* ```tsx
|
|
145
|
+
* // Absolute public URL
|
|
146
|
+
* <Font
|
|
147
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
148
|
+
* src="/fonts/sans/Thmanyahsans12-Regular.otf"
|
|
149
|
+
* fontWeight={400}
|
|
150
|
+
* />
|
|
151
|
+
*
|
|
152
|
+
* // Relative URL (relative to current page)
|
|
153
|
+
* <Font
|
|
154
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
155
|
+
* src="fonts/sans/Thmanyahsans12-Regular.otf"
|
|
156
|
+
* fontWeight={400}
|
|
157
|
+
* />
|
|
158
|
+
*
|
|
159
|
+
* // HTTP/HTTPS URL
|
|
160
|
+
* <Font
|
|
161
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
162
|
+
* src="https://example.com/font.otf"
|
|
163
|
+
* fontWeight={400}
|
|
164
|
+
* />
|
|
165
|
+
*
|
|
166
|
+
* // Base64 data URI
|
|
167
|
+
* <Font
|
|
168
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
169
|
+
* src="data:font/otf;base64,ABC123..."
|
|
170
|
+
* fontWeight={400}
|
|
171
|
+
* />
|
|
172
|
+
* ```
|
|
173
|
+
*
|
|
174
|
+
* The component MUST be placed inside the <head> tag.
|
|
175
|
+
*/
|
|
176
|
+
function Font({ fontFamily, fallbackFontFamily, src, webFont, format, fontStyle = "normal", fontWeight = 400 }) {
|
|
177
|
+
let fontSrc = "";
|
|
178
|
+
let detectedFormat = format;
|
|
179
|
+
let mimeType;
|
|
180
|
+
if (src) if (src.startsWith("data:")) {
|
|
181
|
+
const match = src.match(/^data:([^;]+);base64,(.+)$/);
|
|
182
|
+
if (match) {
|
|
183
|
+
mimeType = match[1];
|
|
184
|
+
detectedFormat = {
|
|
185
|
+
"font/woff2": "woff2",
|
|
186
|
+
"font/woff": "woff",
|
|
187
|
+
"font/otf": "opentype",
|
|
188
|
+
"font/ttf": "truetype",
|
|
189
|
+
"font/eot": "embedded-opentype",
|
|
190
|
+
"image/svg+xml": "svg"
|
|
191
|
+
}[mimeType] || "woff2";
|
|
192
|
+
fontSrc = `src: url(${src});`;
|
|
193
|
+
} else fontSrc = `src: url(${src});`;
|
|
194
|
+
} else if (src.startsWith("http://") || src.startsWith("https://")) {
|
|
195
|
+
detectedFormat = detectFontFormat(src, format).format;
|
|
196
|
+
fontSrc = `src: url(${src})${format ? ` format('${detectedFormat}')` : ""};`;
|
|
197
|
+
} else {
|
|
198
|
+
detectedFormat = detectFontFormat(src, format).format;
|
|
199
|
+
fontSrc = `src: url(${src})${format ? ` format('${detectedFormat}')` : ""};`;
|
|
200
|
+
}
|
|
201
|
+
else if (webFont) fontSrc = webFont.url.startsWith("data:") ? `src: url(${webFont.url});` : `src: url(${webFont.url}) format('${webFont.format}');`;
|
|
202
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { dangerouslySetInnerHTML: { __html: `
|
|
203
|
+
@font-face {
|
|
204
|
+
font-family: '${fontFamily}';
|
|
205
|
+
font-style: ${fontStyle};
|
|
206
|
+
font-weight: ${fontWeight};
|
|
207
|
+
${fontSrc}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
* {
|
|
211
|
+
font-family: '${fontFamily}', ${Array.isArray(fallbackFontFamily) ? fallbackFontFamily.join(", ") : fallbackFontFamily};
|
|
212
|
+
}
|
|
213
|
+
` } });
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region src/page-break.tsx
|
|
218
|
+
/**
|
|
219
|
+
* PageBreak component adds page break CSS for PDF generation.
|
|
220
|
+
* Can be used as a self-closing element or wrapper.
|
|
221
|
+
*/
|
|
222
|
+
const PageBreak = ({ type = "after" }) => {
|
|
223
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: type === "after" ? {
|
|
224
|
+
pageBreakAfter: "always",
|
|
225
|
+
breakAfter: "page"
|
|
226
|
+
} : type === "before" ? {
|
|
227
|
+
pageBreakBefore: "always",
|
|
228
|
+
breakBefore: "page"
|
|
229
|
+
} : {
|
|
230
|
+
pageBreakInside: "avoid",
|
|
231
|
+
breakInside: "avoid"
|
|
232
|
+
} });
|
|
233
|
+
};
|
|
234
|
+
PageBreak.displayName = "PageBreak";
|
|
235
|
+
|
|
236
|
+
//#endregion
|
|
237
|
+
//#region src/print-styles.tsx
|
|
238
|
+
/**
|
|
239
|
+
* PrintStyles component wraps styles in @media print for PDF generation.
|
|
240
|
+
* Should be placed inside the <Head> component.
|
|
241
|
+
*/
|
|
242
|
+
const PrintStyles = ({ preserveColors = false, removeShadows = false, pageBreakAfter = [], pageBreakBefore = [], pageBreakInside = [], children }) => {
|
|
243
|
+
const styles = [];
|
|
244
|
+
if (preserveColors) styles.push(`* {
|
|
245
|
+
-webkit-print-color-adjust: exact !important;
|
|
246
|
+
print-color-adjust: exact !important;
|
|
247
|
+
color-adjust: exact !important;
|
|
248
|
+
}`);
|
|
249
|
+
if (removeShadows) styles.push(`.sheet, .page, .card, .box {
|
|
250
|
+
box-shadow: none !important;
|
|
251
|
+
}`);
|
|
252
|
+
if (pageBreakAfter.length > 0) styles.push(`${pageBreakAfter.join(", ")} {
|
|
253
|
+
page-break-after: always !important;
|
|
254
|
+
break-after: page !important;
|
|
255
|
+
}`);
|
|
256
|
+
if (pageBreakBefore.length > 0) styles.push(`${pageBreakBefore.join(", ")} {
|
|
257
|
+
page-break-before: avoid !important;
|
|
258
|
+
break-before: avoid !important;
|
|
259
|
+
}`);
|
|
260
|
+
if (pageBreakInside.length > 0) styles.push(`${pageBreakInside.join(", ")} {
|
|
261
|
+
page-break-inside: avoid !important;
|
|
262
|
+
break-inside: avoid !important;
|
|
263
|
+
}`);
|
|
264
|
+
const customStyles = children ? typeof children === "string" ? children : "" : "";
|
|
265
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { dangerouslySetInnerHTML: { __html: `@media print {
|
|
266
|
+
${styles.join("\n\n")}${customStyles ? `\n\n${customStyles}` : ""}
|
|
267
|
+
}` } });
|
|
268
|
+
};
|
|
269
|
+
PrintStyles.displayName = "PrintStyles";
|
|
270
|
+
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region src/theme.tsx
|
|
273
|
+
/**
|
|
274
|
+
* Theme component that applies CSS variables and styles based on theme variant.
|
|
275
|
+
* Uses :root CSS variables scoped to the variant class for theme switching.
|
|
276
|
+
* Should be placed inside the <Head> component.
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```tsx
|
|
280
|
+
* <Theme variant="light" css={`
|
|
281
|
+
* --color-bg: #ffffff;
|
|
282
|
+
* --color-text: #000000;
|
|
283
|
+
* `} />
|
|
284
|
+
* <Theme variant="dark" css={`
|
|
285
|
+
* --color-bg: #1a1a1a;
|
|
286
|
+
* --color-text: #ffffff;
|
|
287
|
+
* `} />
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
const Theme = ({ css, variant }) => {
|
|
291
|
+
if (variant === "default") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { dangerouslySetInnerHTML: { __html: `
|
|
292
|
+
:root {
|
|
293
|
+
${css}
|
|
294
|
+
}
|
|
295
|
+
` } });
|
|
296
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { dangerouslySetInnerHTML: { __html: `
|
|
297
|
+
:root.${variant},
|
|
298
|
+
.${variant} {
|
|
299
|
+
${css}
|
|
300
|
+
}
|
|
301
|
+
` } });
|
|
302
|
+
};
|
|
303
|
+
Theme.displayName = "Theme";
|
|
304
|
+
|
|
305
|
+
//#endregion
|
|
306
|
+
exports.Body = Body;
|
|
307
|
+
exports.Document = Document;
|
|
308
|
+
exports.Font = Font;
|
|
309
|
+
exports.Head = Head;
|
|
310
|
+
exports.Html = Html;
|
|
311
|
+
exports.PageBreak = PageBreak;
|
|
312
|
+
exports.PrintStyles = PrintStyles;
|
|
313
|
+
exports.Theme = Theme;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/body.tsx
|
|
5
|
+
const Body = React.forwardRef(({ children,...props }, ref) => /* @__PURE__ */ jsx("body", {
|
|
6
|
+
...props,
|
|
7
|
+
ref,
|
|
8
|
+
children
|
|
9
|
+
}));
|
|
10
|
+
Body.displayName = "Body";
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/head.tsx
|
|
14
|
+
const Head = React.forwardRef(({ children,...props }, ref) => /* @__PURE__ */ jsxs("head", {
|
|
15
|
+
...props,
|
|
16
|
+
ref,
|
|
17
|
+
children: [/* @__PURE__ */ jsx("meta", {
|
|
18
|
+
content: "text/html; charset=UTF-8",
|
|
19
|
+
httpEquiv: "Content-Type"
|
|
20
|
+
}), children]
|
|
21
|
+
}));
|
|
22
|
+
Head.displayName = "Head";
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/html.tsx
|
|
26
|
+
const Html = React.forwardRef(({ children, lang = "en", dir = "ltr",...props }, ref) => /* @__PURE__ */ jsx("html", {
|
|
27
|
+
...props,
|
|
28
|
+
dir,
|
|
29
|
+
lang,
|
|
30
|
+
ref,
|
|
31
|
+
children
|
|
32
|
+
}));
|
|
33
|
+
Html.displayName = "Html";
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/document.tsx
|
|
37
|
+
/**
|
|
38
|
+
* Document component for PDF generation.
|
|
39
|
+
* Convenience wrapper that provides proper HTML5 document structure.
|
|
40
|
+
*/
|
|
41
|
+
const Document = ({ lang = "en", dir = "ltr", head, children, htmlProps, headProps, bodyProps }) => {
|
|
42
|
+
return /* @__PURE__ */ jsxs(Html, {
|
|
43
|
+
lang,
|
|
44
|
+
dir,
|
|
45
|
+
...htmlProps,
|
|
46
|
+
children: [/* @__PURE__ */ jsxs(Head, {
|
|
47
|
+
...headProps,
|
|
48
|
+
children: [
|
|
49
|
+
/* @__PURE__ */ jsx("meta", { charSet: "UTF-8" }),
|
|
50
|
+
/* @__PURE__ */ jsx("meta", {
|
|
51
|
+
name: "viewport",
|
|
52
|
+
content: "width=device-width, initial-scale=1.0"
|
|
53
|
+
}),
|
|
54
|
+
head
|
|
55
|
+
]
|
|
56
|
+
}), /* @__PURE__ */ jsx(Body, {
|
|
57
|
+
...bodyProps,
|
|
58
|
+
children
|
|
59
|
+
})]
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
Document.displayName = "Document";
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/font.tsx
|
|
66
|
+
/**
|
|
67
|
+
* Detect font format from file extension or URL
|
|
68
|
+
*/
|
|
69
|
+
function detectFontFormat(src, format) {
|
|
70
|
+
if (format) return {
|
|
71
|
+
format,
|
|
72
|
+
mimeType: {
|
|
73
|
+
woff: "font/woff",
|
|
74
|
+
woff2: "font/woff2",
|
|
75
|
+
truetype: "font/ttf",
|
|
76
|
+
opentype: "font/otf",
|
|
77
|
+
"embedded-opentype": "font/eot",
|
|
78
|
+
svg: "image/svg+xml"
|
|
79
|
+
}[format]
|
|
80
|
+
};
|
|
81
|
+
const lastDot = src.lastIndexOf(".");
|
|
82
|
+
return {
|
|
83
|
+
woff2: {
|
|
84
|
+
format: "woff2",
|
|
85
|
+
mimeType: "font/woff2"
|
|
86
|
+
},
|
|
87
|
+
woff: {
|
|
88
|
+
format: "woff",
|
|
89
|
+
mimeType: "font/woff"
|
|
90
|
+
},
|
|
91
|
+
otf: {
|
|
92
|
+
format: "opentype",
|
|
93
|
+
mimeType: "font/otf"
|
|
94
|
+
},
|
|
95
|
+
ttf: {
|
|
96
|
+
format: "truetype",
|
|
97
|
+
mimeType: "font/ttf"
|
|
98
|
+
},
|
|
99
|
+
eot: {
|
|
100
|
+
format: "embedded-opentype",
|
|
101
|
+
mimeType: "font/eot"
|
|
102
|
+
},
|
|
103
|
+
svg: {
|
|
104
|
+
format: "svg",
|
|
105
|
+
mimeType: "image/svg+xml"
|
|
106
|
+
}
|
|
107
|
+
}[lastDot !== -1 ? src.slice(lastDot + 1).toLowerCase() : ""] || {
|
|
108
|
+
format: "woff2",
|
|
109
|
+
mimeType: "font/woff2"
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Font Component
|
|
114
|
+
*
|
|
115
|
+
* Uses fonts from URLs (public paths, HTTP/HTTPS, or data URIs).
|
|
116
|
+
* No filesystem loading or base64 conversion - fonts must be served as static files.
|
|
117
|
+
*
|
|
118
|
+
* Usage:
|
|
119
|
+
* ```tsx
|
|
120
|
+
* // Absolute public URL
|
|
121
|
+
* <Font
|
|
122
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
123
|
+
* src="/fonts/sans/Thmanyahsans12-Regular.otf"
|
|
124
|
+
* fontWeight={400}
|
|
125
|
+
* />
|
|
126
|
+
*
|
|
127
|
+
* // Relative URL (relative to current page)
|
|
128
|
+
* <Font
|
|
129
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
130
|
+
* src="fonts/sans/Thmanyahsans12-Regular.otf"
|
|
131
|
+
* fontWeight={400}
|
|
132
|
+
* />
|
|
133
|
+
*
|
|
134
|
+
* // HTTP/HTTPS URL
|
|
135
|
+
* <Font
|
|
136
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
137
|
+
* src="https://example.com/font.otf"
|
|
138
|
+
* fontWeight={400}
|
|
139
|
+
* />
|
|
140
|
+
*
|
|
141
|
+
* // Base64 data URI
|
|
142
|
+
* <Font
|
|
143
|
+
* fontFamily="Thmanyah sans 1.2"
|
|
144
|
+
* src="data:font/otf;base64,ABC123..."
|
|
145
|
+
* fontWeight={400}
|
|
146
|
+
* />
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* The component MUST be placed inside the <head> tag.
|
|
150
|
+
*/
|
|
151
|
+
function Font({ fontFamily, fallbackFontFamily, src, webFont, format, fontStyle = "normal", fontWeight = 400 }) {
|
|
152
|
+
let fontSrc = "";
|
|
153
|
+
let detectedFormat = format;
|
|
154
|
+
let mimeType;
|
|
155
|
+
if (src) if (src.startsWith("data:")) {
|
|
156
|
+
const match = src.match(/^data:([^;]+);base64,(.+)$/);
|
|
157
|
+
if (match) {
|
|
158
|
+
mimeType = match[1];
|
|
159
|
+
detectedFormat = {
|
|
160
|
+
"font/woff2": "woff2",
|
|
161
|
+
"font/woff": "woff",
|
|
162
|
+
"font/otf": "opentype",
|
|
163
|
+
"font/ttf": "truetype",
|
|
164
|
+
"font/eot": "embedded-opentype",
|
|
165
|
+
"image/svg+xml": "svg"
|
|
166
|
+
}[mimeType] || "woff2";
|
|
167
|
+
fontSrc = `src: url(${src});`;
|
|
168
|
+
} else fontSrc = `src: url(${src});`;
|
|
169
|
+
} else if (src.startsWith("http://") || src.startsWith("https://")) {
|
|
170
|
+
detectedFormat = detectFontFormat(src, format).format;
|
|
171
|
+
fontSrc = `src: url(${src})${format ? ` format('${detectedFormat}')` : ""};`;
|
|
172
|
+
} else {
|
|
173
|
+
detectedFormat = detectFontFormat(src, format).format;
|
|
174
|
+
fontSrc = `src: url(${src})${format ? ` format('${detectedFormat}')` : ""};`;
|
|
175
|
+
}
|
|
176
|
+
else if (webFont) fontSrc = webFont.url.startsWith("data:") ? `src: url(${webFont.url});` : `src: url(${webFont.url}) format('${webFont.format}');`;
|
|
177
|
+
return /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: `
|
|
178
|
+
@font-face {
|
|
179
|
+
font-family: '${fontFamily}';
|
|
180
|
+
font-style: ${fontStyle};
|
|
181
|
+
font-weight: ${fontWeight};
|
|
182
|
+
${fontSrc}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
* {
|
|
186
|
+
font-family: '${fontFamily}', ${Array.isArray(fallbackFontFamily) ? fallbackFontFamily.join(", ") : fallbackFontFamily};
|
|
187
|
+
}
|
|
188
|
+
` } });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region src/page-break.tsx
|
|
193
|
+
/**
|
|
194
|
+
* PageBreak component adds page break CSS for PDF generation.
|
|
195
|
+
* Can be used as a self-closing element or wrapper.
|
|
196
|
+
*/
|
|
197
|
+
const PageBreak = ({ type = "after" }) => {
|
|
198
|
+
return /* @__PURE__ */ jsx("div", { style: type === "after" ? {
|
|
199
|
+
pageBreakAfter: "always",
|
|
200
|
+
breakAfter: "page"
|
|
201
|
+
} : type === "before" ? {
|
|
202
|
+
pageBreakBefore: "always",
|
|
203
|
+
breakBefore: "page"
|
|
204
|
+
} : {
|
|
205
|
+
pageBreakInside: "avoid",
|
|
206
|
+
breakInside: "avoid"
|
|
207
|
+
} });
|
|
208
|
+
};
|
|
209
|
+
PageBreak.displayName = "PageBreak";
|
|
210
|
+
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/print-styles.tsx
|
|
213
|
+
/**
|
|
214
|
+
* PrintStyles component wraps styles in @media print for PDF generation.
|
|
215
|
+
* Should be placed inside the <Head> component.
|
|
216
|
+
*/
|
|
217
|
+
const PrintStyles = ({ preserveColors = false, removeShadows = false, pageBreakAfter = [], pageBreakBefore = [], pageBreakInside = [], children }) => {
|
|
218
|
+
const styles = [];
|
|
219
|
+
if (preserveColors) styles.push(`* {
|
|
220
|
+
-webkit-print-color-adjust: exact !important;
|
|
221
|
+
print-color-adjust: exact !important;
|
|
222
|
+
color-adjust: exact !important;
|
|
223
|
+
}`);
|
|
224
|
+
if (removeShadows) styles.push(`.sheet, .page, .card, .box {
|
|
225
|
+
box-shadow: none !important;
|
|
226
|
+
}`);
|
|
227
|
+
if (pageBreakAfter.length > 0) styles.push(`${pageBreakAfter.join(", ")} {
|
|
228
|
+
page-break-after: always !important;
|
|
229
|
+
break-after: page !important;
|
|
230
|
+
}`);
|
|
231
|
+
if (pageBreakBefore.length > 0) styles.push(`${pageBreakBefore.join(", ")} {
|
|
232
|
+
page-break-before: avoid !important;
|
|
233
|
+
break-before: avoid !important;
|
|
234
|
+
}`);
|
|
235
|
+
if (pageBreakInside.length > 0) styles.push(`${pageBreakInside.join(", ")} {
|
|
236
|
+
page-break-inside: avoid !important;
|
|
237
|
+
break-inside: avoid !important;
|
|
238
|
+
}`);
|
|
239
|
+
const customStyles = children ? typeof children === "string" ? children : "" : "";
|
|
240
|
+
return /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: `@media print {
|
|
241
|
+
${styles.join("\n\n")}${customStyles ? `\n\n${customStyles}` : ""}
|
|
242
|
+
}` } });
|
|
243
|
+
};
|
|
244
|
+
PrintStyles.displayName = "PrintStyles";
|
|
245
|
+
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/theme.tsx
|
|
248
|
+
/**
|
|
249
|
+
* Theme component that applies CSS variables and styles based on theme variant.
|
|
250
|
+
* Uses :root CSS variables scoped to the variant class for theme switching.
|
|
251
|
+
* Should be placed inside the <Head> component.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```tsx
|
|
255
|
+
* <Theme variant="light" css={`
|
|
256
|
+
* --color-bg: #ffffff;
|
|
257
|
+
* --color-text: #000000;
|
|
258
|
+
* `} />
|
|
259
|
+
* <Theme variant="dark" css={`
|
|
260
|
+
* --color-bg: #1a1a1a;
|
|
261
|
+
* --color-text: #ffffff;
|
|
262
|
+
* `} />
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
const Theme = ({ css, variant }) => {
|
|
266
|
+
if (variant === "default") return /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: `
|
|
267
|
+
:root {
|
|
268
|
+
${css}
|
|
269
|
+
}
|
|
270
|
+
` } });
|
|
271
|
+
return /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: `
|
|
272
|
+
:root.${variant},
|
|
273
|
+
.${variant} {
|
|
274
|
+
${css}
|
|
275
|
+
}
|
|
276
|
+
` } });
|
|
277
|
+
};
|
|
278
|
+
Theme.displayName = "Theme";
|
|
279
|
+
|
|
280
|
+
//#endregion
|
|
281
|
+
export { Body, Document, Font, Head, Html, PageBreak, PrintStyles, Theme };
|
|
282
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["Document: React.FC<Readonly<DocumentProps>>","detectedFormat: FontFormat | undefined","mimeType: string | undefined","PageBreak: React.FC<Readonly<PageBreakProps>>","PrintStyles: React.FC<Readonly<PrintStylesProps>>","styles: string[]","Theme: React.FC<Readonly<ThemeProps>>"],"sources":["../src/body.tsx","../src/head.tsx","../src/html.tsx","../src/document.tsx","../src/font.tsx","../src/page-break.tsx","../src/print-styles.tsx","../src/theme.tsx"],"sourcesContent":["import * as React from 'react';\n\nexport type BodyProps = Readonly<React.HtmlHTMLAttributes<HTMLBodyElement>>;\n\nexport const Body = React.forwardRef<HTMLBodyElement, BodyProps>(\n ({ children, ...props }, ref) => (\n <body {...props} ref={ref}>\n {children}\n </body>\n ),\n);\n\nBody.displayName = 'Body';\n","import * as React from 'react';\n\nexport type HeadProps = Readonly<React.ComponentPropsWithoutRef<'head'>>;\n\nexport const Head = React.forwardRef<HTMLHeadElement, HeadProps>(\n ({ children, ...props }, ref) => (\n <head {...props} ref={ref}>\n <meta content=\"text/html; charset=UTF-8\" httpEquiv=\"Content-Type\" />\n {children}\n </head>\n ),\n);\n\nHead.displayName = 'Head';\n","import * as React from 'react';\n\nexport type HtmlProps = Readonly<React.ComponentPropsWithoutRef<'html'>>;\n\nexport const Html = React.forwardRef<HTMLHtmlElement, HtmlProps>(\n ({ children, lang = 'en', dir = 'ltr', ...props }, ref) => (\n <html {...props} dir={dir} lang={lang} ref={ref}>\n {children}\n </html>\n ),\n);\n\nHtml.displayName = 'Html';\n","import type * as React from 'react';\nimport { Body } from './body';\nimport { Head } from './head';\nimport { Html } from './html';\n\nexport interface DocumentProps {\n /** Document language */\n lang?: string;\n /** Text direction */\n dir?: 'ltr' | 'rtl';\n /** Head content */\n head?: React.ReactNode;\n /** Body content */\n children: React.ReactNode;\n /** Additional HTML attributes */\n htmlProps?: React.HtmlHTMLAttributes<HTMLHtmlElement>;\n /** Additional head attributes */\n headProps?: React.HtmlHTMLAttributes<HTMLHeadElement>;\n /** Additional body attributes */\n bodyProps?: React.HtmlHTMLAttributes<HTMLBodyElement>;\n}\n\n/**\n * Document component for PDF generation.\n * Convenience wrapper that provides proper HTML5 document structure.\n */\nexport const Document: React.FC<Readonly<DocumentProps>> = ({\n lang = 'en',\n dir = 'ltr',\n head,\n children,\n htmlProps,\n headProps,\n bodyProps,\n}) => {\n return (\n <Html lang={lang} dir={dir} {...htmlProps}>\n <Head {...headProps}>\n <meta charSet=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n {head}\n </Head>\n <Body {...bodyProps}>{children}</Body>\n </Html>\n );\n};\n\nDocument.displayName = 'Document';\n","import type * as React from 'react';\n\ntype FallbackFont =\n | 'Arial'\n | 'Helvetica'\n | 'Verdana'\n | 'Georgia'\n | 'Times New Roman'\n | 'serif'\n | 'sans-serif'\n | 'monospace'\n | 'cursive'\n | 'fantasy';\n\ntype FontFormat =\n | 'woff'\n | 'woff2'\n | 'truetype'\n | 'opentype'\n | 'embedded-opentype'\n | 'svg';\n\ntype FontWeight = React.CSSProperties['fontWeight'];\ntype FontStyle = React.CSSProperties['fontStyle'];\n\nexport interface FontProps {\n /** The font you want to use. NOTE: Do not insert multiple fonts here, use fallbackFontFamily for that */\n fontFamily: string;\n /** An array is possible, but the order of the array is the priority order */\n fallbackFontFamily: FallbackFont | FallbackFont[];\n /**\n * Font source - must be a URL:\n * - Absolute URL path (e.g., \"/fonts/sans/Thmanyahsans12-Regular.otf\")\n * - Relative URL path (e.g., \"fonts/sans/Thmanyahsans12-Regular.otf\")\n * - HTTP/HTTPS URL (e.g., \"https://example.com/font.woff2\")\n * - Base64 data URI (e.g., \"data:font/woff2;base64,ABC123...\")\n *\n * The URL is used as-is - no automatic path conversion.\n * Templates are responsible for providing the correct path.\n */\n src?: string;\n /**\n * Web font URL or base64 data URI (legacy prop, use src instead).\n * Format examples:\n * - URL: \"https://example.com/font.woff2\" or \"/fonts/font.woff2\"\n * - Base64: \"data:font/woff2;base64,ABC123...\"\n */\n webFont?: {\n url: string;\n format: FontFormat;\n };\n /** Font format - auto-detected from file extension if not provided */\n format?: FontFormat;\n /** Default: 'normal' */\n fontStyle?: FontStyle;\n /** Default: 400 */\n fontWeight?: FontWeight;\n}\n\n/**\n * Detect font format from file extension or URL\n */\nfunction detectFontFormat(\n src: string,\n format?: FontFormat,\n): {\n format: FontFormat;\n mimeType: string;\n} {\n if (format) {\n const mimeMap: Record<FontFormat, string> = {\n woff: 'font/woff',\n woff2: 'font/woff2',\n truetype: 'font/ttf',\n opentype: 'font/otf',\n 'embedded-opentype': 'font/eot',\n svg: 'image/svg+xml',\n };\n return { format, mimeType: mimeMap[format] };\n }\n\n // Auto-detect from extension\n const lastDot = src.lastIndexOf('.');\n const ext = lastDot !== -1 ? src.slice(lastDot + 1).toLowerCase() : '';\n const formatMap: Record<string, { format: FontFormat; mimeType: string }> = {\n woff2: { format: 'woff2', mimeType: 'font/woff2' },\n woff: { format: 'woff', mimeType: 'font/woff' },\n otf: { format: 'opentype', mimeType: 'font/otf' },\n ttf: { format: 'truetype', mimeType: 'font/ttf' },\n eot: { format: 'embedded-opentype', mimeType: 'font/eot' },\n svg: { format: 'svg', mimeType: 'image/svg+xml' },\n };\n\n return formatMap[ext] || { format: 'woff2', mimeType: 'font/woff2' };\n}\n\n/**\n * Font Component\n *\n * Uses fonts from URLs (public paths, HTTP/HTTPS, or data URIs).\n * No filesystem loading or base64 conversion - fonts must be served as static files.\n *\n * Usage:\n * ```tsx\n * // Absolute public URL\n * <Font\n * fontFamily=\"Thmanyah sans 1.2\"\n * src=\"/fonts/sans/Thmanyahsans12-Regular.otf\"\n * fontWeight={400}\n * />\n *\n * // Relative URL (relative to current page)\n * <Font\n * fontFamily=\"Thmanyah sans 1.2\"\n * src=\"fonts/sans/Thmanyahsans12-Regular.otf\"\n * fontWeight={400}\n * />\n *\n * // HTTP/HTTPS URL\n * <Font\n * fontFamily=\"Thmanyah sans 1.2\"\n * src=\"https://example.com/font.otf\"\n * fontWeight={400}\n * />\n *\n * // Base64 data URI\n * <Font\n * fontFamily=\"Thmanyah sans 1.2\"\n * src=\"data:font/otf;base64,ABC123...\"\n * fontWeight={400}\n * />\n * ```\n *\n * The component MUST be placed inside the <head> tag.\n */\nexport function Font({\n fontFamily,\n fallbackFontFamily,\n src,\n webFont,\n format,\n fontStyle = 'normal',\n fontWeight = 400,\n}: FontProps): React.JSX.Element {\n let fontSrc = '';\n let detectedFormat: FontFormat | undefined = format;\n let mimeType: string | undefined;\n\n if (src) {\n if (src.startsWith('data:')) {\n const match = src.match(/^data:([^;]+);base64,(.+)$/);\n if (match) {\n mimeType = match[1];\n const mimeToFormat: Record<string, FontFormat> = {\n 'font/woff2': 'woff2',\n 'font/woff': 'woff',\n 'font/otf': 'opentype',\n 'font/ttf': 'truetype',\n 'font/eot': 'embedded-opentype',\n 'image/svg+xml': 'svg',\n };\n detectedFormat = mimeToFormat[mimeType] || 'woff2';\n fontSrc = `src: url(${src});`;\n } else {\n fontSrc = `src: url(${src});`;\n }\n } else if (src.startsWith('http://') || src.startsWith('https://')) {\n const detected = detectFontFormat(src, format);\n detectedFormat = detected.format;\n fontSrc = `src: url(${src})${format ? ` format('${detectedFormat}')` : ''};`;\n } else {\n const detected = detectFontFormat(src, format);\n detectedFormat = detected.format;\n fontSrc = `src: url(${src})${format ? ` format('${detectedFormat}')` : ''};`;\n }\n } else if (webFont) {\n fontSrc = webFont.url.startsWith('data:')\n ? `src: url(${webFont.url});`\n : `src: url(${webFont.url}) format('${webFont.format}');`;\n }\n\n const style = `\n @font-face {\n font-family: '${fontFamily}';\n font-style: ${fontStyle};\n font-weight: ${fontWeight};\n ${fontSrc}\n }\n\n * {\n font-family: '${fontFamily}', ${\n Array.isArray(fallbackFontFamily)\n ? fallbackFontFamily.join(', ')\n : fallbackFontFamily\n };\n }\n `;\n return <style dangerouslySetInnerHTML={{ __html: style }} />;\n}\n","import type * as React from 'react';\n\nexport interface PageBreakProps {\n /** Type of page break */\n type?: 'after' | 'before' | 'avoid';\n}\n\n/**\n * PageBreak component adds page break CSS for PDF generation.\n * Can be used as a self-closing element or wrapper.\n */\nexport const PageBreak: React.FC<Readonly<PageBreakProps>> = ({\n type = 'after',\n}) => {\n const style: React.CSSProperties =\n type === 'after'\n ? { pageBreakAfter: 'always', breakAfter: 'page' }\n : type === 'before'\n ? { pageBreakBefore: 'always', breakBefore: 'page' }\n : { pageBreakInside: 'avoid', breakInside: 'avoid' };\n\n return <div style={style} />;\n};\n\nPageBreak.displayName = 'PageBreak';\n","import type * as React from 'react';\n\nexport interface PrintStylesProps {\n /** Preserve colors when printing */\n preserveColors?: boolean;\n /** Remove shadows from elements */\n removeShadows?: boolean;\n /** Selectors that should have page-break-after: always */\n pageBreakAfter?: string[];\n /** Selectors that should have page-break-before: avoid */\n pageBreakBefore?: string[];\n /** Selectors that should have page-break-inside: avoid */\n pageBreakInside?: string[];\n /** Custom print styles */\n children?: React.ReactNode;\n}\n\n/**\n * PrintStyles component wraps styles in @media print for PDF generation.\n * Should be placed inside the <Head> component.\n */\nexport const PrintStyles: React.FC<Readonly<PrintStylesProps>> = ({\n preserveColors = false,\n removeShadows = false,\n pageBreakAfter = [],\n pageBreakBefore = [],\n pageBreakInside = [],\n children,\n}) => {\n const styles: string[] = [];\n\n if (preserveColors) {\n styles.push(`* {\n -webkit-print-color-adjust: exact !important;\n print-color-adjust: exact !important;\n color-adjust: exact !important;\n}`);\n }\n\n if (removeShadows) {\n styles.push(`.sheet, .page, .card, .box {\n box-shadow: none !important;\n}`);\n }\n\n if (pageBreakAfter.length > 0) {\n styles.push(`${pageBreakAfter.join(', ')} {\n page-break-after: always !important;\n break-after: page !important;\n}`);\n }\n\n if (pageBreakBefore.length > 0) {\n styles.push(`${pageBreakBefore.join(', ')} {\n page-break-before: avoid !important;\n break-before: avoid !important;\n}`);\n }\n\n if (pageBreakInside.length > 0) {\n styles.push(`${pageBreakInside.join(', ')} {\n page-break-inside: avoid !important;\n break-inside: avoid !important;\n}`);\n }\n\n const customStyles = children\n ? typeof children === 'string'\n ? children\n : ''\n : '';\n\n const css = `@media print {\n${styles.join('\\n\\n')}${customStyles ? `\\n\\n${customStyles}` : ''}\n}`;\n\n return <style dangerouslySetInnerHTML={{ __html: css }} />;\n};\n\nPrintStyles.displayName = 'PrintStyles';\n","import type * as React from 'react';\n\nexport interface ThemeProps {\n /** CSS variables and styles for this theme variant */\n css: string;\n /** Theme variant name (e.g., 'light', 'dark', 'blue', 'green', etc.) */\n variant: string;\n}\n\n/**\n * Theme component that applies CSS variables and styles based on theme variant.\n * Uses :root CSS variables scoped to the variant class for theme switching.\n * Should be placed inside the <Head> component.\n *\n * @example\n * ```tsx\n * <Theme variant=\"light\" css={`\n * --color-bg: #ffffff;\n * --color-text: #000000;\n * `} />\n * <Theme variant=\"dark\" css={`\n * --color-bg: #1a1a1a;\n * --color-text: #ffffff;\n * `} />\n * ```\n */\nexport const Theme: React.FC<Readonly<ThemeProps>> = ({ css, variant }) => {\n if (variant === 'default') {\n const scopedCss = `\n :root {\n ${css}\n }\n `;\n return <style dangerouslySetInnerHTML={{ __html: scopedCss }} />;\n }\n\n const scopedCss = `\n :root.${variant},\n .${variant} {\n ${css}\n }\n `;\n\n return <style dangerouslySetInnerHTML={{ __html: scopedCss }} />;\n};\n\nTheme.displayName = 'Theme';\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ahmedrowaihi/pdf-forge-primitive",
|
|
3
|
+
"version": "1.0.0-canary.0",
|
|
4
|
+
"description": "Primitive components for PDF generation: Html, Head, Body, Font, Document, PrintStyles, PageBreak, Theme",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist/**"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"dependencies": {},
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"react": "^18.0 || ^19.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"typescript": "5.9.3",
|
|
18
|
+
"tsdown": "0.15.12",
|
|
19
|
+
"tsconfig": "1.0.0-canary.0"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsdown src/index.ts --format esm,cjs --dts --external react",
|
|
23
|
+
"clean": "rm -rf dist"
|
|
24
|
+
}
|
|
25
|
+
}
|