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 +102 -34
- package/dist/browser.d.ts +93 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +3 -0
- package/package.json +13 -4
package/README.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# podpdf
|
|
2
2
|
|
|
3
|
-
**Ultra-fast, zero-dependency PDF generation for Node.js &
|
|
3
|
+
**Ultra-fast, zero-dependency PDF generation for Node.js, Bun & Browser**
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/podpdf)
|
|
6
6
|
[](https://bundlephobia.com/package/podpdf)
|
|
7
7
|
[](https://www.npmjs.com/package/podpdf)
|
|
8
|
+
[](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
|
-
|
|
|
20
|
-
|
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
|
27
|
-
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
|
43
|
-
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
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":"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"}
|
package/dist/browser.js
ADDED
|
@@ -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.
|
|
4
|
-
"description": "Ultra-fast, zero-dependency PDF generation
|
|
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
|
|
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",
|