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.
Files changed (65) hide show
  1. package/.github/workflows/ci.yml +1 -1
  2. package/README.md +1 -1
  3. package/dist/__tests__/e2e/pdf-assertions.d.ts +1 -0
  4. package/dist/__tests__/e2e/pdf-assertions.d.ts.map +1 -1
  5. package/dist/__tests__/e2e/pdf-assertions.js +9 -3
  6. package/dist/__tests__/e2e/pdf-assertions.js.map +1 -1
  7. package/dist/fingerprint.d.ts +12 -0
  8. package/dist/fingerprint.d.ts.map +1 -0
  9. package/dist/fingerprint.js +196 -0
  10. package/dist/fingerprint.js.map +1 -0
  11. package/dist/image-builder.d.ts +4 -2
  12. package/dist/image-builder.d.ts.map +1 -1
  13. package/dist/image-builder.js +12 -3
  14. package/dist/image-builder.js.map +1 -1
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +7 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/models.d.ts +75 -8
  20. package/dist/models.d.ts.map +1 -1
  21. package/dist/models.js +179 -21
  22. package/dist/models.js.map +1 -1
  23. package/dist/page-builder.d.ts +24 -0
  24. package/dist/page-builder.d.ts.map +1 -0
  25. package/dist/page-builder.js +107 -0
  26. package/dist/page-builder.js.map +1 -0
  27. package/dist/paragraph-builder.d.ts +48 -54
  28. package/dist/paragraph-builder.d.ts.map +1 -1
  29. package/dist/paragraph-builder.js +408 -135
  30. package/dist/paragraph-builder.js.map +1 -1
  31. package/dist/pdfdancer_v1.d.ts +90 -9
  32. package/dist/pdfdancer_v1.d.ts.map +1 -1
  33. package/dist/pdfdancer_v1.js +535 -50
  34. package/dist/pdfdancer_v1.js.map +1 -1
  35. package/dist/types.d.ts +24 -3
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/types.js +117 -2
  38. package/dist/types.js.map +1 -1
  39. package/docs/openapi.yml +2076 -0
  40. package/fixtures/Showcase.pdf +0 -0
  41. package/package.json +1 -1
  42. package/src/__tests__/e2e/acroform.test.ts +5 -5
  43. package/src/__tests__/e2e/context-manager-showcase.test.ts +267 -0
  44. package/src/__tests__/e2e/form_x_object.test.ts +1 -1
  45. package/src/__tests__/e2e/image-showcase.test.ts +133 -0
  46. package/src/__tests__/e2e/image.test.ts +1 -1
  47. package/src/__tests__/e2e/line-showcase.test.ts +118 -0
  48. package/src/__tests__/e2e/line.test.ts +1 -16
  49. package/src/__tests__/e2e/page-showcase.test.ts +154 -0
  50. package/src/__tests__/e2e/paragraph-showcase.test.ts +523 -0
  51. package/src/__tests__/e2e/paragraph.test.ts +8 -8
  52. package/src/__tests__/e2e/pdf-assertions.ts +10 -3
  53. package/src/__tests__/e2e/pdfdancer-showcase.test.ts +40 -0
  54. package/src/__tests__/e2e/snapshot-showcase.test.ts +158 -0
  55. package/src/__tests__/e2e/snapshot.test.ts +296 -0
  56. package/src/__tests__/fingerprint.test.ts +36 -0
  57. package/src/fingerprint.ts +169 -0
  58. package/src/image-builder.ts +13 -6
  59. package/src/index.ts +6 -1
  60. package/src/models.ts +208 -24
  61. package/src/page-builder.ts +130 -0
  62. package/src/paragraph-builder.ts +517 -159
  63. package/src/pdfdancer_v1.ts +630 -51
  64. package/src/types.ts +145 -2
  65. package/update-api-spec.sh +3 -0
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdfdancer-client-typescript",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
4
4
  "description": "A TypeScript client library for the PDFDancer PDF manipulation API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,7 +15,7 @@ describe('AcroForm Fields E2E Tests (v2 API)', () => {
