pdfdancer-client-typescript 1.0.8 → 1.0.10

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.
Files changed (42) hide show
  1. package/README.md +189 -27
  2. package/dist/__tests__/e2e/pdf-assertions.d.ts +35 -0
  3. package/dist/__tests__/e2e/pdf-assertions.d.ts.map +1 -0
  4. package/dist/__tests__/e2e/pdf-assertions.js +212 -0
  5. package/dist/__tests__/e2e/pdf-assertions.js.map +1 -0
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +7 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/models.d.ts +108 -1
  11. package/dist/models.d.ts.map +1 -1
  12. package/dist/models.js +201 -4
  13. package/dist/models.js.map +1 -1
  14. package/dist/paragraph-builder.d.ts +8 -4
  15. package/dist/paragraph-builder.d.ts.map +1 -1
  16. package/dist/paragraph-builder.js +65 -17
  17. package/dist/paragraph-builder.js.map +1 -1
  18. package/dist/pdfdancer_v1.d.ts +42 -4
  19. package/dist/pdfdancer_v1.d.ts.map +1 -1
  20. package/dist/pdfdancer_v1.js +215 -20
  21. package/dist/pdfdancer_v1.js.map +1 -1
  22. package/dist/types.d.ts +19 -3
  23. package/dist/types.d.ts.map +1 -1
  24. package/dist/types.js +49 -1
  25. package/dist/types.js.map +1 -1
  26. package/fixtures/Empty.pdf +0 -0
  27. package/package.json +1 -1
  28. package/src/__tests__/e2e/acroform.test.ts +13 -0
  29. package/src/__tests__/e2e/create-new.test.ts +133 -0
  30. package/src/__tests__/e2e/form_x_object.test.ts +8 -0
  31. package/src/__tests__/e2e/image.test.ts +30 -18
  32. package/src/__tests__/e2e/line.test.ts +33 -6
  33. package/src/__tests__/e2e/page.test.ts +4 -0
  34. package/src/__tests__/e2e/paragraph.test.ts +223 -2
  35. package/src/__tests__/e2e/path.test.ts +8 -0
  36. package/src/__tests__/e2e/pdf-assertions.ts +241 -0
  37. package/src/__tests__/url-builder.test.ts +44 -0
  38. package/src/index.ts +8 -1
  39. package/src/models.ts +264 -3
  40. package/src/paragraph-builder.ts +70 -20
  41. package/src/pdfdancer_v1.ts +301 -28
  42. package/src/types.ts +67 -3
@@ -2,9 +2,10 @@
2
2
  * E2E tests for paragraph operations — new PDFDancer API
3
3
  */
4
4
 
5
- import {Color, PDFDancer, StandardFonts} from '../../index';
5
+ import {Color, FontType, PDFDancer, StandardFonts} from '../../index';
6
6
  import {getFontPath, readFontFixture, requireEnvAndFixture} from './test-helpers';
7
7
  import {expectWithin} from '../assertions';
8
+ import {PDFAssertions} from './pdf-assertions';
8
9
 
