react-vector-pdf 0.3.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.
package/README.md ADDED
@@ -0,0 +1,254 @@
1
+ # react-vector-pdf
2
+
3
+ **Vector-based, React-driven PDF generation.**
4
+
5
+ `react-vector-pdf` is a React wrapper around **jsPDF** that allows you to build complex, multi-page PDFs using declarative React components. Unlike HTML-to-Canvas solutions, this library renders **selectable, searchable vector text**, resulting in smaller file sizes and crystal-clear output at any zoom level.
6
+
7
+ ## Features
8
+
9
+ - **Vector Text**: High-quality, selectable, and searchable text (no canvas rasterization).
10
+ - **React Component API**: Build PDFs using declarative components like `<PdfDocument>`, `<PdfText>`, `<PdfTable>`, `<PdfView>`, etc.
11
+ - **Smart Layout Engine**:
12
+ - **Auto-Paging**: Content that exceeds the page height automatically moves to the next page.
13
+ - **Flow & Absolute Positioning**: Stack elements naturally or place them at fixed coordinates.
14
+ - **Advanced Components**:
15
+ - **PdfTable**: Supports auto-wrapping, **row spans**, **col spans**, **vertical alignment**, custom cell styling, and **intelligent page breaking** (keeps spanned rows together).
16
+ - **PdfImage**: Renders images from URLs with options for flow or absolute positioning. Automatically handles page breaks in flow mode.
17
+ - **PdfList**: Bullet and numbered lists with auto-wrapping.
18
+ - **PdfView/PdfBox**: Container components with support for borders, background colors, and granular **padding/margin** (e.g., `paddingTop`, `marginBottom`).
19
+ - **Global Document Options**:
20
+ - **Formatting**: A4, Letter, distinct margins, base fonts.
21
+ - **Headers & Footers**: Custom render functions.
22
+ - **Page Numbering**: Highly configurable (presets, templates, scopes, formats like Roman/Arabic).
23
+ - **Center Labels**: Watermarks or classification labels in header/footer.
24
+ - **Metadata**: Set PDF title, author, subject, and keywords.
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ npm install react-vector-pdf
30
+ ```
31
+
32
+ ## Quick Start
33
+
34
+ Wrap your application in the `PdfDocument` component. This component serves as the context provider and renderer.
35
+
36
+ ```tsx
37
+ import React from "react";
38
+ import { PdfDocument, PdfText, PdfTable } from "pdfify-core";
39
+
40
+ const MyPdf = () => {
41
+ const handleSave = (pdf) => {
42
+ pdf.save("my-document.pdf");
43
+ };
44
+
45
+ return (
46
+ <PdfDocument
47
+ onReady={handleSave}
48
+ options={{
49
+ margin: { top: 20, right: 15, bottom: 15, left: 15 },
50
+ font: { size: 12, name: "helvetica" }, // default font
51
+ lineHeight: 1.25,
52
+ }}
53
+ >
54
+ <PdfText fontSize={18} fontStyle="bold" spacingBelow={10}>
55
+ Hello World
56
+ </PdfText>
57
+
58
+ <PdfText>This is a vector PDF generated from React components.</PdfText>
59
+ </PdfDocument>
60
+ );
61
+ };
62
+ ```
63
+
64
+ ## Components
65
+
66
+ ### 1. `PdfText`
67
+
68
+ Renders a paragraph of text. Automatically wraps to the content width.
69
+
70
+ **Props:**
71
+
72
+ - `fontSize` (number): Font size in points.
73
+ - `fontStyle` ('normal' | 'bold' | 'italic' | 'bolditalic'): Font style.
74
+ - `color` (string): Hex color code (e.g., "#FF0000").
75
+ - `align` ('left' | 'center' | 'right' | 'justify'): Text alignment.
76
+ - `lineHeight` (number): Line height multiplier.
77
+ - `spacingBelow` (number): Vertical space (mm) to add after the paragraph.
78
+
79
+ ```tsx
80
+ <PdfText
81
+ fontSize={14}
82
+ fontStyle="bold"
83
+ color="#333333"
84
+ align="center"
85
+ spacingBelow={5}
86
+ >
87
+ I am a centered, bold heading.
88
+ </PdfText>
89
+ ```
90
+
91
+ ### 2. `PdfTable`
92
+
93
+ A robust table component designed for dynamic data.
94
+
95
+ **Key Features:**
96
+
97
+ - **Auto-layout**: Columns can use explicit widths or percentage-based widths.
98
+ - **Spanning**: Support for `rowSpan` and `colSpan`.
99
+ - **Page Breaking**: Smartly handles page breaks, ensuring rows with rowspans are kept together if possible.
100
+ - **Styling**: Supports granular padding (`paddingTop`, `paddingLeft`, etc.) and borders.
101
+ - **Alignment**: Supports both horizontal (`align`) and vertical (`verticalAlign`) alignment.
102
+
103
+ **Props:**
104
+
105
+ - `data`: Array of objects or cell definitions.
106
+ - `columns`: Array of column definitions `{ header, accessor, width, align }`.
107
+ - `width`: Table width (number or string percentage like "100%").
108
+ - `borderWidth`, `borderColor`: Global table border settings.
109
+ - `headerStyle`, `rowStyle`: Default styles for headers and rows.
110
+ - `cellPadding`: Default padding for cells (number or object).
111
+
112
+ **Complex Cell Example:**
113
+
114
+ ```tsx
115
+ <PdfTable
116
+ width="100%"
117
+ columns={[
118
+ { header: "ID", accessor: "id", width: 10 },
119
+ { header: "Description", accessor: "desc", width: "auto" },
120
+ { header: "Price", accessor: "price", width: 20, align: "right" },
121
+ ]}
122
+ data={[
123
+ // Simple Row
124
+ { id: 1, desc: "Standard Item", price: "$10.00" },
125
+
126
+ // Row with Spans and Custom Styles
127
+ {
128
+ id: "2", // Simple cell
129
+ desc: {
130
+ content: "I span 2 rows and am vertically centered",
131
+ rowSpan: 2,
132
+ style: {
133
+ fillColor: "#e0f2fe",
134
+ align: "center",
135
+ verticalAlign: "middle", // 'top' | 'middle' | 'bottom'
136
+ paddingTop: 5,
137
+ },
138
+ },
139
+ price: "$20.00",
140
+ },
141
+ // Next row must account for the rowspan above
142
+ { id: 3, desc: null, price: "$5.00" },
143
+ ]}
144
+ />
145
+ ```
146
+
147
+ ### 3. `PdfImage`
148
+
149
+ Embed images (JPEG/PNG).
150
+
151
+ **Props:**
152
+
153
+ - `src` (string): URL or Base64 data of the image.
154
+ - `x`, `y` (number): Optional fixed coordinates. If omitted, uses **flow layout**.
155
+ - `w`, `h` (number): Explicit dimensions. If only one is provided, aspect ratio is maintained.
156
+ - `align` ('left' | 'center' | 'right'): Horizontal alignment (flow mode only).
157
+ - `flow` (boolean): Force flow mode if necessary.
158
+
159
+ **Flow Behavior**: In flow mode, if an image is too tall for the remaining page space, it will automatically trigger a page break and render on the next page.
160
+
161
+ ```tsx
162
+ <PdfImage src="https://example.com/logo.png" h={20} align="center" />
163
+ ```
164
+
165
+ ### 4. `PdfView` & `PdfBox`
166
+
167
+ Containers for grouping content, adding borders, backgrounds, or margins/padding.
168
+
169
+ `PdfView` is akin to a block-level `div`, and `PdfBox` is similar but allows explicit positioning.
170
+
171
+ **Styling Support:**
172
+
173
+ - `margin` / `padding`: Shorthand (number or object `{top, right...}`).
174
+ - **Granular Props**: `marginTop`, `marginBottom`, `paddingLeft`, `paddingRight`, etc.
175
+ - `borderWidth`, `borderColor`: Draw borders.
176
+ - `fillColor`: Background color.
177
+
178
+ ```tsx
179
+ <PdfView
180
+ style={{
181
+ marginTop: 10,
182
+ marginBottom: 10,
183
+ padding: 5,
184
+ borderWidth: 0.2,
185
+ borderColor: "#ccc",
186
+ }}
187
+ >
188
+ <PdfText>Content inside a styled box.</PdfText>
189
+ </PdfView>
190
+ ```
191
+
192
+ ### 5. `PdfList`
193
+
194
+ Renders bulleted or numbered lists.
195
+
196
+ **Props:**
197
+
198
+ - `items`: Array of strings.
199
+ - `ordered`: Boolean. `true` for numbered (1., 2.), `false` for bullets.
200
+ - `indent`: Number (mm) for indentation. Default 5.
201
+ - `markerWidth`: Number (mm) for the marker area. Default 5.
202
+ - `spacing`: Number (mm) for space between items. Default 2.
203
+ - `style`: TextStyle object for the list items.
204
+
205
+ ```tsx
206
+ <PdfList
207
+ ordered={true}
208
+ indent={10}
209
+ spacing={3}
210
+ items={["First item", "Second item"]}
211
+ />
212
+ ```
213
+
214
+ ## Global Document Features
215
+
216
+ Configuration is passed via the `PdfDocument` props.
217
+
218
+ ### Page Numbering
219
+
220
+ Automatically adds page numbers to every page (or specific pages) with full customization.
221
+
222
+ ```tsx
223
+ <PdfDocument
224
+ pageNumbers={{
225
+ enabled: true,
226
+ position: "footer", // 'header' | 'footer'
227
+ align: "right",
228
+ format: "page-slash-total", // 'Page 1/10', '1/10', 'Page 1 of 10'
229
+ scope: "except-first", // 'all' | 'first-only' | 'except-first' | 'custom'
230
+ template: "Pg {page} / {total}", // Custom template support
231
+ style: { fontSize: 9, color: "#666" }
232
+ }}
233
+ >
234
+ ```
235
+
236
+ ### Center Label / Watermark
237
+
238
+ Adds a centered label (e.g., "CONFIDENTIAL") to the header or footer area.
239
+
240
+ ```tsx
241
+ <PdfDocument
242
+ centerLabel={{
243
+ enabled: true,
244
+ text: "CONFIDENTIAL",
245
+ position: "header",
246
+ scope: "all",
247
+ style: { color: "red", fontSize: 12 }
248
+ }}
249
+ >
250
+ ```
251
+
252
+ ## License
253
+
254
+ MIT
@@ -0,0 +1,10 @@
1
+ import { default as React } from 'react';
2
+ import { BoxStyle } from '../core/types';
3
+ export interface PdfBoxProps extends BoxStyle {
4
+ x?: number;
5
+ y?: number;
6
+ w?: number;
7
+ h?: number;
8
+ children?: React.ReactNode;
9
+ }
10
+ export declare const PdfBox: React.FC<PdfBoxProps>;
@@ -0,0 +1,12 @@
1
+ import { default as React } from 'react';
2
+ export interface PdfImageProps {
3
+ src: string;
4
+ x?: number;
5
+ y?: number;
6
+ w?: number;
7
+ h?: number;
8
+ mime?: "PNG" | "JPEG";
9
+ flow?: boolean;
10
+ align?: "left" | "center" | "right";
11
+ }
12
+ export declare const PdfImage: React.FC<PdfImageProps>;
@@ -0,0 +1,11 @@
1
+ import { default as React } from 'react';
2
+ import { TextStyle } from '../core/types';
3
+ export interface PdfListProps {
4
+ items: string[];
5
+ ordered?: boolean;
6
+ style?: TextStyle;
7
+ indent?: number;
8
+ markerWidth?: number;
9
+ spacing?: number;
10
+ }
11
+ export declare const PdfList: React.FC<PdfListProps>;
@@ -0,0 +1,22 @@
1
+ import { default as React } from 'react';
2
+ import { PdfRenderer } from '../core/PdfRenderer';
3
+ import { CenterLabelOptions, PDFOptions, PageNumberOptions } from '../core/types';
4
+ export declare const usePdf: () => PdfRenderer;
5
+ export interface PdfDocumentProps {
6
+ options?: PDFOptions;
7
+ header?: (ctx: PdfRenderer, page: number, total: number) => void;
8
+ footer?: (ctx: PdfRenderer, page: number, total: number) => void;
9
+ pageNumbers?: PageNumberOptions;
10
+ centerLabel?: CenterLabelOptions;
11
+ metadata?: {
12
+ title?: string;
13
+ author?: string;
14
+ subject?: string;
15
+ keywords?: string[];
16
+ };
17
+ children: React.ReactNode;
18
+ onReady?: (ctx: PdfRenderer) => void;
19
+ filename?: string;
20
+ autoSave?: boolean;
21
+ }
22
+ export declare const PdfDocument: React.FC<PdfDocumentProps>;
@@ -0,0 +1,33 @@
1
+ import { default as React } from 'react';
2
+ import { BoxStyle, TextStyle } from '../core/types';
3
+ export interface TableColumn {
4
+ header?: string;
5
+ accessor?: string | ((row: any) => React.ReactNode);
6
+ width?: number | string;
7
+ align?: "left" | "center" | "right";
8
+ id?: string;
9
+ }
10
+ export interface TableCell {
11
+ content?: string | number;
12
+ colSpan?: number;
13
+ rowSpan?: number;
14
+ style?: TextStyle & BoxStyle;
15
+ }
16
+ export interface PdfTableProps {
17
+ data: any[];
18
+ columns: TableColumn[];
19
+ width?: number | string;
20
+ borderWidth?: number;
21
+ borderColor?: string;
22
+ cellPadding?: number | {
23
+ top?: number;
24
+ right?: number;
25
+ bottom?: number;
26
+ left?: number;
27
+ };
28
+ headerStyle?: TextStyle & BoxStyle;
29
+ rowStyle?: TextStyle & BoxStyle;
30
+ alternateRowStyle?: TextStyle & BoxStyle;
31
+ headerHeight?: number;
32
+ }
33
+ export declare const PdfTable: React.FC<PdfTableProps>;
@@ -0,0 +1,10 @@
1
+ import { default as React } from 'react';
2
+ import { TextStyle } from '../core/types';
3
+ export interface PdfTextProps extends TextStyle {
4
+ children: string;
5
+ x?: number;
6
+ y?: number;
7
+ maxWidth?: number;
8
+ spacingBelow?: number;
9
+ }
10
+ export declare const PdfText: React.FC<PdfTextProps>;
@@ -0,0 +1,8 @@
1
+ import { default as React } from 'react';
2
+ import { ViewStyle } from '../core/types';
3
+ export interface PdfViewProps {
4
+ style?: ViewStyle;
5
+ children?: React.ReactNode;
6
+ debug?: boolean;
7
+ }
8
+ export declare const PdfView: React.FC<PdfViewProps>;
@@ -0,0 +1,7 @@
1
+ export { PdfBox } from './PdfBox';
2
+ export { PdfImage } from './PdfImage';
3
+ export { PdfList } from './PdfList';
4
+ export { PdfDocument } from './PdfProvider';
5
+ export { PdfTable } from './PdfTable';
6
+ export { PdfText } from './PdfText';
7
+ export { PdfView } from './PdfView';
@@ -0,0 +1,82 @@
1
+ import { jsPDF } from 'jspdf';
2
+ import { BoxStyle, PDFOptions, TextStyle } from './types';
3
+ type FontStyle = "normal" | "bold" | "italic" | "bolditalic";
4
+ export declare class PdfRenderer {
5
+ private pdf;
6
+ private pageWidth;
7
+ private pageHeight;
8
+ private cursorX;
9
+ private cursorY;
10
+ private contentWidth;
11
+ private margin;
12
+ private defaultFont;
13
+ private defaultColor;
14
+ private defaultLineHeight;
15
+ private headerDrawer?;
16
+ private footerDrawer?;
17
+ private pendingTasks;
18
+ private opQueue;
19
+ constructor(opts?: PDFOptions);
20
+ get instance(): jsPDF;
21
+ get width(): number;
22
+ get height(): number;
23
+ get contentLeft(): number;
24
+ get contentRight(): number;
25
+ get contentTop(): number;
26
+ get contentBottom(): number;
27
+ get contentHeight(): number;
28
+ get contentAreaWidth(): number;
29
+ get baseFont(): {
30
+ name?: string;
31
+ style: FontStyle;
32
+ size: number;
33
+ };
34
+ get baseLineHeight(): number;
35
+ resetFlowCursor(): void;
36
+ setHeaderFooter(header?: (pdf: jsPDF, pageNum: number, pageCount: number, renderer: PdfRenderer) => void, footer?: (pdf: jsPDF, pageNum: number, pageCount: number, renderer: PdfRenderer) => void): void;
37
+ private applyBaseFont;
38
+ addPage(): void;
39
+ private ensureSpace;
40
+ setTextStyle(style?: TextStyle): void;
41
+ textRaw(text: string, x: number, y: number, style?: TextStyle, maxWidth?: number, align?: TextStyle["align"]): void;
42
+ box(x: number, y: number, w: number, h: number, style?: BoxStyle): void;
43
+ line(x1: number, y1: number, x2: number, y2: number): void;
44
+ imageFromUrl(url: string, opts?: {
45
+ x?: number;
46
+ y?: number;
47
+ w?: number;
48
+ h?: number;
49
+ mime?: "PNG" | "JPEG";
50
+ align?: "left" | "center" | "right";
51
+ }): Promise<{
52
+ width: number;
53
+ height: number;
54
+ x: number | undefined;
55
+ y: number;
56
+ }>;
57
+ queueOperation(op: () => Promise<void> | void): void;
58
+ private registerTask;
59
+ waitForTasks(): Promise<void>;
60
+ private loadImageAsDataURL;
61
+ paragraph(text: string, style?: TextStyle, maxWidth?: number): number;
62
+ moveCursor(dx: number, dy: number): void;
63
+ setCursor(x: number, y: number): void;
64
+ getCursor(): {
65
+ x: number;
66
+ y: number;
67
+ };
68
+ getPageCount(): number;
69
+ applyHeaderFooter(): void;
70
+ measureText(text: string, style?: TextStyle, maxWidth?: number): {
71
+ width: number;
72
+ height: number;
73
+ };
74
+ setMetadata(metadata: {
75
+ title?: string;
76
+ author?: string;
77
+ subject?: string;
78
+ keywords?: string[];
79
+ }): void;
80
+ save(filename: string): void;
81
+ }
82
+ export {};
@@ -0,0 +1,80 @@
1
+ export type PDFFormat = "a4" | "letter" | [number, number];
2
+ export type Align = "left" | "center" | "right" | "justify";
3
+ export interface PDFOptions {
4
+ format?: PDFFormat;
5
+ orientation?: "p" | "l";
6
+ unit?: "mm" | "pt";
7
+ margin?: {
8
+ top: number;
9
+ right: number;
10
+ bottom: number;
11
+ left: number;
12
+ };
13
+ font?: {
14
+ name?: string;
15
+ style?: "normal" | "bold" | "italic" | "bolditalic";
16
+ size?: number;
17
+ };
18
+ color?: string;
19
+ lineHeight?: number;
20
+ }
21
+ export interface TextStyle {
22
+ fontSize?: number;
23
+ fontStyle?: "normal" | "bold" | "italic" | "bolditalic";
24
+ color?: string;
25
+ align?: Align;
26
+ verticalAlign?: "top" | "middle" | "bottom";
27
+ lineHeight?: number;
28
+ }
29
+ export interface BoxStyle {
30
+ borderColor?: string;
31
+ borderWidth?: number;
32
+ fillColor?: string;
33
+ radius?: number;
34
+ padding?: number | {
35
+ top?: number;
36
+ right?: number;
37
+ bottom?: number;
38
+ left?: number;
39
+ };
40
+ paddingTop?: number;
41
+ paddingRight?: number;
42
+ paddingBottom?: number;
43
+ paddingLeft?: number;
44
+ }
45
+ export interface ViewStyle extends BoxStyle {
46
+ margin?: number | {
47
+ top?: number;
48
+ right?: number;
49
+ bottom?: number;
50
+ left?: number;
51
+ };
52
+ marginTop?: number;
53
+ marginRight?: number;
54
+ marginBottom?: number;
55
+ marginLeft?: number;
56
+ width?: number | string;
57
+ height?: number;
58
+ }
59
+ export type PageNumberPreset = "page-slash-total" | "slash" | "page-of-total";
60
+ export interface PageNumberOptions {
61
+ enabled?: boolean;
62
+ position?: "header" | "footer";
63
+ align?: "left" | "right" | "center";
64
+ preset?: PageNumberPreset;
65
+ template?: string;
66
+ y?: number;
67
+ offsetX?: number;
68
+ style?: TextStyle;
69
+ scope?: "all" | "first-only" | "except-first";
70
+ format?: "arabic" | "roman-upper" | "roman-lower";
71
+ }
72
+ export interface CenterLabelOptions {
73
+ enabled?: boolean;
74
+ position?: "header" | "footer";
75
+ text: string;
76
+ scope?: "all" | "first-only" | "except-first" | number[];
77
+ y?: number;
78
+ offsetX?: number;
79
+ style?: TextStyle;
80
+ }
@@ -0,0 +1,14 @@
1
+ export declare const mm: (v: number) => number;
2
+ export declare function resolvePadding(p?: number | {
3
+ top?: number;
4
+ right?: number;
5
+ bottom?: number;
6
+ left?: number;
7
+ }): {
8
+ top: number;
9
+ right: number;
10
+ bottom: number;
11
+ left: number;
12
+ };
13
+ export declare function hexToRgb(hex: string): [number, number, number] | null;
14
+ export declare function toRoman(n: number, uppercase?: boolean): string;
@@ -0,0 +1,6 @@
1
+ import { default as React } from 'react';
2
+ interface CodeBlockProps {
3
+ code: string;
4
+ }
5
+ export declare const CodeBlock: React.FC<CodeBlockProps>;
6
+ export {};
@@ -0,0 +1,2 @@
1
+ import { default as React } from 'react';
2
+ export declare const DemoApp: React.FC;
@@ -0,0 +1,30 @@
1
+ import { default as React } from 'react';
2
+ export interface DemoPdfProps {
3
+ pnEnabled: boolean;
4
+ pnPos: "header" | "footer";
5
+ pnAlign: "left" | "center" | "right";
6
+ pnPreset: "page-slash-total" | "slash" | "page-of-total";
7
+ pnTemplate: string;
8
+ pnFormat: "arabic" | "roman-upper" | "roman-lower";
9
+ pnScope: "all" | "first-only" | "except-first" | "custom";
10
+ pnCustomPages: string;
11
+ pnY: string;
12
+ pnOffsetX: string;
13
+ pnFontSize: string;
14
+ pnColor: string;
15
+ clEnabled: boolean;
16
+ clPos: "header" | "footer";
17
+ clText: string;
18
+ clScope: "all" | "first-only" | "except-first" | "custom";
19
+ clCustomPages: string;
20
+ clY: string;
21
+ clOffsetX: string;
22
+ clFontSize: string;
23
+ clColor: string;
24
+ tableStriped: boolean;
25
+ tableBorderWidth: string;
26
+ tableHeaderColor: string;
27
+ onReady: (pdf: any) => void;
28
+ filename: string;
29
+ }
30
+ export declare const DemoPdfDocument: React.FC<DemoPdfProps>;
@@ -0,0 +1,4 @@
1
+ export * from './components';
2
+ export { PdfList } from './components/PdfList';
3
+ export { PdfRenderer } from './core/PdfRenderer';
4
+ export * from './core/types';
package/dist/main.d.ts ADDED
File without changes