15
15
  const fields = await pdf.selectFormFields();
16
16
  expect(fields).toHaveLength(10);
17
17
  expect(fields[0].type).toBe('TEXT_FIELD');
18
- expect(fields[4].type).toBe('CHECK_BOX');
18
+ expect(fields[4].type).toBe('CHECKBOX');
19
19
  expect(fields[6].type).toBe('RADIO_BUTTON');
20
20
 
21
21
  let allAtOrigin = true;
@@ -30,7 +30,7 @@ describe('AcroForm Fields E2E Tests (v2 API)', () => {
30
30
  const firstPageFields = await pdf.page(0).selectFormFields();
31
31
  expect(firstPageFields).toHaveLength(10);
32
32
 
33
- const firstForm = await pdf.page(0).selectFormFieldsAt(290, 460);
33
+ const firstForm = await pdf.page(0).selectFormFieldsAt(280, 455, 1);
34
34
  expect(firstForm).toHaveLength(1);
35
35
  expect(firstForm[0].type).toBe('RADIO_BUTTON');
36
36
  expect(firstForm[0].internalId).toBe('FORM_FIELD_000008');
@@ -63,7 +63,7 @@ describe('AcroForm Fields E2E Tests (v2 API)', () => {
63
63
  const [baseUrl, token, pdfData] = await requireEnvAndFixture('mixed-form-types.pdf');
64
64
  const pdf = await PDFDancer.open(pdfData, token, baseUrl);
65
65
 
66
- let fields = await pdf.page(0).selectFormFieldsAt(290, 460);
66
+ let fields = await pdf.page(0).selectFormFieldsAt(280, 455, 1);
67
67
  expect(fields).toHaveLength(1);
68
68
  const field = fields[0];
69
69
  expect(Math.abs((field.position.getX() ?? 0) - 280)).toBeLessThan(0.1);
@@ -71,10 +71,10 @@ describe('AcroForm Fields E2E Tests (v2 API)', () => {
71
71
 
72
72
  await field.moveTo(30, 40);
73
73
 
74
- fields = await pdf.page(0).selectFormFieldsAt(290, 460);
74
+ fields = await pdf.page(0).selectFormFieldsAt(280, 455, 1);
75
75
  expect(fields).toHaveLength(0);
76
76
 
77
- fields = await pdf.page(0).selectFormFieldsAt(30, 40);
77
+ fields = await pdf.page(0).selectFormFieldsAt(30, 40, 1);
78
78
  expect(fields).toHaveLength(1);
79
79
  expect(fields[0].internalId).toBe(field.internalId);
80
80
 
@@ -0,0 +1,267 @@
1
+ import {Color, PDFDancer, StandardFonts} from '../../index';
2
+ import {requireEnvAndFixture} from './test-helpers';
3
+ import {PDFAssertions} from './pdf-assertions';
4
+
5
+ const SAMPLE_PARAGRAPH = 'This is regular Sans text showing alignment and styles.';
6
+
7
+ describe('Paragraph Edit Session E2E Tests (Showcase)', () => {
8
+ test('basic usage', async () => {
9
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
10
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
11
+
12
+ const paragraphs = await pdf.selectParagraphs();
13
+ expect(paragraphs.length).toBe(24);
14
+ });
15
+
16
+ test('edit text only', async () => {
17
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
18
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
19
+
20
+ const [paragraph] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
21
+ await paragraph.edit().replace('This is replaced\ntext on two lines').apply();
22
+
23
+ const assertions = await PDFAssertions.create(pdf);
24
+ await assertions.assertTextlineExists('This is replaced', 0);
25
+ await assertions.assertTextlineExists('text on two lines', 0);
26
+ await assertions.assertTextlineDoesNotExist(SAMPLE_PARAGRAPH, 0);
27
+ });
28
+
29
+ test('edit font only', async () => {
30
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
31
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
32
+
33
+ const [paragraph] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
34
+ await paragraph.edit().font('Helvetica', 28).apply();
35
+
36
+ const assertions = await PDFAssertions.create(pdf);
37
+ await assertions.assertTextlineHasFont(SAMPLE_PARAGRAPH, 'Helvetica', 28);
38
+ await assertions.assertTextlineHasColor(SAMPLE_PARAGRAPH, new Color(0, 0, 0));
39
+ });
40
+
41
+ test('edit text and font', async () => {
42
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
43
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
44
+
45
+ const [paragraph] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
46
+ await paragraph.edit().replace('New Text\nHere').font('Helvetica', 16).apply();
47
+
48
+ const assertions = await PDFAssertions.create(pdf);
49
+ await assertions.assertTextlineHasFont('New Text', 'Helvetica', 16);
50
+ await assertions.assertTextlineHasFont('Here', 'Helvetica', 16);
51
+ await assertions.assertTextlineDoesNotExist(SAMPLE_PARAGRAPH);
52
+ });
53
+
54
+ test('edit all properties', async () => {
55
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
56
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
57
+
58
+ const [paragraph] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
59
+ await paragraph.edit()
60
+ .replace('Fully\nModified')
61
+ .font('Helvetica', 18)
62
+ .color(new Color(255, 0, 0))
63
+ .lineSpacing(1.5)
64
+ .moveTo(100, 200)
65
+ .apply();
66
+
67
+ const assertions = await PDFAssertions.create(pdf);
68
+ await assertions.assertTextlineHasFont('Fully', 'Helvetica', 18);
69
+ await assertions.assertTextlineHasFont('Modified', 'Helvetica', 18);
70
+ await assertions.assertTextlineHasColor('Fully', new Color(255, 0, 0));
71
+ await assertions.assertTextlineHasColor('Modified', new Color(255, 0, 0));
72
+ await assertions.assertParagraphIsAt('Fully', 100, 200, 0);
73
+ });
74
+
75
+ test('edit color only', async () => {
76
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
77
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
78
+
79
+ const [paragraph] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
80
+ await paragraph.edit().color(new Color(0, 255, 0)).apply();
81
+
82
+ const assertions = await PDFAssertions.create(pdf);
83
+ await assertions.assertTextlineHasColor(SAMPLE_PARAGRAPH, new Color(0, 255, 0));
84
+ });
85
+
86
+ test('edit line spacing only', async () => {
87
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
88
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
89
+
90
+ const [paragraph] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
91
+ await paragraph.edit().lineSpacing(2.0).apply();
92
+
93
+ const assertions = await PDFAssertions.create(pdf);
94
+ await assertions.assertTextlineExists(SAMPLE_PARAGRAPH);
95
+ });
96
+
97
+ test('edit move only', async () => {
98
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
99
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
100
+
101
+ const [paragraph] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
102
+ await paragraph.edit().moveTo(150, 300).apply();
103
+
104
+ const assertions = await PDFAssertions.create(pdf);
105
+ await assertions.assertTextlineHasFont(SAMPLE_PARAGRAPH, 'AAAZPH+Roboto-Regular', 12);
106
+ await assertions.assertParagraphIsAt(SAMPLE_PARAGRAPH, 150, 300, 0, 0.22);
107
+ });
108
+
109
+ test('multiple edits sequential', async () => {
110
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
111
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
112
+
113
+ const [first] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
114
+ await first.edit().replace('First Edit').apply();
115
+
116
+ const [second] = await pdf.page(0).selectParagraphsStartingWith('First Edit');
117
+ await second.edit().replace('Second Edit').font('Helvetica', 20).apply();
118
+
119
+ const assertions = await PDFAssertions.create(pdf);
120
+ await assertions.assertTextlineHasFont('Second Edit', 'Helvetica', 20);
121
+ await assertions.assertTextlineDoesNotExist('First Edit');
122
+ await assertions.assertTextlineDoesNotExist(SAMPLE_PARAGRAPH);
123
+ });
124
+
125
+ test('edit multiple paragraphs', async () => {
126
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
127
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
128
+
129
+ const paragraphs = await pdf.page(0).selectParagraphs();
130
+ await paragraphs[0].edit().replace('Modified First').font('Helvetica', 14).apply();
131
+ await paragraphs[1].edit().replace('Modified Second').font('Helvetica', 14).apply();
132
+
133
+ const assertions = await PDFAssertions.create(pdf);
134
+ await assertions.assertTextlineHasFont('Modified First', 'Helvetica', 14);
135
+ await assertions.assertTextlineHasFont('Modified Second', 'Helvetica', 14);
136
+ });
137
+
138
+ test('edit with exception does not apply', async () => {
139
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
140
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
141
+
142
+ const [paragraph] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
143
+ const editor = paragraph.edit();
144
+ editor.replace('Should Fail');
145
+
146
+ await expect(async () => {
147
+ throw new Error('boom');
148
+ }).rejects.toThrow('boom');
149
+
150
+ const remaining = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
151
+ expect(remaining.length).toBeGreaterThan(0);
152
+ });
153
+
154
+ test('nested PDF and edit sessions', async () => {
155
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
156
+ const pdf1 = await PDFDancer.open(pdfData, token, baseUrl);
157
+ const pdf2 = await PDFDancer.new({initialPageCount: 1}, token, baseUrl);
158
+
159
+ await pdf2.page(0).newParagraph()
160
+ .text('Temporary Text')
161
+ .font(StandardFonts.HELVETICA, 12)
162
+ .at(50, 50)
163
+ .apply();
164
+
165
+ const [paragraph] = await pdf1.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
166
+ await paragraph.edit().replace('Nested Edit').apply();
167
+
168
+ const assertions = await PDFAssertions.create(pdf1);
169
+ await assertions.assertTextlineExists('Nested Edit');
170
+ });
171
+
172
+ test('edit preserves position when not specified', async () => {
173
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
174
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
175
+
176
+ const [paragraph] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
177
+ const original = paragraph.position;
178
+ await paragraph.edit().replace('No Move').apply();
179
+
180
+ const [updated] = await pdf.page(0).selectParagraphsStartingWith('No Move');
181
+ expect(updated.position.getX()).toBeCloseTo(original.getX()!, 5);
182
+ expect(updated.position.getY()).toBeCloseTo(original.getY()!, 5);
183
+ });
184
+
185
+ test('edit chaining', async () => {
186
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
187
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
188
+
189
+ const [paragraph] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
190
+ await paragraph.edit()
191
+ .replace('Chained\nEdits')
192
+ .font('Helvetica', 15)
193
+ .color(new Color(128, 128, 128))
194
+ .lineSpacing(1.8)
195
+ .apply();
196
+
197
+ const assertions = await PDFAssertions.create(pdf);
198
+ await assertions.assertTextlineHasFont('Chained', 'Helvetica', 15);
199
+ await assertions.assertTextlineHasFont('Edits', 'Helvetica', 15);
200
+ await assertions.assertTextlineHasColor('Chained', new Color(128, 128, 128));
201
+ });
202
+
203
+ test('edit with standard fonts', async () => {
204
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
205
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
206
+
207
+ const [paragraph] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
208
+ await paragraph.edit().replace('Times Roman').font(StandardFonts.TIMES_ROMAN, 18).apply();
209
+
210
+ const assertions = await PDFAssertions.create(pdf);
211
+ await assertions.assertTextlineHasFont('Times Roman', StandardFonts.TIMES_ROMAN, 18);
212
+ });
213
+
214
+ test('edit with multiline text', async () => {
215
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
216
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
217
+
218
+ const [paragraph] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
219
+ await paragraph.edit().replace('Line 1\nLine 2').font('Helvetica', 12).apply();
220
+
221
+ const assertions = await PDFAssertions.create(pdf);
222
+ await assertions.assertTextlineExists('Line 1');
223
+ await assertions.assertTextlineExists('Line 2');
224
+ });
225
+
226
+ test('edit with empty text', async () => {
227
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
228
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
229
+
230
+ const [paragraph] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
231
+ await paragraph.edit().replace('').font('Helvetica', 12).apply();
232
+
233
+ const assertions = await PDFAssertions.create(pdf);
234
+ await assertions.assertTextlineDoesNotExist(SAMPLE_PARAGRAPH);
235
+ });
236
+
237
+ test('example from docs', async () => {
238
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
239
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
240
+
241
+ const [paragraph] = await pdf.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
242
+ await paragraph.edit().replace('Awesomely\nObvious!').font('Helvetica', 12).apply();
243
+
244
+ const assertions = await PDFAssertions.create(pdf);
245
+ await assertions.assertTextlineHasFont('Awesomely', 'Helvetica', 12);
246
+ await assertions.assertTextlineHasFont('Obvious!', 'Helvetica', 12);
247
+ await assertions.assertTextlineDoesNotExist(SAMPLE_PARAGRAPH);
248
+ });
249
+
250
+ test('vs manual apply', async () => {
251
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
252
+ const pdf1 = await PDFDancer.open(pdfData, token, baseUrl);
253
+
254
+ const [paragraph1] = await pdf1.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
255
+ await paragraph1.edit().replace('Test Text').font('Helvetica', 14).color(new Color(255, 0, 0)).apply();
256
+
257
+ const assertions1 = await PDFAssertions.create(pdf1);
258
+ await assertions1.assertTextlineHasFont('Test Text', 'Helvetica', 14);
259
+
260
+ const pdf2 = await PDFDancer.open(pdfData, token, baseUrl);
261
+ const [paragraph2] = await pdf2.page(0).selectParagraphsStartingWith(SAMPLE_PARAGRAPH);
262
+ await paragraph2.edit().replace('Test Text').font('Helvetica', 14).color(new Color(255, 0, 0)).apply();
263
+
264
+ const assertions2 = await PDFAssertions.create(pdf2);
265
+ await assertions2.assertTextlineHasFont('Test Text', 'Helvetica', 14);
266
+ });
267
+ });
@@ -48,7 +48,7 @@ describe('Form E2E Tests (v2 API)', () => {
48
48
  expect(forms).toHaveLength(0);
49
49
 
50
50
  // Page 0, position (321,601) — expect a form
51
- forms = await pdf.page(0).selectFormsAt(321, 601);
51
+ forms = await pdf.page(0).selectFormsAt(321, 601, 1);
52
52
  expect(forms).toHaveLength(1);
53
53
  expect(forms[0].internalId).toBe('FORM_000005');
54
54
 
@@ -0,0 +1,133 @@
1
+ import fs from 'fs';
2
+ import {ObjectType, PDFDancer} from '../../index';
3
+ import {getImagePath, requireEnvAndFixture} from './test-helpers';
4
+ import {PDFAssertions} from './pdf-assertions';
5
+
6
+ describe('Image E2E Tests (Showcase)', () => {
7
+ test('find images', async () => {
8
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
9
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
10
+
11
+ const images = await pdf.selectImages();
12
+ expect(images.length).toBe(12);
13
+ expect(images[0].type).toBe(ObjectType.IMAGE);
14
+
15
+ const pageImages = await pdf.page(0).selectImages();
16
+ expect(pageImages.length).toBe(2);
17
+ });
18
+
19
+ test('delete all images', async () => {
20
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
21
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
22
+
23
+ const images = await pdf.selectImages();
24
+ expect(images.length).toBe(12);
25
+ for (const image of images) {
26
+ await image.delete();
27
+ }
28
+
29
+ expect(await pdf.selectImages()).toHaveLength(0);
30
+
31
+ const assertions = await PDFAssertions.create(pdf);
32
+ for (const page of await pdf.pages()) {
33
+ await assertions.assertNumberOfImages(0, page.position.pageIndex ?? 0);
34
+ }
35
+ });
36
+
37
+ test('move image', async () => {
38
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
39
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
40
+
41
+ const images = await pdf.selectImages();
42
+ const image = images[10];
43
+ const position = image.position;
44
+ const originalX = position.getX()!;
45
+ const originalY = position.getY()!;
46
+ expect(position.pageIndex).toBe(5);
47
+
48
+ const newX = 500.1;
49
+ const newY = 600.1;
50
+ await image.moveTo(newX, newY);
51
+
52
+ const moved = (await pdf.page(5).selectImagesAt(newX, newY))[0];
53
+ expect(moved.position).toBeDefined();
54
+ expect(Math.abs(moved.position.getX()! - newX)).toBeLessThanOrEqual(0.05);
55
+ expect(Math.abs(moved.position.getY()! - newY)).toBeLessThanOrEqual(0.05);
56
+
57
+ const assertions = await PDFAssertions.create(pdf);
58
+ await assertions.assertImageAt(newX, newY, 5);
59
+ await assertions.assertNoImageAt(originalX, originalY, 5);
60
+ });
61
+
62
+ test('find image by position', async () => {
63
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
64
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
65
+
66
+ const none = await pdf.page(5).selectImagesAt(0, 0);
67
+ expect(none.length).toBe(0);
68
+
69
+ const found = await pdf.page(5).selectImagesAt(57, 55, 1);
70
+ expect(found.length).toBe(1);
71
+ expect(found[0].internalId).toBe('IMAGE_000011');
72
+ });
73
+
74
+ test('add image via document builder', async () => {
75
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
76
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
77
+
78
+ expect(await pdf.selectImages()).toHaveLength(12);
79
+ expect(await pdf.page(6).selectImages()).toHaveLength(1);
80
+
81
+ const fixturePath = getImagePath('logo-80.png');
82
+ expect(fs.existsSync(fixturePath)).toBe(true);
83
+
84
+ await pdf.newImage()
85
+ .fromFile(fixturePath)
86
+ .at(6, 50.1, 98.0)
87
+ .add();
88
+
89
+ expect(await pdf.selectImages()).toHaveLength(13);
90
+ const pageImages = await pdf.page(6).selectImages();
91
+ expect(pageImages.length).toBe(2);
92
+
93
+ const added = pageImages[1];
94
+ expect(added.position.pageIndex).toBe(6);
95
+ expect(added.internalId).toBe('IMAGE_000013');
96
+ expect(Math.abs(added.position.getX()! - 50.1)).toBeLessThanOrEqual(0.05);
97
+ expect(Math.abs(added.position.getY()! - 98.0)).toBeLessThanOrEqual(0.05);
98
+
99
+ const assertions = await PDFAssertions.create(pdf);
100
+ await assertions.assertImageAt(50.1, 98, 6);
101
+ await assertions.assertNumberOfImages(2, 6);
102
+ });
103
+
104
+ test('add image via page builder', async () => {
105
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
106
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
107
+
108
+ expect(await pdf.selectImages()).toHaveLength(12);
109
+ expect(await pdf.page(6).selectImages()).toHaveLength(1);
110
+
111
+ const fixturePath = getImagePath('logo-80.png');
112
+ expect(fs.existsSync(fixturePath)).toBe(true);
113
+
114
+ await pdf.page(6).newImage()
115
+ .fromFile(fixturePath)
116
+ .at(50.1, 98.0)
117
+ .add();
118
+
119
+ expect(await pdf.selectImages()).toHaveLength(13);
120
+ const pageImages = await pdf.page(6).selectImages();
121
+ expect(pageImages.length).toBe(2);
122
+
123
+ const added = pageImages[1];
124
+ expect(added.position.pageIndex).toBe(6);
125
+ expect(added.internalId).toBe('IMAGE_000013');
126
+ expect(Math.abs(added.position.getX()! - 50.1)).toBeLessThanOrEqual(0.05);
127
+ expect(Math.abs(added.position.getY()! - 98.0)).toBeLessThanOrEqual(0.05);
128
+
129
+ const assertions = await PDFAssertions.create(pdf);
130
+ await assertions.assertImageAt(50.1, 98, 6);
131
+ await assertions.assertNumberOfImages(2, 6);
132
+ });
133
+ });
@@ -84,7 +84,7 @@ describe('Image E2E Tests (v2 API)', () => {
84
84
  const none = await pdf.page(11).selectImagesAt(0, 0);
85
85
  expect(none).toHaveLength(0);
86
86
 
87
- const found = await pdf.page(11).selectImagesAt(55, 310);
87
+ const found = await pdf.page(11).selectImagesAt(54, 300, 1);
88
88
  expect(found).toHaveLength(1);
89
89
  expect(found[0].internalId).toBe('IMAGE_000003');
90
90
  });
@@ -0,0 +1,118 @@
1
+ import {FontType, PDFDancer} from '../../index';
2
+ import {requireEnvAndFixture} from './test-helpers';
3
+ import {PDFAssertions} from './pdf-assertions';
4
+
5
+ const SAMPLE_PARAGRAPH = 'This is regular Sans text showing alignment and styles.';
6
+
7
+ describe('Text Line E2E Tests (Showcase)', () => {
8
+ test('find lines by position multi', async () => {
9
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
10
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
11
+
12
+ for (let i = 0; i < 10; i++) {
13
+ const lines = await pdf.selectTextLines();
14
+ for (const line of lines) {
15
+ const status = line.objectRef().status;
16
+ expect(status).toBeDefined();
17
+ expect(status?.isModified()).toBe(false);
18
+ expect(status?.isEncodable()).toBe(true);
19
+ }
20
+ }
21
+ });
22
+
23
+ test('find lines by position', async () => {
24
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
25
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
26
+
27
+ const lines = await pdf.selectTextLines();
28
+ expect(lines.length).toBe(36);
29
+
30
+ const first = lines[0];
31
+ expect(first.internalId).toBe('TEXTLINE_000001');
32
+ expect(first.position).toBeDefined();
33
+ expect(Math.abs(first.position.getX()! - 180)).toBeLessThanOrEqual(1);
34
+ expect(Math.abs(first.position.getY()! - 750)).toBeLessThanOrEqual(1);
35
+ expect(first.objectRef().status?.isModified()).toBe(false);
36
+ expect(first.objectRef().status?.isEncodable()).toBe(true);
37
+
38
+ const last = lines[lines.length - 1];
39
+ expect(last.internalId).toBe('TEXTLINE_000036');
40
+ expect(Math.abs(last.position.getX()! - 69.3)).toBeLessThanOrEqual(2);
41
+ expect(Math.abs(last.position.getY()! - 45)).toBeLessThanOrEqual(2);
42
+ expect(last.objectRef().status?.isModified()).toBe(false);
43
+ expect(last.objectRef().status?.isEncodable()).toBe(true);
44
+ });
45
+
46
+ test('find lines by text', async () => {
47
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
48
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
49
+
50
+ const lines = await pdf.page(0).selectTextLinesStartingWith(SAMPLE_PARAGRAPH);
51
+ expect(lines.length).toBe(1);
52
+
53
+ const line = lines[0];
54
+ expect(line.internalId).toBe('TEXTLINE_000002');
55
+ expect(Math.abs(line.position.getX()! - 65)).toBeLessThanOrEqual(1);
56
+ expect(Math.abs(line.position.getY()! - 706.8)).toBeLessThanOrEqual(2);
57
+ });
58
+
59
+ test('delete line', async () => {
60
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
61
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
62
+
63
+ const [line] = await pdf.page(0).selectTextLinesStartingWith(SAMPLE_PARAGRAPH);
64
+ await line.delete();
65
+
66
+ const remaining = await pdf.page(0).selectTextLinesStartingWith(SAMPLE_PARAGRAPH);
67
+ expect(remaining.length).toBe(0);
68
+
69
+ const assertions = await PDFAssertions.create(pdf);
70
+ await assertions.assertTextlineDoesNotExist(SAMPLE_PARAGRAPH);
71
+ });
72
+
73
+ test('move line', async () => {
74
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
75
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
76
+
77
+ const [line] = await pdf.page(0).selectTextLinesStartingWith(SAMPLE_PARAGRAPH);
78
+ const pos = line.position;
79
+ const newX = pos.getX()! + 100;
80
+ const newY = pos.getY()! + 18;
81
+
82
+ await line.moveTo(newX, newY);
83
+
84
+ const moved = (await pdf.page(0).selectTextLinesAt(newX, newY, 1))[0];
85
+ const status = moved.objectRef().status;
86
+ expect(status).toBeDefined();
87
+ expect(status?.isEncodable()).toBe(true);
88
+ expect(status?.getFontType()).toBe(FontType.EMBEDDED);
89
+ expect(status?.isModified()).toBe(false);
90
+
91
+ const assertions = await PDFAssertions.create(pdf);
92
+ await assertions.assertTextlineIsAt(SAMPLE_PARAGRAPH, newX, newY);
93
+ });
94
+
95
+ test('modify line', async () => {
96
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('Showcase.pdf');
97
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
98
+
99
+ const [line] = await pdf.page(0).selectTextLinesStartingWith(SAMPLE_PARAGRAPH);
100
+ await line.edit().text(' replaced ').apply();
101
+
102
+ expect(await pdf.page(0).selectTextLinesStartingWith(SAMPLE_PARAGRAPH)).toHaveLength(0);
103
+ expect(await pdf.page(0).selectParagraphsStartingWith(' replaced ')).not.toHaveLength(0);
104
+
105
+ const lines = await pdf.page(0).selectTextLinesStartingWith(' replaced ');
106
+ expect(lines.length).toBeGreaterThan(0);
107
+ const status = lines[0].objectRef().status;
108
+ expect(status).toBeDefined();
109
+ expect(status?.isEncodable()).toBe(true);
110
+ expect(status?.getFontType()).toBe(FontType.EMBEDDED);
111
+ expect(status?.isModified()).toBe(true);
112
+
113
+ const assertions = await PDFAssertions.create(pdf);
114
+ await assertions.assertTextlineDoesNotExist(SAMPLE_PARAGRAPH);
115
+ await assertions.assertTextlineExists(' replaced ');
116
+ await assertions.assertParagraphExists(' replaced ');
117
+ });
118
+ });
@@ -94,16 +94,8 @@ describe('Text Line E2E Tests (v2 API)', () => {
94
94
  let newY = line.position!.getY()!;
95
95
  await line.moveTo(newX, newY);
96
96
 
97
- const movedPara = await pdf.page(0).selectParagraphsAt(newX, newY);
97
+ const movedPara = await pdf.page(0).selectTextLinesAt(newX, newY);
98
98
  expect(movedPara.length).toBeGreaterThan(0);
99
-
100
- const outPath = createTempPath('moveLine.pdf');
101
- await pdf.save(outPath);
102
- expect(fs.existsSync(outPath)).toBe(true);
103
- expect(fs.statSync(outPath).size).toBeGreaterThan(0);
104
-
105
- fs.unlinkSync(outPath);
106
-
107
99
  const assertions = await PDFAssertions.create(pdf);
108
100
  await assertions.assertTextlineIsAt('The Complete', newX, newY, 0, 0.25);
109
101
  });
@@ -119,11 +111,6 @@ describe('Text Line E2E Tests (v2 API)', () => {
119
111
  expect(result.warning).toBeDefined();
120
112
  expect(result.warning).toContain('You are using an embedded font and modified the text.');
121
113
 
122
- const outPath = createTempPath('modifyLine.pdf');
123
- await pdf.save(outPath);
124
- expect(fs.existsSync(outPath)).toBe(true);
125
- expect(fs.statSync(outPath).size).toBeGreaterThan(0);
126
-
127
114
  const stillOld = await pdf.page(0).selectParagraphsStartingWith('The Complete');
128
115
  expect(stillOld).toHaveLength(0);
129
116
 
@@ -140,8 +127,6 @@ describe('Text Line E2E Tests (v2 API)', () => {
140
127
  const containingParas = await pdf.page(0).selectParagraphsStartingWith(' replaced ');
141
128
  expect(containingParas.length).toBeGreaterThan(0);
142
129
 
143
- fs.unlinkSync(outPath);
144
-
145
130
  const assertions = await PDFAssertions.create(pdf);
146
131
  await assertions.assertTextlineExists(' replaced ');
147
132
  });