pdfdancer-client-typescript 1.0.12 → 1.0.13
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/.github/workflows/ci.yml +1 -1
- package/README.md +1 -1
- package/dist/__tests__/e2e/pdf-assertions.d.ts +1 -0
- package/dist/__tests__/e2e/pdf-assertions.d.ts.map +1 -1
- package/dist/__tests__/e2e/pdf-assertions.js +9 -3
- package/dist/__tests__/e2e/pdf-assertions.js.map +1 -1
- package/dist/fingerprint.d.ts +12 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +196 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/image-builder.d.ts +4 -2
- package/dist/image-builder.d.ts.map +1 -1
- package/dist/image-builder.js +12 -3
- package/dist/image-builder.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +75 -8
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +179 -21
- package/dist/models.js.map +1 -1
- package/dist/page-builder.d.ts +24 -0
- package/dist/page-builder.d.ts.map +1 -0
- package/dist/page-builder.js +107 -0
- package/dist/page-builder.js.map +1 -0
- package/dist/paragraph-builder.d.ts +48 -54
- package/dist/paragraph-builder.d.ts.map +1 -1
- package/dist/paragraph-builder.js +408 -135
- package/dist/paragraph-builder.js.map +1 -1
- package/dist/pdfdancer_v1.d.ts +90 -9
- package/dist/pdfdancer_v1.d.ts.map +1 -1
- package/dist/pdfdancer_v1.js +535 -50
- package/dist/pdfdancer_v1.js.map +1 -1
- package/dist/types.d.ts +24 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +117 -2
- package/dist/types.js.map +1 -1
- package/docs/openapi.yml +2076 -0
- package/fixtures/Showcase.pdf +0 -0
- package/package.json +1 -1
- package/src/__tests__/e2e/acroform.test.ts +5 -5
- package/src/__tests__/e2e/context-manager-showcase.test.ts +267 -0
- package/src/__tests__/e2e/form_x_object.test.ts +1 -1
- package/src/__tests__/e2e/image-showcase.test.ts +133 -0
- package/src/__tests__/e2e/image.test.ts +1 -1
- package/src/__tests__/e2e/line-showcase.test.ts +118 -0
- package/src/__tests__/e2e/line.test.ts +1 -16
- package/src/__tests__/e2e/page-showcase.test.ts +154 -0
- package/src/__tests__/e2e/paragraph-showcase.test.ts +523 -0
- package/src/__tests__/e2e/paragraph.test.ts +8 -8
- package/src/__tests__/e2e/pdf-assertions.ts +10 -3
- package/src/__tests__/e2e/pdfdancer-showcase.test.ts +40 -0
- package/src/__tests__/e2e/snapshot-showcase.test.ts +158 -0
- package/src/__tests__/e2e/snapshot.test.ts +296 -0
- package/src/__tests__/fingerprint.test.ts +36 -0
- package/src/fingerprint.ts +169 -0
- package/src/image-builder.ts +13 -6
- package/src/index.ts +6 -1
- package/src/models.ts +208 -24
- package/src/page-builder.ts +130 -0
- package/src/paragraph-builder.ts +517 -159
- package/src/pdfdancer_v1.ts +630 -51
- package/src/types.ts +145 -2
- package/update-api-spec.sh +3 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fingerprint generation for PDFDancer client
|
|
3
|
+
* Generates a unique fingerprint hash to identify client requests
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as crypto from 'crypto';
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import * as os from 'os';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get the install salt from localStorage (browser) or file storage (Node.js)
|
|
13
|
+
* This creates a persistent identifier for this client installation
|
|
14
|
+
*/
|
|
15
|
+
function getInstallSalt(): string {
|
|
16
|
+
const storageKey = 'pdfdancer_install_salt';
|
|
17
|
+
|
|
18
|
+
// Check if we're in a browser environment
|
|
19
|
+
if (typeof localStorage !== 'undefined') {
|
|
20
|
+
let salt = localStorage.getItem(storageKey);
|
|
21
|
+
if (!salt) {
|
|
22
|
+
salt = crypto.randomBytes(16).toString('hex');
|
|
23
|
+
localStorage.setItem(storageKey, salt);
|
|
24
|
+
}
|
|
25
|
+
return salt;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Node.js environment - use file storage
|
|
29
|
+
try {
|
|
30
|
+
const saltDir = path.join(os.homedir(), '.pdfdancer');
|
|
31
|
+
const saltFile = path.join(saltDir, 'install_salt');
|
|
32
|
+
|
|
33
|
+
// Create directory if it doesn't exist
|
|
34
|
+
if (!fs.existsSync(saltDir)) {
|
|
35
|
+
fs.mkdirSync(saltDir, { recursive: true, mode: 0o700 });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Read existing salt or generate new one
|
|
39
|
+
if (fs.existsSync(saltFile)) {
|
|
40
|
+
return fs.readFileSync(saltFile, 'utf8').trim();
|
|
41
|
+
} else {
|
|
42
|
+
const salt = crypto.randomBytes(16).toString('hex');
|
|
43
|
+
fs.writeFileSync(saltFile, salt, { mode: 0o600 });
|
|
44
|
+
return salt;
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
// Fallback to generating a new salt if file operations fail
|
|
48
|
+
return crypto.randomBytes(16).toString('hex');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Attempt to get the client's IP address
|
|
54
|
+
* Note: This is limited on the client side and may not always be accurate
|
|
55
|
+
*/
|
|
56
|
+
async function getClientIP(): Promise<string> {
|
|
57
|
+
try {
|
|
58
|
+
// In browser, we can't reliably get the real IP
|
|
59
|
+
// Return a placeholder that will be consistent per session
|
|
60
|
+
return 'client-unknown';
|
|
61
|
+
} catch {
|
|
62
|
+
return 'client-unknown';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get the OS type
|
|
68
|
+
*/
|
|
69
|
+
function getOSType(): string {
|
|
70
|
+
// Check if we're in Node.js
|
|
71
|
+
if (typeof process !== 'undefined' && process.platform) {
|
|
72
|
+
return process.platform;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Browser environment
|
|
76
|
+
if (typeof navigator !== 'undefined') {
|
|
77
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
78
|
+
if (userAgent.includes('win')) return 'windows';
|
|
79
|
+
if (userAgent.includes('mac')) return 'macos';
|
|
80
|
+
if (userAgent.includes('linux')) return 'linux';
|
|
81
|
+
if (userAgent.includes('android')) return 'android';
|
|
82
|
+
if (userAgent.includes('iphone') || userAgent.includes('ipad')) return 'ios';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return 'unknown';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get the current hostname
|
|
90
|
+
*/
|
|
91
|
+
function getHostname(): string {
|
|
92
|
+
// Node.js environment
|
|
93
|
+
if (typeof process !== 'undefined') {
|
|
94
|
+
try {
|
|
95
|
+
return os.hostname();
|
|
96
|
+
} catch {
|
|
97
|
+
// Fall through to browser logic
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Browser environment
|
|
102
|
+
if (typeof window !== 'undefined' && window.location) {
|
|
103
|
+
return window.location.hostname;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return 'unknown';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get the timezone
|
|
111
|
+
*/
|
|
112
|
+
function getTimezone(): string {
|
|
113
|
+
try {
|
|
114
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
115
|
+
} catch {
|
|
116
|
+
return 'unknown';
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get the locale
|
|
122
|
+
*/
|
|
123
|
+
function getLocale(): string {
|
|
124
|
+
try {
|
|
125
|
+
if (typeof navigator !== 'undefined' && navigator.language) {
|
|
126
|
+
return navigator.language;
|
|
127
|
+
}
|
|
128
|
+
return Intl.DateTimeFormat().resolvedOptions().locale;
|
|
129
|
+
} catch {
|
|
130
|
+
return 'unknown';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Generate a fingerprint hash from client data points
|
|
136
|
+
*
|
|
137
|
+
* @param userId Optional user ID to include in the fingerprint
|
|
138
|
+
* @returns SHA256 hash of fingerprint components
|
|
139
|
+
*/
|
|
140
|
+
export async function generateFingerprint(userId?: string): Promise<string> {
|
|
141
|
+
const ip = await getClientIP();
|
|
142
|
+
const uid = userId || 'unknown';
|
|
143
|
+
const osType = getOSType();
|
|
144
|
+
const sdkLanguage = 'typescript';
|
|
145
|
+
const timezone = getTimezone();
|
|
146
|
+
const locale = getLocale();
|
|
147
|
+
const domain = getHostname();
|
|
148
|
+
const installSalt = getInstallSalt();
|
|
149
|
+
|
|
150
|
+
// Hash individual components
|
|
151
|
+
const ipHash = crypto.createHash('sha256').update(ip).digest('hex');
|
|
152
|
+
const uidHash = crypto.createHash('sha256').update(uid).digest('hex');
|
|
153
|
+
const domainHash = crypto.createHash('sha256').update(domain).digest('hex');
|
|
154
|
+
|
|
155
|
+
// Concatenate all components and hash
|
|
156
|
+
const fingerprintData =
|
|
157
|
+
ipHash +
|
|
158
|
+
uidHash +
|
|
159
|
+
osType +
|
|
160
|
+
sdkLanguage +
|
|
161
|
+
timezone +
|
|
162
|
+
locale +
|
|
163
|
+
domainHash +
|
|
164
|
+
installSalt;
|
|
165
|
+
|
|
166
|
+
const fingerprintHash = crypto.createHash('sha256').update(fingerprintData).digest('hex');
|
|
167
|
+
|
|
168
|
+
return fingerprintHash;
|
|
169
|
+
}
|
package/src/image-builder.ts
CHANGED
|
@@ -8,13 +8,11 @@ interface PDFDancerInternals {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export class ImageBuilder {
|
|
11
|
-
private _client: PDFDancer;
|
|
12
11
|
private _imageData: Uint8Array<ArrayBuffer> | undefined;
|
|
13
12
|
private _position: Position | undefined;
|
|
14
|
-
private _internals: PDFDancerInternals;
|
|
13
|
+
private readonly _internals: PDFDancerInternals;
|
|
15
14
|
|
|
16
|
-
constructor(_client: PDFDancer) {
|
|
17
|
-
this._client = _client;
|
|
15
|
+
constructor(private _client: PDFDancer, private readonly _defaultPageIndex?: number) {
|
|
18
16
|
// Cast to the internal interface to get access
|
|
19
17
|
this._internals = this._client as unknown as PDFDancerInternals;
|
|
20
18
|
}
|
|
@@ -33,8 +31,17 @@ export class ImageBuilder {
|
|
|
33
31
|
return this;
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
at(
|
|
37
|
-
|
|
34
|
+
at(x: number, y: number): this;
|
|
35
|
+
at(pageIndex: number, x: number, y: number): this;
|
|
36
|
+
at(pageIndexOrX: number, xOrY: number, maybeY?: number): this {
|
|
37
|
+
if (maybeY === undefined) {
|
|
38
|
+
if (this._defaultPageIndex === undefined) {
|
|
39
|
+
throw new Error('Page index must be provided when adding an image');
|
|
40
|
+
}
|
|
41
|
+
this._position = Position.atPageCoordinates(this._defaultPageIndex, pageIndexOrX, xOrY);
|
|
42
|
+
} else {
|
|
43
|
+
this._position = Position.atPageCoordinates(pageIndexOrX, xOrY, maybeY);
|
|
44
|
+
}
|
|
38
45
|
return this;
|
|
39
46
|
}
|
|
40
47
|
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
export { PDFDancer } from './pdfdancer_v1';
|
|
8
8
|
export { ParagraphBuilder } from './paragraph-builder';
|
|
9
|
+
export { PageBuilder } from './page-builder';
|
|
9
10
|
|
|
10
11
|
export {
|
|
11
12
|
PdfDancerException,
|
|
@@ -28,15 +29,19 @@ export {
|
|
|
28
29
|
Image,
|
|
29
30
|
BoundingRect,
|
|
30
31
|
Paragraph,
|
|
32
|
+
TextLine,
|
|
31
33
|
PositionMode,
|
|
32
34
|
ShapeType,
|
|
33
35
|
Point,
|
|
34
36
|
StandardFonts,
|
|
37
|
+
STANDARD_PAGE_SIZES,
|
|
35
38
|
Orientation,
|
|
36
39
|
CommandResult,
|
|
37
40
|
TextStatus,
|
|
38
41
|
FontRecommendation,
|
|
39
|
-
FontType
|
|
42
|
+
FontType,
|
|
43
|
+
DocumentSnapshot,
|
|
44
|
+
PageSnapshot
|
|
40
45
|
} from './models';
|
|
41
46
|
|
|
42
47
|
export const VERSION = "1.0.0";
|
package/src/models.ts
CHANGED
|
@@ -8,13 +8,16 @@ export enum ObjectType {
|
|
|
8
8
|
PATH = "PATH",
|
|
9
9
|
PARAGRAPH = "PARAGRAPH",
|
|
10
10
|
TEXT_LINE = "TEXT_LINE",
|
|
11
|
+
TEXT_ELEMENT = "TEXT_ELEMENT",
|
|
11
12
|
PAGE = "PAGE",
|
|
12
13
|
FORM_FIELD = "FORM_FIELD",
|
|
13
14
|
TEXT_FIELD = "TEXT_FIELD",
|
|
14
|
-
|
|
15
|
+
CHECKBOX = "CHECKBOX",
|
|
15
16
|
RADIO_BUTTON = "RADIO_BUTTON"
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
const DEFAULT_LINE_SPACING = 1.2;
|
|
20
|
+
|
|
18
21
|
/**
|
|
19
22
|
* Defines how position matching should be performed when searching for objects.
|
|
20
23
|
*/
|
|
@@ -82,6 +85,7 @@ class PositioningError extends Error {
|
|
|
82
85
|
*/
|
|
83
86
|
export class Position {
|
|
84
87
|
public name?: string;
|
|
88
|
+
public tolerance?: number;
|
|
85
89
|
|
|
86
90
|
constructor(
|
|
87
91
|
public pageIndex?: number,
|
|
@@ -102,9 +106,12 @@ export class Position {
|
|
|
102
106
|
|
|
103
107
|
/**
|
|
104
108
|
* Creates a position specification for specific coordinates on a page.
|
|
109
|
+
* @param tolerance Optional tolerance in points for coordinate matching (default: 0)
|
|
105
110
|
*/
|
|
106
|
-
static atPageCoordinates(pageIndex: number, x: number, y: number): Position {
|
|
107
|
-
|
|
111
|
+
static atPageCoordinates(pageIndex: number, x: number, y: number, tolerance: number = 0): Position {
|
|
112
|
+
const pos = Position.atPage(pageIndex).atCoordinates({x, y});
|
|
113
|
+
pos.tolerance = tolerance;
|
|
114
|
+
return pos;
|
|
108
115
|
}
|
|
109
116
|
|
|
110
117
|
static atPageWithText(pageIndex: number, text: string) {
|
|
@@ -480,10 +487,60 @@ export class Image {
|
|
|
480
487
|
export class Paragraph {
|
|
481
488
|
constructor(
|
|
482
489
|
public position?: Position,
|
|
483
|
-
public textLines?: string
|
|
490
|
+
public textLines?: Array<TextLine | string>,
|
|
491
|
+
public font?: Font,
|
|
492
|
+
public color?: Color,
|
|
493
|
+
public lineSpacing: number = DEFAULT_LINE_SPACING,
|
|
494
|
+
public lineSpacings?: number[] | null
|
|
495
|
+
) {
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
getPosition(): Position | undefined {
|
|
499
|
+
return this.position;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
setPosition(position: Position): void {
|
|
503
|
+
this.position = position;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
clearLines(): void {
|
|
507
|
+
this.textLines = [];
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
addLine(textLine: TextLine | string): void {
|
|
511
|
+
if (!this.textLines) {
|
|
512
|
+
this.textLines = [];
|
|
513
|
+
}
|
|
514
|
+
this.textLines.push(textLine);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
getLines(): Array<TextLine | string> {
|
|
518
|
+
if (!this.textLines) {
|
|
519
|
+
this.textLines = [];
|
|
520
|
+
}
|
|
521
|
+
return this.textLines;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
setLines(lines: Array<TextLine | string>): void {
|
|
525
|
+
this.textLines = [...lines];
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
setLineSpacings(spacings?: number[] | null): void {
|
|
529
|
+
this.lineSpacings = spacings ? [...spacings] : spacings ?? null;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
getLineSpacings(): number[] | null | undefined {
|
|
533
|
+
return this.lineSpacings ? [...this.lineSpacings] : this.lineSpacings;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
export class TextLine {
|
|
538
|
+
constructor(
|
|
539
|
+
public position?: Position,
|
|
484
540
|
public font?: Font,
|
|
485
541
|
public color?: Color,
|
|
486
|
-
public lineSpacing: number =
|
|
542
|
+
public lineSpacing: number = DEFAULT_LINE_SPACING,
|
|
543
|
+
public text: string = ""
|
|
487
544
|
) {
|
|
488
545
|
}
|
|
489
546
|
|
|
@@ -587,38 +644,76 @@ export class AddRequest {
|
|
|
587
644
|
data: dataB64
|
|
588
645
|
};
|
|
589
646
|
} else if (obj instanceof Paragraph) {
|
|
647
|
+
const fontToDict = (font?: Font | null) => {
|
|
648
|
+
if (!font) {
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
return {name: font.name, size: font.size};
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
const colorToDict = (color?: Color | null) => {
|
|
655
|
+
if (!color) {
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
658
|
+
return {red: color.r, green: color.g, blue: color.b, alpha: color.a};
|
|
659
|
+
};
|
|
660
|
+
|
|
590
661
|
const lines: any[] = [];
|
|
591
662
|
if (obj.textLines) {
|
|
592
663
|
for (const line of obj.textLines) {
|
|
664
|
+
let text: string;
|
|
665
|
+
let font = obj.font;
|
|
666
|
+
let color = obj.color;
|
|
667
|
+
let position = obj.position;
|
|
668
|
+
let spacing = obj.lineSpacing;
|
|
669
|
+
|
|
670
|
+
if (line instanceof TextLine) {
|
|
671
|
+
text = line.text;
|
|
672
|
+
font = line.font ?? font;
|
|
673
|
+
color = line.color ?? color;
|
|
674
|
+
position = line.position ?? position;
|
|
675
|
+
spacing = line.lineSpacing ?? spacing;
|
|
676
|
+
} else {
|
|
677
|
+
text = line;
|
|
678
|
+
}
|
|
679
|
+
|
|
593
680
|
const textElement = {
|
|
594
|
-
text
|
|
595
|
-
font:
|
|
596
|
-
color:
|
|
597
|
-
position:
|
|
681
|
+
text,
|
|
682
|
+
font: fontToDict(font),
|
|
683
|
+
color: colorToDict(color),
|
|
684
|
+
position: position ? positionToDict(position) : null
|
|
598
685
|
};
|
|
599
686
|
|
|
600
687
|
const textLine: any = {
|
|
601
688
|
textElements: [textElement]
|
|
602
689
|
};
|
|
603
690
|
|
|
604
|
-
if (
|
|
605
|
-
textLine.color =
|
|
691
|
+
if (color) {
|
|
692
|
+
textLine.color = colorToDict(color);
|
|
693
|
+
}
|
|
694
|
+
if (position) {
|
|
695
|
+
textLine.position = positionToDict(position);
|
|
606
696
|
}
|
|
607
|
-
if (
|
|
608
|
-
textLine.
|
|
697
|
+
if (spacing !== undefined && spacing !== null) {
|
|
698
|
+
textLine.lineSpacing = spacing;
|
|
609
699
|
}
|
|
700
|
+
|
|
610
701
|
lines.push(textLine);
|
|
611
702
|
}
|
|
612
703
|
}
|
|
613
704
|
|
|
614
|
-
const lineSpacings = obj.
|
|
705
|
+
const lineSpacings = obj.lineSpacings
|
|
706
|
+
? [...obj.lineSpacings]
|
|
707
|
+
: (obj.lineSpacing !== undefined && obj.lineSpacing !== null
|
|
708
|
+
? [obj.lineSpacing]
|
|
709
|
+
: null);
|
|
615
710
|
|
|
616
711
|
return {
|
|
617
712
|
type: "PARAGRAPH",
|
|
618
713
|
position: obj.position ? positionToDict(obj.position) : null,
|
|
619
|
-
lines,
|
|
714
|
+
lines: lines.length ? lines : null,
|
|
620
715
|
lineSpacings,
|
|
621
|
-
font: obj.font
|
|
716
|
+
font: fontToDict(obj.font)
|
|
622
717
|
};
|
|
623
718
|
} else {
|
|
624
719
|
throw new Error(`Unsupported object type: ${typeof obj}`);
|
|
@@ -775,18 +870,35 @@ export class CreatePdfRequest {
|
|
|
775
870
|
*/
|
|
776
871
|
export class AddPageRequest {
|
|
777
872
|
constructor(
|
|
778
|
-
public pageIndex
|
|
779
|
-
public pageSize?:
|
|
780
|
-
public orientation?: string
|
|
873
|
+
public pageIndex?: number,
|
|
874
|
+
public pageSize?: PageSize,
|
|
875
|
+
public orientation?: Orientation | string
|
|
781
876
|
) {
|
|
782
877
|
}
|
|
783
878
|
|
|
784
879
|
toDict(): Record<string, any> {
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
}
|
|
880
|
+
const payload: Record<string, any> = {};
|
|
881
|
+
|
|
882
|
+
if (this.pageIndex !== undefined) {
|
|
883
|
+
payload.pageIndex = this.pageIndex;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
if (this.orientation !== undefined && this.orientation !== null) {
|
|
887
|
+
const value = typeof this.orientation === 'string'
|
|
888
|
+
? this.orientation.trim().toUpperCase()
|
|
889
|
+
: this.orientation;
|
|
890
|
+
payload.orientation = typeof value === 'string' ? value : value;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
if (this.pageSize) {
|
|
894
|
+
payload.pageSize = {
|
|
895
|
+
name: this.pageSize.name,
|
|
896
|
+
width: this.pageSize.width,
|
|
897
|
+
height: this.pageSize.height
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
return payload;
|
|
790
902
|
}
|
|
791
903
|
}
|
|
792
904
|
|
|
@@ -835,6 +947,78 @@ export class CommandResult {
|
|
|
835
947
|
}
|
|
836
948
|
}
|
|
837
949
|
|
|
950
|
+
/**
|
|
951
|
+
* Represents a snapshot of a single page in a PDF document.
|
|
952
|
+
* Contains the page reference and all elements on that page.
|
|
953
|
+
*/
|
|
954
|
+
export class PageSnapshot {
|
|
955
|
+
constructor(
|
|
956
|
+
public pageRef: PageRef,
|
|
957
|
+
public elements: ObjectRef[]
|
|
958
|
+
) {}
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* Filters elements by object type.
|
|
962
|
+
*/
|
|
963
|
+
getElementsByType(type: ObjectType): ObjectRef[] {
|
|
964
|
+
return this.elements.filter(el => el.type === type);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Gets the page index from the page reference.
|
|
969
|
+
*/
|
|
970
|
+
getPageIndex(): number | undefined {
|
|
971
|
+
return this.pageRef.position.pageIndex;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* Returns the number of elements on this page.
|
|
976
|
+
*/
|
|
977
|
+
getElementCount(): number {
|
|
978
|
+
return this.elements.length;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Represents a complete snapshot of a PDF document.
|
|
984
|
+
* Contains page count, font information, and snapshots of all pages.
|
|
985
|
+
*/
|
|
986
|
+
export class DocumentSnapshot {
|
|
987
|
+
constructor(
|
|
988
|
+
public pageCount: number,
|
|
989
|
+
public fonts: FontRecommendation[],
|
|
990
|
+
public pages: PageSnapshot[]
|
|
991
|
+
) {}
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* Gets the snapshot for a specific page by index.
|
|
995
|
+
*/
|
|
996
|
+
getPageSnapshot(pageIndex: number): PageSnapshot | undefined {
|
|
997
|
+
return this.pages.find(page => page.getPageIndex() === pageIndex);
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* Gets all elements across all pages.
|
|
1002
|
+
*/
|
|
1003
|
+
getAllElements(): ObjectRef[] {
|
|
1004
|
+
return this.pages.flatMap(page => page.elements);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Gets all elements of a specific type across all pages.
|
|
1009
|
+
*/
|
|
1010
|
+
getElementsByType(type: ObjectType): ObjectRef[] {
|
|
1011
|
+
return this.getAllElements().filter(el => el.type === type);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* Returns the total number of elements across all pages.
|
|
1016
|
+
*/
|
|
1017
|
+
getTotalElementCount(): number {
|
|
1018
|
+
return this.pages.reduce((sum, page) => sum + page.getElementCount(), 0);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
838
1022
|
// Helper function to convert Position to dictionary for JSON serialization
|
|
839
1023
|
function positionToDict(position: Position): Record<string, any> {
|
|
840
1024
|
const result: Record<string, any> = {
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import {PDFDancer} from './pdfdancer_v1';
|
|
2
|
+
import {AddPageRequest, Orientation, PageRef, PageSize, STANDARD_PAGE_SIZES} from './models';
|
|
3
|
+
import {ValidationException} from './exceptions';
|
|
4
|
+
|
|
5
|
+
interface PDFDancerInternals {
|
|
6
|
+
addPage(request?: AddPageRequest | null): Promise<PageRef>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const normalizeOrientation = (value: Orientation | string): Orientation => {
|
|
10
|
+
if (typeof value === 'string') {
|
|
11
|
+
const normalized = value.trim().toUpperCase();
|
|
12
|
+
if (!(normalized in Orientation)) {
|
|
13
|
+
throw new ValidationException(`Invalid orientation: ${value}`);
|
|
14
|
+
}
|
|
15
|
+
return Orientation[normalized as keyof typeof Orientation];
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const normalizePageSize = (value: PageSize | string): PageSize => {
|
|
21
|
+
if (typeof value === 'string') {
|
|
22
|
+
const normalized = value.trim().toUpperCase();
|
|
23
|
+
const standard = STANDARD_PAGE_SIZES[normalized];
|
|
24
|
+
if (!standard) {
|
|
25
|
+
throw new ValidationException(`Unknown page size: ${value}`);
|
|
26
|
+
}
|
|
27
|
+
return {name: normalized, width: standard.width, height: standard.height};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const width = value.width;
|
|
31
|
+
const height = value.height;
|
|
32
|
+
if (width === undefined || height === undefined) {
|
|
33
|
+
throw new ValidationException('Custom page size must include width and height');
|
|
34
|
+
}
|
|
35
|
+
if (width <= 0 || height <= 0) {
|
|
36
|
+
throw new ValidationException('Page size width and height must be positive numbers');
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
name: value.name?.toUpperCase(),
|
|
40
|
+
width,
|
|
41
|
+
height
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export class PageBuilder {
|
|
46
|
+
private readonly _client: PDFDancer;
|
|
47
|
+
private readonly _internals: PDFDancerInternals;
|
|
48
|
+
private _pageIndex?: number;
|
|
49
|
+
private _orientation?: Orientation;
|
|
50
|
+
private _pageSize?: PageSize;
|
|
51
|
+
|
|
52
|
+
constructor(client: PDFDancer) {
|
|
53
|
+
if (!client) {
|
|
54
|
+
throw new ValidationException('Client cannot be null');
|
|
55
|
+
}
|
|
56
|
+
this._client = client;
|
|
57
|
+
this._internals = this._client as unknown as PDFDancerInternals;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
atIndex(pageIndex: number): this {
|
|
61
|
+
if (pageIndex === null || pageIndex === undefined) {
|
|
62
|
+
throw new ValidationException('Page index cannot be null');
|
|
63
|
+
}
|
|
64
|
+
if (!Number.isInteger(pageIndex) || pageIndex < 0) {
|
|
65
|
+
throw new ValidationException('Page index must be a non-negative integer');
|
|
66
|
+
}
|
|
67
|
+
this._pageIndex = pageIndex;
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
orientation(orientation: Orientation | string): this {
|
|
72
|
+
this._orientation = normalizeOrientation(orientation);
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
portrait(): this {
|
|
77
|
+
this._orientation = Orientation.PORTRAIT;
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
landscape(): this {
|
|
82
|
+
this._orientation = Orientation.LANDSCAPE;
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
pageSize(pageSize: PageSize | string): this {
|
|
87
|
+
this._pageSize = normalizePageSize(pageSize);
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
a4(): this {
|
|
92
|
+
return this.pageSize('A4');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
letter(): this {
|
|
96
|
+
return this.pageSize('LETTER');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
a3(): this {
|
|
100
|
+
return this.pageSize('A3');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
a5(): this {
|
|
104
|
+
return this.pageSize('A5');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
legal(): this {
|
|
108
|
+
return this.pageSize('LEGAL');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
customSize(width: number, height: number): this {
|
|
112
|
+
if (width <= 0 || height <= 0) {
|
|
113
|
+
throw new ValidationException('Custom page size dimensions must be positive');
|
|
114
|
+
}
|
|
115
|
+
this._pageSize = {width, height};
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async add(): Promise<PageRef> {
|
|
120
|
+
const request = this._buildRequest();
|
|
121
|
+
return this._internals.addPage(request);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private _buildRequest(): AddPageRequest | null {
|
|
125
|
+
if (this._pageIndex === undefined && this._orientation === undefined && this._pageSize === undefined) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
return new AddPageRequest(this._pageIndex, this._pageSize, this._orientation);
|
|
129
|
+
}
|
|
130
|
+
}
|