podpdf 1.1.0 → 1.2.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/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # podpdf
2
2
 
3
- **Ultra-fast, zero-dependency PDF generation for Node.js & Bun**
3
+ **Ultra-fast, zero-dependency PDF generation for Node.js, Bun & Browser**
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/podpdf.svg)](https://www.npmjs.com/package/podpdf)
6
6
  [![bundle size](https://img.shields.io/badge/size-8KB-brightgreen)](https://bundlephobia.com/package/podpdf)
7
7
  [![zero dependencies](https://img.shields.io/badge/dependencies-0-blue)](https://www.npmjs.com/package/podpdf)
8
+ [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Support-yellow?logo=buy-me-a-coffee)](https://buymeacoffee.com/herostack)
8
9
 
9
10
  ```
10
11
  ~9 KB minified • Zero dependencies • 5x faster than jsPDF • TypeScript native
@@ -12,41 +13,45 @@
12
13
 
13
14
  ## Why podpdf?
14
15
 
15
- | Library | Size | Dependencies | Speed |
16
- |---------|------|--------------|-------|
17
- | **podpdf** | **~9 KB** | **0** | **5.5x** |
18
- | **podpdf/plus** | **~13 KB** | **0** | **5x** |
19
- | jsPDF | 290 KB | 2+ | 1x |
20
- | pdfkit | 1 MB | 10+ | 0.8x |
16
+ | Library | Size | Dependencies | Speed | Environment |
17
+ |---------|------|--------------|-------|-------------|
18
+ | **podpdf** | **~9 KB** | **0** | **5.5x** | Node.js/Bun |
19
+ | **podpdf/plus** | **~13 KB** | **0** | **5x** | Node.js/Bun |
20
+ | **podpdf/browser** | **~9 KB** | **0** | **5.5x** | **Browser** ✨ |
21
+ | jsPDF | 290 KB | 2+ | 1x | Browser/Node |
22
+ | pdfkit | 1 MB | 10+ | 0.8x | Node.js only |
21
23
 
22
24
  ## Feature Comparison
23
25
 
24
- | Feature | podpdf | podpdf/plus | jsPDF | pdfkit |
25
- |---------|:------:|:-----------:|:-----:|:------:|
26
- | **Text** | | | | ✅ |
27
- | **Text Styling (bold/italic)** | ✅ | ✅ | ✅ | ✅ |
28
- | **Text Wrap** | | ✅ | ✅ | ✅ |
29
- | **Text Alignment** | | ✅ | ✅ | ✅ |
30
- | **Rectangle** | | ✅ | ✅ | ✅ |
31
- | **Rounded Rectangle** | ✅ | ✅ | ✅ | ✅ |
32
- | **Circle** | | ✅ | ✅ | ✅ |
33
- | **Line (solid/dashed)** | | ✅ | | ✅ |
34
- | **Tables** | ✅ | ✅ | ⚠️ Plugin | ⚠️ Manual |
35
- | **Images (JPEG)** | ✅ | ✅ | ✅ | ✅ |
36
- | **Images (PNG)** | ❌ | ✅ | ✅ | ✅ |
37
- | **Links/URLs** | | | ✅ | |
38
- | **Multi-page** | | ✅ | ✅ | ✅ |
39
- | **Document Metadata** | ✅ | ✅ | ✅ | ✅ |
40
- | **Custom Fonts (TTF)** | ❌ | ✅ | ✅ | ✅ |
41
- | **Vector Graphics** | ⚠️ Basic | ⚠️ Basic | ✅ | ✅ Full |
42
- | **Forms/Fields** | | | | ✅ |
43
- | **Encryption** | ❌ | ❌ | ✅ | ✅ |
44
- | **TypeScript Native** | ✅ | ✅ | ❌ | ❌ |
45
- | **Fluent API** | | | ⚠️ Partial | ✅ |
46
- | **Browser Support** | | ✅ | ✅ | ❌ |
47
- | **Node.js/Bun** | | | | ✅ |
48
-
49
- > **podpdf** - Best balance of size, speed, and features for common use-cases (invoices, reports, tables)
26
+ ### Core Features
27
+
28
+ | Feature | podpdf | podpdf/plus | podpdf/browser |
29
+ |---------|:------:|:-----------:|:--------------:|
30
+ | Text & Styling | ✅ | ✅ | ✅ |
31
+ | Text Wrap & Alignment | ✅ | ✅ | ✅ |
32
+ | Shapes (rect, circle, line) | ✅ | ✅ | ✅ |
33
+ | Tables | ✅ | ✅ | ✅ |
34
+ | Images (JPEG) | ✅ | ✅ | ✅ |
35
+ | Images (PNG) | | ✅ | |
36
+ | Links/URLs | ✅ | ✅ | |
37
+ | Multi-page | ✅ | ✅ | ✅ |
38
+ | Document Metadata | ✅ | ✅ | ✅ |
39
+ | Custom Fonts (TTF) | | ✅ | |
40
+ | TypeScript Native | ✅ | ✅ | ✅ |
41
+
42
+ ### Environment Support
43
+
44
+ | Platform | podpdf | podpdf/plus | podpdf/browser |
45
+ |----------|:------:|:-----------:|:--------------:|
46
+ | Node.js / Bun | ✅ | ✅ | ❌ |
47
+ | Browser | Manual | Manual | Native |
48
+ | File System (.save) | ✅ | ✅ | ❌ |
49
+ | Browser Download (.download) | | | ✅ |
50
+
51
+ **Choose the right variant:**
52
+ - `podpdf` - Node.js/Bun for invoices, reports, tables
53
+ - `podpdf/plus` - Need PNG images or custom fonts
54
+ - `podpdf/browser` - Browser-native PDF generation
50
55
 
51
56
  ## Installation
52
57
 
@@ -60,8 +65,20 @@ pnpm add podpdf
60
65
  bun add podpdf
61
66
  ```
62
67
 
68
+ ## Package Variants
69
+
70
+ podpdf provides **3 specialized exports** for different use cases:
71
+
72
+ | Import | Use Case | Key Features |
73
+ |--------|----------|--------------|
74
+ | `podpdf` | Node.js/Bun | Core features, `.save()` to file |
75
+ | `podpdf/plus` | Node.js/Bun | PNG images + custom TTF fonts |
76
+ | `podpdf/browser` | Browser | Browser-native, `.download()` method |
77
+
63
78
  ## Quick Start
64
79
 
80
+ ### Node.js / Bun
81
+
65
82
  ```typescript
66
83
  import { pdf } from 'podpdf'
67
84
 
@@ -71,6 +88,17 @@ await pdf('A4')
71
88
  .save('hello.pdf')
72
89
  ```
73
90
 
91
+ ### Browser
92
+
93
+ ```typescript
94
+ import { pdf } from 'podpdf/browser'
95
+
96
+ pdf('A4')
97
+ .text('Hello Browser!', 50, 50, { size: 24, weight: 'bold' })
98
+ .rect(50, 80, 200, 100, { fill: '#3498db', radius: 10 })
99
+ .download('hello.pdf') // Triggers browser download
100
+ ```
101
+
74
102
  ## Features
75
103
 
76
104
  - **Text** - Multiple fonts, sizes, colors, alignment, text wrapping
@@ -432,12 +460,52 @@ Tested with 1000 document generations:
432
460
 
433
461
  ## Browser Support
434
462
 
435
- podpdf is designed for Node.js and Bun. For browser usage, the `build()` method returns a `Uint8Array` that can be converted to a Blob:
463
+ ### podpdf/browser (Recommended for Browser)
464
+
465
+ For browser environments, use the dedicated `podpdf/browser` module with built-in download support:
466
+
467
+ ```typescript
468
+ import { pdf } from 'podpdf/browser'
469
+
470
+ // Create and download PDF directly in browser
471
+ pdf('A4')
472
+ .text('Hello from Browser!', 50, 50, { size: 24, weight: 'bold' })
473
+ .rect(50, 80, 200, 100, { fill: '#3498db', radius: 10 })
474
+ .download('document.pdf') // Triggers browser download
475
+
476
+ // Or get as Blob for custom handling
477
+ const blob = doc.toBlob()
478
+
479
+ // Or get data URL for iframe preview
480
+ const dataURL = doc.toDataURL()
481
+ const iframe = document.createElement('iframe')
482
+ iframe.src = dataURL
483
+ ```
484
+
485
+ **Key differences in `podpdf/browser`:**
486
+ - ✅ No Node.js dependencies (fully browser-compatible)
487
+ - ✅ `download(filename)` - Directly triggers browser download
488
+ - ✅ `toBlob()` - Returns Blob object
489
+ - ✅ `toDataURL()` - Returns object URL for preview
490
+ - ❌ No `save()` method (use `download()` instead)
491
+ - ✅ Same API for text, shapes, tables, images, etc.
492
+
493
+ ### Using Core podpdf in Browser
494
+
495
+ The main `podpdf` module can also work in browsers using the `build()` method:
436
496
 
437
497
  ```typescript
498
+ import { pdf } from 'podpdf'
499
+
438
500
  const bytes = doc.build()
439
501
  const blob = new Blob([bytes], { type: 'application/pdf' })
440
502
  const url = URL.createObjectURL(blob)
503
+
504
+ // Trigger download
505
+ const a = document.createElement('a')
506
+ a.href = url
507
+ a.download = 'document.pdf'
508
+ a.click()
441
509
  ```
442
510
 
443
511
  ## License
@@ -0,0 +1,93 @@
1
+ export type Color = string | [number, number, number];
2
+ export type Align = 'left' | 'center' | 'right';
3
+ export type Weight = 'normal' | 'bold' | 'italic' | 'bolditalic';
4
+ export interface Size {
5
+ width: number;
6
+ height: number;
7
+ }
8
+ export interface TextOpts {
9
+ size?: number;
10
+ color?: Color;
11
+ align?: Align;
12
+ weight?: Weight;
13
+ maxWidth?: number;
14
+ font?: string;
15
+ }
16
+ export interface RectOpts {
17
+ fill?: Color;
18
+ stroke?: Color;
19
+ lineWidth?: number;
20
+ radius?: number;
21
+ }
22
+ export interface LineOpts {
23
+ color?: Color;
24
+ width?: number;
25
+ dash?: number[];
26
+ }
27
+ export interface CircleOpts {
28
+ fill?: Color;
29
+ stroke?: Color;
30
+ lineWidth?: number;
31
+ }
32
+ export interface ImageOpts {
33
+ width?: number;
34
+ height?: number;
35
+ }
36
+ export interface LinkOpts {
37
+ underline?: boolean;
38
+ color?: Color;
39
+ }
40
+ export interface TableCol {
41
+ header: string;
42
+ width?: number;
43
+ align?: Align;
44
+ }
45
+ export interface TableOpts {
46
+ columns: TableCol[];
47
+ headerBg?: Color;
48
+ headerColor?: Color;
49
+ borderColor?: Color;
50
+ fontSize?: number;
51
+ padding?: number;
52
+ }
53
+ export interface PDFMetadata {
54
+ title?: string;
55
+ author?: string;
56
+ subject?: string;
57
+ keywords?: string;
58
+ creator?: string;
59
+ }
60
+ export declare const SIZES: Record<string, Size>;
61
+ export declare class PDF {
62
+ private pages;
63
+ private cur;
64
+ private sz;
65
+ private meta?;
66
+ constructor(s?: Size | keyof typeof SIZES);
67
+ metadata(m: PDFMetadata): this;
68
+ page(s?: Size | keyof typeof SIZES): this;
69
+ text(t: string, x: number, y: number, o?: TextOpts): this;
70
+ rect(x: number, y: number, w: number, h: number, o?: RectOpts): this;
71
+ line(x1: number, y1: number, x2: number, y2: number, o?: LineOpts): this;
72
+ circle(cx: number, cy: number, r: number, o?: CircleOpts): this;
73
+ image(d: Uint8Array, x: number, y: number, o?: ImageOpts): this;
74
+ link(t: string, url: string, x: number, y: number, o?: LinkOpts): this;
75
+ table(data: string[][], x: number, y: number, o: TableOpts): this;
76
+ private ensure;
77
+ build(): Uint8Array;
78
+ /**
79
+ * Download PDF in browser using Blob and URL APIs
80
+ * @param filename - The filename for the downloaded PDF (default: 'document.pdf')
81
+ */
82
+ download(filename?: string): void;
83
+ /**
84
+ * Get PDF as Blob object (useful for preview or custom handling)
85
+ */
86
+ toBlob(): Blob;
87
+ /**
88
+ * Get PDF as data URL (useful for iframe preview)
89
+ */
90
+ toDataURL(): string;
91
+ }
92
+ export declare const pdf: (s?: Size | keyof typeof SIZES) => PDF;
93
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AACrD,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAA;AAC/C,MAAM,MAAM,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,YAAY,CAAA;AAChE,MAAM,WAAW,IAAI;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACvD,MAAM,WAAW,QAAQ;IAAG,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;AAC5H,MAAM,WAAW,QAAQ;IAAG,IAAI,CAAC,EAAE,KAAK,CAAC;IAAC,MAAM,CAAC,EAAE,KAAK,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;AAC/F,MAAM,WAAW,QAAQ;IAAG,KAAK,CAAC,EAAE,KAAK,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE;AAC5E,MAAM,WAAW,UAAU;IAAG,IAAI,CAAC,EAAE,KAAK,CAAC;IAAC,MAAM,CAAC,EAAE,KAAK,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;AAChF,MAAM,WAAW,SAAS;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;AAC9D,MAAM,WAAW,QAAQ;IAAG,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE;AAChE,MAAM,WAAW,QAAQ;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE;AAC3E,MAAM,WAAW,SAAS;IAAG,OAAO,EAAE,QAAQ,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;IAAC,WAAW,CAAC,EAAE,KAAK,CAAC;IAAC,WAAW,CAAC,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE;AACnJ,MAAM,WAAW,WAAW;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE;AAEvH,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAA+I,CAAA;AA2GtL,qBAAa,GAAG;IACd,OAAO,CAAC,KAAK,CAAc;IAAC,OAAO,CAAC,GAAG,CAAqB;IAAC,OAAO,CAAC,EAAE,CAAO;IAAC,OAAO,CAAC,IAAI,CAAC,CAAa;gBAC7F,CAAC,GAAE,IAAI,GAAG,MAAM,OAAO,KAAY;IAC/C,QAAQ,CAAC,CAAC,EAAE,WAAW;IAEvB,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,OAAO,KAAK;IAClC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,QAAQ;IAClD,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,QAAQ;IAC7D,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,QAAQ;IACjE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,UAAU;IACxD,KAAK,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,SAAS;IACxD,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,QAAQ;IAC/D,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS;IAC1D,OAAO,CAAC,MAAM;IAEd,KAAK,IAAI,UAAU;IAuDnB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,GAAE,MAAuB;IAkB1C;;OAEG;IACH,MAAM,IAAI,IAAI;IAKd;;OAEG;IACH,SAAS,IAAI,MAAM;CAKpB;AAED,eAAO,MAAM,GAAG,GAAI,IAAI,IAAI,GAAG,MAAM,OAAO,KAAK,QAAe,CAAA"}
@@ -0,0 +1,3 @@
1
+ var P={A4:{width:595,height:842},A3:{width:842,height:1191},A5:{width:420,height:595},LETTER:{width:612,height:792}},g={normal:"Helvetica",bold:"Helvetica-Bold",italic:"Helvetica-Oblique",bolditalic:"Helvetica-BoldOblique"},F=(q)=>{if(Array.isArray(q))return q;let B=q.replace("#","");if(B.length===3)return[parseInt(B[0]+B[0],16)/255,parseInt(B[1]+B[1],16)/255,parseInt(B[2]+B[2],16)/255];return[parseInt(B.slice(0,2),16)/255,parseInt(B.slice(2,4),16)/255,parseInt(B.slice(4,6),16)/255]},A=(q)=>{let[B,z,H]=F(q);return`${B.toFixed(3)} ${z.toFixed(3)} ${H.toFixed(3)} rg`},D=(q)=>{let[B,z,H]=F(q);return`${B.toFixed(3)} ${z.toFixed(3)} ${H.toFixed(3)} RG`},E=(q)=>q.replace(/\\/g,"\\\\").replace(/\(/g,"\\(").replace(/\)/g,"\\)"),J=(q)=>Number.isInteger(q)?q.toString():q.toFixed(2),j=(q,B)=>q.length*B*0.52;class R{c=[];s=0;e=new TextEncoder;w(q){let B=typeof q==="string"?this.e.encode(q):q;return this.c.push(B),this.s+=B.length,this}l(q){return this.w(q+`
2
+ `)}size(){return this.s}out(){let q=new Uint8Array(this.s),B=0;for(let z of this.c)q.set(z,B),B+=z.length;return q}}class v{w;h;c=[];f=new Set;imgs=[];links=[];ic=0;constructor(q){this.w=q.width,this.h=q.height}text(q,B,z,H={}){let C=H.size??12,K=H.weight??"normal",Q=H.color??"#000",W=H.align??"left",N=g[K];this.f.add(N);let G=this.h-z,L=B,X=H.maxWidth?this.wrap(q,H.maxWidth,C):[q];for(let V of X)L=W==="center"?B-j(V,C)/2:W==="right"?B-j(V,C):B,this.c.push("q","BT",A(Q),`/${N.replace("-","")} ${C} Tf`,`${J(L)} ${J(G)} Td`,`(${E(V)}) Tj`,"ET","Q"),G-=C*1.2;return this}rect(q,B,z,H,C={}){let K=this.h-B-H;if(this.c.push("q"),C.radius&&C.radius>0){let Q=Math.min(C.radius,z/2,H/2);this.c.push(`${J(q+Q)} ${J(K)} m`,`${J(q+z-Q)} ${J(K)} l`,`${J(q+z)} ${J(K)} ${J(q+z)} ${J(K+Q)} y`,`${J(q+z)} ${J(K+H-Q)} l`,`${J(q+z)} ${J(K+H)} ${J(q+z-Q)} ${J(K+H)} y`,`${J(q+Q)} ${J(K+H)} l`,`${J(q)} ${J(K+H)} ${J(q)} ${J(K+H-Q)} y`,`${J(q)} ${J(K+Q)} l`,`${J(q)} ${J(K)} ${J(q+Q)} ${J(K)} y`,"h")}else this.c.push(`${J(q)} ${J(K)} ${J(z)} ${J(H)} re`);if(C.fill)this.c.push(A(C.fill)),this.c.push(C.stroke?"B":"f");if(C.stroke){if(this.c.push(D(C.stroke)),C.lineWidth)this.c.push(`${C.lineWidth} w`);if(!C.fill)this.c.push("S")}return this.c.push("Q"),this}line(q,B,z,H,C={}){if(this.c.push("q"),C.color)this.c.push(D(C.color));if(C.width)this.c.push(`${C.width} w`);if(C.dash)this.c.push(`[${C.dash.join(" ")}] 0 d`);return this.c.push(`${J(q)} ${J(this.h-B)} m`,`${J(z)} ${J(this.h-H)} l`,"S","Q"),this}circle(q,B,z,H={}){let C=this.h-B;if(this.c.push("q"),this.c.push(`${J(q+z)} ${J(C)} m`,`${J(q+z)} ${J(C+z*0.5523)} ${J(q+z*0.5523)} ${J(C+z)} ${J(q)} ${J(C+z)} c`,`${J(q-z*0.5523)} ${J(C+z)} ${J(q-z)} ${J(C+z*0.5523)} ${J(q-z)} ${J(C)} c`,`${J(q-z*0.5523)} ${J(C-z)} ${J(q-z)} ${J(C-z*0.5523)} ${J(q)} ${J(C-z)} c`,`${J(q+z*0.5523)} ${J(C-z)} ${J(q+z)} ${J(C-z*0.5523)} ${J(q+z)} ${J(C)} c`),H.fill)this.c.push(A(H.fill)),this.c.push(H.stroke?"B":"f");if(H.stroke){if(this.c.push(D(H.stroke)),H.lineWidth)this.c.push(`${H.lineWidth} w`);if(!H.fill)this.c.push("S")}return this.c.push("Q"),this}image(q,B,z,H={}){let C=this.imgInfo(q);if(!C)return this;let K=H.width??C.w,Q=H.height??C.h,W=`I${++this.ic}`;return this.imgs.push({d:q,w:C.w,h:C.h,x:B,y:this.h-z-Q,id:W}),this.c.push("q",`${J(K)} 0 0 ${J(Q)} ${J(B)} ${J(this.h-z-Q)} cm`,`/${W} Do`,"Q"),this}link(q,B,z,H,C={}){let K=C.color??"#00E";this.text(q,z,H,{color:K,size:12});let W=j(q,12);if(C.underline!==!1)this.line(z,H+2,z+W,H+2,{color:K,width:0.5});return this.links.push({x:z,y:this.h-H-12,w:W,h:12,url:B}),this}table(q,B,z,H){let C=H.padding??8,K=H.fontSize??10,Q=K+C*2,W=H.columns.map((X,V)=>X.width??Math.max(j(X.header,K),...q.map((O)=>j(O[V]??"",K)))+C*2),N=W.reduce((X,V)=>X+V,0),G=z;this.rect(B,G,N,Q,{fill:H.headerBg??"#F0F0F0"});let L=B;for(let X=0;X<H.columns.length;X++){let V=H.columns[X];this.text(V.header,L+(V.align==="center"?W[X]/2:V.align==="right"?W[X]-C:C),G+C+K*0.8,{size:K,weight:"bold",color:H.headerColor??"#000",align:V.align}),L+=W[X]}G+=Q;for(let X=0;X<q.length;X++){this.rect(B,G,N,Q,{fill:X%2?"#F9F9F9":"#FFF"}),L=B;for(let V=0;V<H.columns.length;V++){let O=H.columns[V];this.text(q[X][V]??"",L+(O.align==="center"?W[V]/2:O.align==="right"?W[V]-C:C),G+C+K*0.8,{size:K,align:O.align}),L+=W[V]}G+=Q}return this.rect(B,z,N,Q*(q.length+1),{stroke:H.borderColor??"#CCC"}),this}wrap(q,B,z){let H=q.split(" "),C=[],K="";for(let Q of H){let W=K?`${K} ${Q}`:Q;if(j(W,z)<=B)K=W;else{if(K)C.push(K);K=Q}}if(K)C.push(K);return C}imgInfo(q){if(q[0]===255&&q[1]===216){let B=2;while(B<q.length){if(q[B]!==255)break;let z=q[B+1];if(z>=192&&z<=207&&z!==196&&z!==200&&z!==204)return{h:q[B+5]<<8|q[B+6],w:q[B+7]<<8|q[B+8]};B+=2+(q[B+2]<<8|q[B+3])}}if(q[0]===137&&q[1]===80)return{w:q[16]<<24|q[17]<<16|q[18]<<8|q[19],h:q[20]<<24|q[21]<<16|q[22]<<8|q[23]};return null}}class S{pages=[];cur=null;sz;meta;constructor(q="A4"){this.sz=typeof q==="string"?P[q]:q}metadata(q){return this.meta=q,this}page(q){return this.cur=new v(q?typeof q==="string"?P[q]:q:this.sz),this.pages.push(this.cur),this}text(q,B,z,H){return this.ensure().text(q,B,z,H),this}rect(q,B,z,H,C){return this.ensure().rect(q,B,z,H,C),this}line(q,B,z,H,C){return this.ensure().line(q,B,z,H,C),this}circle(q,B,z,H){return this.ensure().circle(q,B,z,H),this}image(q,B,z,H){return this.ensure().image(q,B,z,H),this}link(q,B,z,H,C){return this.ensure().link(q,B,z,H,C),this}table(q,B,z,H){return this.ensure().table(q,B,z,H),this}ensure(){if(!this.cur)this.page();return this.cur}build(){let q=new R,B=[0],z=0;q.l("%PDF-1.4").l("%µµµµ");let H=new Set;this.pages.forEach((M)=>M.f.forEach((_)=>H.add(_)));let C=Array.from(H),K={};for(let M of C){let _=++z;K[M]=_,B[_]=q.size(),q.l(`${_} 0 obj`).l(`<</Type/Font/Subtype/Type1/BaseFont/${M}>>`).l("endobj")}let Q=[],W=[],N=[];for(let M of this.pages){let _=[];for(let Y of M.imgs){let T=++z;_.push(T),B[T]=q.size();let u=String.fromCharCode(...Y.d);q.l(`${T} 0 obj`).l(`<</Type/XObject/Subtype/Image/Width ${Y.w}/Height ${Y.h}/ColorSpace/DeviceRGB/BitsPerComponent 8/Filter/DCTDecode/Length ${Y.d.length}>>`).l("stream").w(u).l("").l("endstream").l("endobj")}N.push(_);let $=M.c.join(`
3
+ `),Z=new TextEncoder().encode($).length,U=++z;Q.push(U),B[U]=q.size(),q.l(`${U} 0 obj`).l(`<</Length ${Z}>>`).l("stream").w($).l("").l("endstream").l("endobj");let k=[];for(let Y of M.links){let T=++z;k.push(T),B[T]=q.size(),q.l(`${T} 0 obj`).l(`<</Type/Annot/Subtype/Link/Rect[${Y.x} ${Y.y} ${Y.x+Y.w} ${Y.y+Y.h}]/Border[0 0 0]/A<</Type/Action/S/URI/URI(${Y.url})>>>>`).l("endobj")}W.push(k)}let G=++z,L=z+1,X=this.pages.map((M,_)=>L+_);B[G]=q.size(),q.l(`${G} 0 obj`).l(`<</Type/Pages/Kids[${X.map((M)=>`${M} 0 R`).join(" ")}]/Count ${X.length}>>`).l("endobj");for(let M=0;M<this.pages.length;M++){let _=this.pages[M],$=++z,Z=Array.from(_.f).map((Y)=>`/${Y.replace("-","")} ${K[Y]} 0 R`).join(""),U=_.imgs.map((Y,T)=>`/${Y.id} ${N[M][T]} 0 R`).join(""),k=W[M].length?`/Annots[${W[M].map((Y)=>`${Y} 0 R`).join(" ")}]`:"";B[$]=q.size(),q.l(`${$} 0 obj`).l(`<</Type/Page/Parent ${G} 0 R/MediaBox[0 0 ${_.w} ${_.h}]/Contents ${Q[M]} 0 R/Resources<<${Z?`/Font<<${Z}>>`:""}${U?`/XObject<<${U}>>`:""}>>${k}>>`).l("endobj")}let V=0;if(this.meta){V=++z,B[V]=q.size();let M=new Date,_=`D:${M.getFullYear()}${String(M.getMonth()+1).padStart(2,"0")}${String(M.getDate()).padStart(2,"0")}${String(M.getHours()).padStart(2,"0")}${String(M.getMinutes()).padStart(2,"0")}${String(M.getSeconds()).padStart(2,"0")}`,$=this.meta;q.l(`${V} 0 obj`).l(`<<${$.title?`/Title(${E($.title)})`:""}${$.author?`/Author(${E($.author)})`:""}${$.subject?`/Subject(${E($.subject)})`:""}${$.keywords?`/Keywords(${E($.keywords)})`:""}${$.creator?`/Creator(${E($.creator)})`:""}/Producer(podpdf-browser)/CreationDate(${_})>>`).l("endobj")}let O=++z;B[O]=q.size(),q.l(`${O} 0 obj`).l(`<</Type/Catalog/Pages ${G} 0 R>>`).l("endobj");let b=q.size();q.l("xref").l(`0 ${z+1}`).l("0000000000 65535 f ");for(let M=1;M<=z;M++)q.l(`${B[M].toString().padStart(10,"0")} 00000 n `);return q.l("trailer").l(`<</Size ${z+1}/Root ${O} 0 R${V?`/Info ${V} 0 R`:""}>>`).l("startxref").l(b.toString()).l("%%EOF"),q.out()}download(q="document.pdf"){if(typeof window>"u")throw Error("download() method is only available in browser environment. Use build() to get Uint8Array instead.");let B=this.build(),z=new Blob([B],{type:"application/pdf"}),H=URL.createObjectURL(z),C=document.createElement("a");C.href=H,C.download=q,C.style.display="none",document.body.appendChild(C),C.click(),document.body.removeChild(C),URL.revokeObjectURL(H)}toBlob(){let q=this.build();return new Blob([q],{type:"application/pdf"})}toDataURL(){let q=this.build(),B=new Blob([q],{type:"application/pdf"});return URL.createObjectURL(B)}}var I=(q)=>new S(q);export{I as pdf,P as SIZES,S as PDF};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "podpdf",
3
- "version": "1.1.0",
4
- "description": "Ultra-fast, zero-dependency PDF generation library. 8KB minified, 5x faster than jsPDF.",
3
+ "version": "1.2.0",
4
+ "description": "Ultra-fast, zero-dependency PDF generation for Node.js, Bun & Browser. 8KB minified, 5x faster than jsPDF.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -14,13 +14,19 @@
14
14
  "./plus": {
15
15
  "import": "./dist/plus.js",
16
16
  "types": "./dist/plus.d.ts"
17
+ },
18
+ "./browser": {
19
+ "import": "./dist/browser.js",
20
+ "types": "./dist/browser.d.ts"
17
21
  }
18
22
  },
19
23
  "files": [
20
24
  "dist"
21
25
  ],
22
26
  "scripts": {
23
- "build": "bun build src/index.ts src/plus.ts --outdir dist --target node --minify && bun run build:types",
27
+ "build": "bun run build:node && bun run build:browser && bun run build:types",
28
+ "build:node": "bun build src/index.ts src/plus.ts --outdir dist --target node --minify",
29
+ "build:browser": "bun build src/browser.ts --outdir dist --target browser --minify",
24
30
  "build:types": "tsc --emitDeclarationOnly",
25
31
  "dev": "bun --watch src/index.ts",
26
32
  "test": "bun test/test.ts",
@@ -39,7 +45,10 @@
39
45
  "zero-dependency",
40
46
  "fast",
41
47
  "lightweight",
42
- "minimal"
48
+ "minimal",
49
+ "browser",
50
+ "browser-pdf",
51
+ "client-side"
43
52
  ],
44
53
  "author": "",
45
54
  "license": "MIT",