9
10
  describe('Paragraph E2E Tests (v2 API)', () => {
10
11
 
@@ -29,6 +30,11 @@ describe('Paragraph E2E Tests (v2 API)', () => {
29
30
  expect(last.position).toBeDefined();
30
31
  expectWithin(last.position.boundingRect?.x, 54, 1);
31
32
  expectWithin(last.position.boundingRect?.y, 496, 2);
33
+
34
+ expect(last.objectRef().status).toBeDefined();
35
+ expect(last.objectRef().status?.isEncodable()).toBe(true);
36
+ expect(last.objectRef().status?.getFontType()).toBe(FontType.EMBEDDED);
37
+ expect(last.objectRef().status?.isModified()).toBe(false);
32
38
  });
33
39
 
34
40
  test('find paragraphs by text', async () => {
@@ -101,6 +107,21 @@ describe('Paragraph E2E Tests (v2 API)', () => {
101
107
  .apply()
102
108
 
103
109
  await assertNewParagraphExists(pdf);
110
+
111
+ const movedParas = await pdf.page(0).selectParagraphsAt(300.1, 500);
112
+ expect(movedParas.length).toBeGreaterThan(0);
113
+ const moved = movedParas[0];
114
+ expect(moved.objectRef().status).toBeDefined();
115
+ expect(moved.objectRef().status?.isEncodable()).toBe(true);
116
+ expect(moved.objectRef().status?.getFontType()).toBe(FontType.STANDARD);
117
+ expect(moved.objectRef().status?.isModified()).toBe(true);
118
+
119
+ const assertions = await PDFAssertions.create(pdf);
120
+ await assertions.assertTextlineHasFont('Awesomely', 'Helvetica', 12, 0);
121
+ await assertions.assertTextlineHasFont('Obvious!', 'Helvetica', 12, 0);
122
+ await assertions.assertTextlineHasColor('Awesomely', new Color(255, 255, 255), 0);
123
+ await assertions.assertTextlineHasColor('Obvious!', new Color(255, 255, 255), 0);
124
+ await assertions.assertParagraphIsAt('Awesomely', 300.1, 500, 0);
104
125
  });
105
126
 
106
127
  test('modify paragraph (simple)', async () => {
@@ -108,8 +129,28 @@ describe('Paragraph E2E Tests (v2 API)', () => {
108
129
  const pdf = await PDFDancer.open(pdfData, token, baseUrl);
109
130
 
110
131
  const [para] = await pdf.page(0).selectParagraphsStartingWith('The Complete');
111
- await para.edit().replace('Awesomely\nObvious!').apply();
132
+ const result = await para.edit().replace('Awesomely\nObvious!').apply();
133
+
134
+ // This should issue a warning about an embedded font modification
135
+ expect(typeof result).toBe('object');
136
+ expect((result as any).warning).toBeDefined();
137
+ expect((result as any).warning).toContain('You are using an embedded font and modified the text.');
138
+
112
139
  await assertNewParagraphExists(pdf);
140
+
141
+ const modifiedParas = await pdf.page(0).selectParagraphsStartingWith('Awesomely');
142
+ expect(modifiedParas.length).toBeGreaterThan(0);
143
+ const modified = modifiedParas[0];
144
+ expect(modified.objectRef().status).toBeDefined();
145
+ expect(modified.objectRef().status?.isEncodable()).toBe(true);
146
+ expect(modified.objectRef().status?.getFontType()).toBe(FontType.EMBEDDED);
147
+ expect(modified.objectRef().status?.isModified()).toBe(true);
148
+
149
+ const assertions = await PDFAssertions.create(pdf);
150
+ await assertions.assertTextlineHasFont('Awesomely', 'IXKSWR+Poppins-Bold', 1, 0);
151
+ await assertions.assertTextlineHasFont('Obvious!', 'IXKSWR+Poppins-Bold', 1, 0);
152
+ await assertions.assertTextlineHasColor('Awesomely', new Color(255, 255, 255), 0);
153
+ await assertions.assertTextlineHasColor('Obvious!', new Color(255, 255, 255), 0);
113
154
  });
114
155
 
115
156
  test('add paragraph with missing font (expect error)', async () => {
@@ -399,8 +440,188 @@ describe('Paragraph E2E Tests (v2 API)', () => {
399
440
  expect(lines.length).toBe(4);
400
441
  });
401
442
 
443
+
444
+ test('modify paragraph with assertions', async () => {
445
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
446
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
447
+
448
+ const [para] = await pdf.page(0).selectParagraphsStartingWith('The Complete');
449
+
450
+ await para.edit().replace('Awesomely\nObvious!')
451
+ .font("Helvetica", 12)
452
+ .lineSpacing(0.7)
453
+ .moveTo(300.1, 500)
454
+ .apply();
455
+
456
+ const assertions = await PDFAssertions.create(pdf);
457
+ await assertions.assertTextlineHasFont('Awesomely', 'Helvetica', 12, 0);
458
+ await assertions.assertTextlineHasFont('Obvious!', 'Helvetica', 12, 0);
459
+ await assertions.assertTextlineHasColor('Awesomely', new Color(255, 255, 255), 0);
460
+ await assertions.assertTextlineHasColor('Obvious!', new Color(255, 255, 255), 0);
461
+ await assertions.assertParagraphIsAt('Awesomely', 300.1, 500, 0);
462
+ });
463
+
464
+ test('modify paragraph without position', async () => {
465
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
466
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
467
+
468
+ const [para] = await pdf.page(0).selectParagraphsStartingWith('The Complete');
469
+ let originalX = para.position.getX();
470
+ let originalY = para.position.getY();
471
+
472
+ await para.edit()
473
+ .replace('Awesomely\nObvious!')
474
+ .font('Helvetica', 12)
475
+ .lineSpacing(0.7)
476
+ .apply();
477
+
478
+ const assertions = await PDFAssertions.create(pdf);
479
+ await assertions.assertTextlineHasFont('Awesomely', 'Helvetica', 12, 0);
480
+ await assertions.assertTextlineHasFont('Obvious!', 'Helvetica', 12, 0);
481
+ await assertions.assertTextlineHasColor('Awesomely', new Color(255, 255, 255), 0);
482
+ await assertions.assertTextlineHasColor('Obvious!', new Color(255, 255, 255), 0);
483
+ await assertions.assertParagraphIsAt('Awesomely', originalX!, originalY!, 0);
484
+ });
485
+
486
+ test('modify paragraph without position and spacing', async () => {
487
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
488
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
489
+
490
+ const [para] = await pdf.page(0).selectParagraphsStartingWith('The Complete');
491
+ let originalX = para.position.getX();
492
+ let originalY = para.position.getY();
493
+
494
+ await para.edit()
495
+ .replace('Awesomely\nObvious!')
496
+ .font('Helvetica', 12)
497
+ .apply();
498
+
499
+ const assertions = await PDFAssertions.create(pdf);
500
+ await assertions.assertTextlineHasFont('Awesomely', 'Helvetica', 12, 0);
501
+ await assertions.assertTextlineHasFont('Obvious!', 'Helvetica', 12, 0);
502
+ await assertions.assertTextlineHasColor('Awesomely', new Color(255, 255, 255), 0);
503
+ await assertions.assertTextlineHasColor('Obvious!', new Color(255, 255, 255), 0);
504
+ await assertions.assertParagraphIsAt('Awesomely', originalX!, originalY!, 0);
505
+ });
506
+
507
+ test('modify paragraph only font', async () => {
508
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
509
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
510
+
511
+ const [para] = await pdf.page(0).selectParagraphsStartingWith('The Complete');
512
+
513
+ await para.edit()
514
+ .font('Helvetica', 28)
515
+ .apply();
516
+
517
+ const assertions = await PDFAssertions.create(pdf);
518
+ await assertions.assertTextlineHasFont('The Complete', 'Helvetica', 28, 0);
519
+ await assertions.assertTextlineHasColor('The Complete', new Color(255, 255, 255), 0);
520
+ });
521
+
522
+ test('modify paragraph only move', async () => {
523
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
524
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
525
+
526
+ const [para] = await pdf.page(0).selectParagraphsStartingWith('The Complete');
527
+
528
+ await para.edit()
529
+ .moveTo(1, 1)
530
+ .apply();
531
+
532
+ const assertions = await PDFAssertions.create(pdf);
533
+ await assertions.assertTextlineHasFont('The Complete', 'IXKSWR+Poppins-Bold', 1, 0);
534
+ await assertions.assertTextlineIsAt('The Complete', 1, 1, 0, 0.22);
535
+ await assertions.assertTextlineHasColor('The Complete', new Color(255, 255, 255), 0);
536
+ });
537
+
538
+ test('modify paragraph simple', async () => {
539
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
540
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
541
+
542
+ const [para] = await pdf.page(0).selectParagraphsStartingWith('The Complete');
543
+ await para.edit().replace('Awesomely\nObvious!').apply();
544
+
545
+ const assertions = await PDFAssertions.create(pdf);
546
+ await assertions.assertTextlineHasFont('Awesomely', 'IXKSWR+Poppins-Bold', 1, 0);
547
+ await assertions.assertTextlineHasFont('Obvious!', 'IXKSWR+Poppins-Bold', 1, 0);
548
+ await assertions.assertTextlineHasColor('Awesomely', new Color(255, 255, 255), 0);
549
+ await assertions.assertTextlineHasColor('Obvious!', new Color(255, 255, 255), 0);
550
+ });
551
+
552
+ test('add paragraph with standard font Times Roman', async () => {
553
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
554
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
555
+
556
+ await pdf.page(0).newParagraph()
557
+ .text('Times Roman Test')
558
+ .font(StandardFonts.TIMES_ROMAN, 14)
559
+ .at(150, 150)
560
+ .apply();
561
+
562
+ const assertions = await PDFAssertions.create(pdf);
563
+ await assertions.assertTextHasFont('Times Roman Test', StandardFonts.TIMES_ROMAN, 14, 0);
564
+ await assertions.assertParagraphIsAt('Times Roman Test', 150, 150, 0);
565
+ });
566
+
567
+ test('add paragraph with standard font Courier Bold', async () => {
568
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
569
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
570
+
571
+ await pdf.page(0).newParagraph()
572
+ .text('Courier Monospace\nCode Example')
573
+ .font(StandardFonts.COURIER_BOLD, 12)
574
+ .lineSpacing(1.5)
575
+ .at(200, 200)
576
+ .apply();
577
+
578
+ const assertions = await PDFAssertions.create(pdf);
579
+ await assertions.assertTextHasFont('Courier Monospace', StandardFonts.COURIER_BOLD, 12, 0);
580
+ await assertions.assertParagraphIsAt('Courier Monospace', 200, 200, 0);
581
+ });
582
+
583
+ test('paragraph color reading', async () => {
584
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
585
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
586
+
587
+ await pdf.page(0).newParagraph()
588
+ .text('Red Color Test')
589
+ .font(StandardFonts.HELVETICA, 14)
590
+ .color(new Color(255, 0, 0))
591
+ .at(100, 100)
592
+ .apply();
593
+
594
+ await pdf.page(0).newParagraph()
595
+ .text('Blue Color Test')
596
+ .font(StandardFonts.HELVETICA, 14)
597
+ .color(new Color(0, 0, 255))
598
+ .at(100, 120)
599
+ .apply();
600
+
601
+ const assertions = await PDFAssertions.create(pdf);
602
+ await assertions.assertTextlineHasColor('Blue Color Test', new Color(0, 0, 255), 0);
603
+ await assertions.assertTextlineHasColor('Red Color Test', new Color(255, 0, 0), 0);
604
+ });
605
+
606
+ test('add paragraph to new page', async () => {
607
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Empty.pdf');
608
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
609
+
610
+ await pdf.page(0).newParagraph()
611
+ .text('Awesome')
612
+ .font('Roboto-Regular', 14)
613
+ .at(50, 100)
614
+ .apply();
615
+
616
+ const assertions = await PDFAssertions.create(pdf);
617
+ await assertions.assertTextlineHasFontMatching('Awesome', 'Roboto-Regular', 14, 0);
618
+ await assertions.assertTextlineHasColor('Awesome', new Color(0, 0, 0), 0);
619
+ await assertions.assertParagraphIsAt('Awesome', 50, 100, 0);
620
+ });
621
+
402
622
  test('Symbol and ZapfDingbats fonts are available as standard fonts', () => {
403
623
  expect(StandardFonts.SYMBOL).toBe('Symbol');
404
624
  expect(StandardFonts.ZAPF_DINGBATS).toBe('ZapfDingbats');
405
625
  });
626
+
406
627
  });
@@ -4,6 +4,7 @@
4
4
 
5
5
  import {requireEnvAndFixture} from './test-helpers';
6
6
  import {PDFDancer} from "../../pdfdancer_v1";
7
+ import {PDFAssertions} from './pdf-assertions';
7
8
 
8
9
  describe('Path E2E Tests (New API)', () => {
9
10
 
@@ -47,6 +48,10 @@ describe('Path E2E Tests (New API)', () => {
47
48
 
48
49
  const allPaths = await pdf.selectPaths();
49
50
  expect(allPaths).toHaveLength(8);
51
+
52
+ const assertions = await PDFAssertions.create(pdf);
53
+ await assertions.assertNoPathAt(80, 720);
54
+ await assertions.assertNumberOfPaths(8);
50
55
  });
51
56
 
52
57
  test('move path', async () => {
@@ -67,5 +72,8 @@ describe('Path E2E Tests (New API)', () => {
67
72
  const movedPos = moved[0].position;
68
73
  expect(movedPos.getX()).toBeCloseTo(50.1, 1);
69
74
  expect(movedPos.getY()).toBeCloseTo(100, 1);
75
+
76
+ const assertions = await PDFAssertions.create(pdf);
77
+ await assertions.assertPathIsAt('PATH_000001', 50.1, 100);
70
78
  });
71
79
  });
@@ -0,0 +1,241 @@
1
+ /**
2
+ * PDF Assertions helper for e2e tests
3
+ */
4
+
5
+ import fs from 'fs';
6
+ import os from 'os';
7
+ import path from 'path';
8
+ import {Color, Orientation, PDFDancer} from '../../index';
9
+ import {expectWithin} from '../assertions';
10
+
11
+ export class PDFAssertions {
12
+ private pdf: PDFDancer;
13
+
14
+ private constructor(pdf: PDFDancer) {
15
+ this.pdf = pdf;
16
+ }
17
+
18
+ static async create(sourcePdf: PDFDancer): Promise<PDFAssertions> {
19
+ // Save and reload the PDF to ensure all changes are persisted
20
+ // This matches the Python implementation
21
+ const token = (sourcePdf as any)._token;
22
+ const baseUrl = (sourcePdf as any)._baseUrl;
23
+
24
+ // Create a temporary file
25
+ const tempFile = path.join(os.tmpdir(), `test-${Date.now()}.pdf`);
26
+ await sourcePdf.save(tempFile);
27
+
28
+ // Reopen the PDF with the same token and baseUrl
29
+ const pdfData = fs.readFileSync(tempFile);
30
+ const pdf = await PDFDancer.open(new Uint8Array(pdfData), token, baseUrl);
31
+
32
+ return new PDFAssertions(pdf);
33
+ }
34
+
35
+ private async aggregateCounts(pageIndex?: number) {
36
+ const [paragraphs, textLines, images, paths, forms, fields] = await Promise.all([
37
+ this.pdf.selectParagraphs(),
38
+ this.pdf.selectLines(),
39
+ this.pdf.selectImages(),
40
+ this.pdf.selectPaths(),
41
+ this.pdf.selectForms(),
42
+ this.pdf.selectFormFields()
43
+ ]);
44
+
45
+ const filter = <T extends { position: { pageIndex?: number } }>(items: T[]): T[] => {
46
+ if (pageIndex === undefined) {
47
+ return items;
48
+ }
49
+ return items.filter(item => item.position.pageIndex === pageIndex);
50
+ };
51
+
52
+ return {
53
+ paragraphs: filter(paragraphs).length,
54
+ textLines: filter(textLines).length,
55
+ images: filter(images).length,
56
+ paths: filter(paths).length,
57
+ forms: filter(forms).length,
58
+ fields: filter(fields).length
59
+ };
60
+ }
61
+
62
+ async assertTotalNumberOfElements(expected: number, pageIndex?: number): Promise<this> {
63
+ const totals = await this.aggregateCounts(pageIndex);
64
+ const actual = Object.values(totals).reduce((acc, count) => acc + count, 0);
65
+ expect(actual).toBe(expected);
66
+ return this;
67
+ }
68
+
69
+ async assertPageCount(expected: number): Promise<this> {
70
+ const pages = await this.pdf.pages();
71
+ expect(pages.length).toBe(expected);
72
+ return this;
73
+ }
74
+
75
+ async assertPageDimension(width: number, height: number, orientation?: Orientation, pageIndex = 0): Promise<this> {
76
+ const pages = await this.pdf.pages();
77
+ expect(pageIndex).toBeLessThan(pages.length);
78
+ const page = pages[pageIndex];
79
+ expect(page.pageSize?.width).toBeCloseTo(width, 6);
80
+ expect(page.pageSize?.height).toBeCloseTo(height, 6);
81
+ if (orientation) {
82
+ expect(page.orientation).toBe(orientation);
83
+ }
84
+ return this;
85
+ }
86
+
87
+ async assertParagraphIsAt(text: string, x: number, y: number, page = 0, epsilon = 1e-4): Promise<this> {
88
+ const paragraphs = await this.pdf.page(page).selectParagraphsMatching(`.*${text}.*`);
89
+ expect(paragraphs.length).toBeGreaterThan(0);
90
+ const reference = paragraphs[0].objectRef();
91
+ expectWithin(reference.position.getX()!, x, epsilon);
92
+ expectWithin(reference.position.getY()!, y, epsilon);
93
+
94
+ const byPosition = await this.pdf.page(page).selectParagraphsAt(x, y);
95
+ expect(byPosition.length).toBeGreaterThan(0);
96
+ return this;
97
+ }
98
+
99
+ async assertTextHasFont(text: string, fontName: string, fontSize: number, page = 0): Promise<this> {
100
+ await this.assertTextlineHasFont(text, fontName, fontSize, page);
101
+
102
+ const paragraphs = await this.pdf.page(page).selectParagraphsMatching(`.*${text}.*`);
103
+ expect(paragraphs.length).toBeGreaterThan(0);
104
+ const reference = paragraphs[0].objectRef();
105
+ expect(reference.fontName).toBe(fontName);
106
+ expectWithin(reference.fontSize!, fontSize, 1e-6);
107
+ return this;
108
+ }
109
+
110
+ async assertTextHasFontMatching(text: string, fontName: string, fontSize: number, page = 0): Promise<this> {
111
+ await this.assertTextlineHasFontMatching(text, fontName, fontSize, page);
112
+ return this;
113
+ }
114
+
115
+ async assertTextHasColor(text: string, color: Color, page = 0): Promise<this> {
116
+ await this.assertTextlineHasColor(text, color, page);
117
+ return this;
118
+ }
119
+
120
+ async assertTextlineHasColor(text: string, color: Color, page = 0): Promise<this> {
121
+ const lines = await this.pdf.page(page).selectTextLinesMatching(text);
122
+ expect(lines.length).toBe(1);
123
+ const reference = lines[0].objectRef();
124
+ const refColor = reference.color;
125
+ expect(refColor).toEqual(color);
126
+ expect(reference.text).toContain(text);
127
+ return this;
128
+ }
129
+
130
+ async assertTextlineHasFont(text: string, fontName: string, fontSize: number, page = 0): Promise<this> {
131
+ const lines = await this.pdf.page(page).selectTextLinesStartingWith(text);
132
+ expect(lines.length).toBe(1);
133
+ const reference = lines[0].objectRef();
134
+ expect(reference.fontName).toBe(fontName);
135
+ expectWithin(reference.fontSize!, fontSize, 1e-6);
136
+ return this;
137
+ }
138
+
139
+ async assertTextlineHasFontMatching(text: string, fontName: string, fontSize: number, page = 0): Promise<this> {
140
+ const lines = await this.pdf.page(page).selectTextLinesStartingWith(text);
141
+ expect(lines.length).toBe(1);
142
+ const reference = lines[0].objectRef();
143
+ expect((reference.fontName ?? '').includes(fontName)).toBe(true);
144
+ expectWithin(reference.fontSize!, fontSize, 1e-6);
145
+ return this;
146
+ }
147
+
148
+ async assertTextlineIsAt(text: string, x: number, y: number, page = 0, epsilon = 1e-4): Promise<this> {
149
+ const lines = await this.pdf.page(page).selectTextLinesStartingWith(text);
150
+ expect(lines.length).toBe(1);
151
+ const reference = lines[0].objectRef();
152
+ expectWithin(reference.position.getX()!, x, epsilon);
153
+ expectWithin(reference.position.getY()!, y, epsilon);
154
+ const byPosition = await this.pdf.page(page).selectTextLinesAt(x, y);
155
+ expect(byPosition.length).toBeGreaterThan(0);
156
+ return this;
157
+ }
158
+
159
+ async assertPathIsAt(internalId: string, x: number, y: number, page = 0, epsilon = 1e-4): Promise<this> {
160
+ const paths = await this.pdf.page(page).selectPathsAt(x, y);
161
+ expect(paths.length).toBe(1);
162
+ const reference = paths[0];
163
+ expect(reference.internalId).toBe(internalId);
164
+ expectWithin(reference.position.getX()!, x, epsilon);
165
+ expectWithin(reference.position.getY()!, y, epsilon);
166
+ return this;
167
+ }
168
+
169
+ async assertNoPathAt(x: number, y: number, page = 0): Promise<this> {
170
+ const paths = await this.pdf.page(page).selectPathsAt(x, y);
171
+ expect(paths.length).toBe(0);
172
+ return this;
173
+ }
174
+
175
+ async assertNumberOfPaths(expected: number, page?: number): Promise<this> {
176
+ const paths = page === undefined ? await this.pdf.selectPaths() : await this.pdf.page(page).selectPaths();
177
+ expect(paths.length).toBe(expected);
178
+ return this;
179
+ }
180
+
181
+ async assertNumberOfImages(expected: number, page?: number): Promise<this> {
182
+ const images = page === undefined ? await this.pdf.selectImages() : await this.pdf.page(page).selectImages();
183
+ expect(images.length).toBe(expected);
184
+ return this;
185
+ }
186
+
187
+ async assertImageAt(x: number, y: number, page = 0): Promise<this> {
188
+ const images = await this.pdf.page(page).selectImagesAt(x, y);
189
+ expect(images.length).toBe(1);
190
+ return this;
191
+ }
192
+
193
+ async assertNoImageAt(x: number, y: number, page = 0): Promise<this> {
194
+ const images = await this.pdf.page(page).selectImagesAt(x, y);
195
+ expect(images.length).toBe(0);
196
+ return this;
197
+ }
198
+
199
+ async assertImageWithIdAt(internalId: string, x: number, y: number, page = 0): Promise<this> {
200
+ const images = await this.pdf.page(page).selectImagesAt(x, y);
201
+ expect(images.length).toBe(1);
202
+ expect(images[0].internalId).toBe(internalId);
203
+ return this;
204
+ }
205
+
206
+ async assertNumberOfFormXObjects(expected: number, page?: number): Promise<this> {
207
+ const forms = page === undefined ? await this.pdf.selectForms() : await this.pdf.page(page).selectForms();
208
+ expect(forms.length).toBe(expected);
209
+ return this;
210
+ }
211
+
212
+ async assertNumberOfFormFields(expected: number, page?: number): Promise<this> {
213
+ const fields = page === undefined ? await this.pdf.selectFormFields() : await this.pdf.page(page).selectFormFields();
214
+ expect(fields.length).toBe(expected);
215
+ return this;
216
+ }
217
+
218
+ async assertParagraphExists(text: string, page = 0): Promise<this> {
219
+ const paragraphs = await this.pdf.page(page).selectParagraphsStartingWith(text);
220
+ expect(paragraphs.length).toBeGreaterThan(0);
221
+ return this;
222
+ }
223
+
224
+ async assertParagraphDoesNotExist(text: string, page = 0): Promise<this> {
225
+ const paragraphs = await this.pdf.page(page).selectParagraphsStartingWith(text);
226
+ expect(paragraphs.length).toBe(0);
227
+ return this;
228
+ }
229
+
230
+ async assertTextlineExists(text: string, page = 0): Promise<this> {
231
+ const lines = await this.pdf.page(page).selectTextLinesStartingWith(text);
232
+ expect(lines.length).toBeGreaterThan(0);
233
+ return this;
234
+ }
235
+
236
+ async assertTextlineDoesNotExist(text: string, page = 0): Promise<this> {
237
+ const lines = await this.pdf.page(page).selectTextLinesStartingWith(text);
238
+ expect(lines.length).toBe(0);
239
+ return this;
240
+ }
241
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Unit tests for URL building helper
3
+ */
4
+
5
+ describe('URL Building', () => {
6
+ // Helper function to simulate the _buildUrl logic
7
+ function buildUrl(baseUrl: string, path: string): string {
8
+ const base = baseUrl.replace(/\/+$/, '');
9
+ const endpoint = path.replace(/^\/+/, '');
10
+ return `${base}/${endpoint}`;
11
+ }
12
+
13
+ test('handles base URL with trailing slash', () => {
14
+ expect(buildUrl('http://localhost:8080/', '/session/create')).toBe('http://localhost:8080/session/create');
15
+ });
16
+
17
+ test('handles base URL without trailing slash', () => {
18
+ expect(buildUrl('http://localhost:8080', '/session/create')).toBe('http://localhost:8080/session/create');
19
+ });
20
+
21
+ test('handles path without leading slash', () => {
22
+ expect(buildUrl('http://localhost:8080', 'session/create')).toBe('http://localhost:8080/session/create');
23
+ });
24
+
25
+ test('handles both with slashes', () => {
26
+ expect(buildUrl('http://localhost:8080/', '/session/create')).toBe('http://localhost:8080/session/create');
27
+ });
28
+
29
+ test('handles neither with slashes', () => {
30
+ expect(buildUrl('http://localhost:8080', 'session/create')).toBe('http://localhost:8080/session/create');
31
+ });
32
+
33
+ test('handles multiple trailing slashes', () => {
34
+ expect(buildUrl('http://localhost:8080///', '///session/create')).toBe('http://localhost:8080/session/create');
35
+ });
36
+
37
+ test('handles production URL', () => {
38
+ expect(buildUrl('https://api.pdfdancer.com/', '/pdf/find')).toBe('https://api.pdfdancer.com/pdf/find');
39
+ });
40
+
41
+ test('handles base URL with path', () => {
42
+ expect(buildUrl('http://localhost:8080/api/', '/session/create')).toBe('http://localhost:8080/api/session/create');
43
+ });
44
+ });
package/src/index.ts CHANGED
@@ -18,6 +18,8 @@ export {
18
18
  export {
19
19
  ObjectRef,
20
20
  FormFieldRef,
21
+ PageRef,
22
+ PageSize,
21
23
  TextObjectRef,
22
24
  Position,
23
25
  ObjectType,
@@ -29,7 +31,12 @@ export {
29
31
  PositionMode,
30
32
  ShapeType,
31
33
  Point,
32
- StandardFonts
34
+ StandardFonts,
35
+ Orientation,
36
+ CommandResult,
37
+ TextStatus,
38
+ FontRecommendation,
39
+ FontType
33
40
  } from './models';
34
41
 
35
42
  export const VERSION = "1.0.0";