pdfdancer-client-typescript 1.0.1

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 (52) hide show
  1. package/.eslintrc.js +19 -0
  2. package/README.md +267 -0
  3. package/dist/__tests__/e2e/test-helpers.d.ts +32 -0
  4. package/dist/__tests__/e2e/test-helpers.d.ts.map +1 -0
  5. package/dist/__tests__/e2e/test-helpers.js +157 -0
  6. package/dist/__tests__/e2e/test-helpers.js.map +1 -0
  7. package/dist/client-v1.d.ts +169 -0
  8. package/dist/client-v1.d.ts.map +1 -0
  9. package/dist/client-v1.js +558 -0
  10. package/dist/client-v1.js.map +1 -0
  11. package/dist/exceptions.d.ts +43 -0
  12. package/dist/exceptions.d.ts.map +1 -0
  13. package/dist/exceptions.js +66 -0
  14. package/dist/exceptions.js.map +1 -0
  15. package/dist/index.d.ts +13 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +33 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/models.d.ts +216 -0
  20. package/dist/models.d.ts.map +1 -0
  21. package/dist/models.js +427 -0
  22. package/dist/models.js.map +1 -0
  23. package/dist/paragraph-builder.d.ts +67 -0
  24. package/dist/paragraph-builder.d.ts.map +1 -0
  25. package/dist/paragraph-builder.js +160 -0
  26. package/dist/paragraph-builder.js.map +1 -0
  27. package/example.ts +103 -0
  28. package/fixtures/DancingScript-Regular.ttf +0 -0
  29. package/fixtures/JetBrainsMono-Regular.ttf +0 -0
  30. package/fixtures/ObviouslyAwesome.pdf +0 -0
  31. package/fixtures/README.md +25 -0
  32. package/fixtures/basic-paths.pdf +0 -0
  33. package/fixtures/logo-80.png +0 -0
  34. package/fixtures/mixed-form-types.pdf +0 -0
  35. package/jest.config.js +26 -0
  36. package/package.json +38 -0
  37. package/scripts/release.js +91 -0
  38. package/scripts/test-release.js +59 -0
  39. package/src/__tests__/client-v1.test.ts +111 -0
  40. package/src/__tests__/e2e/form.test.ts +51 -0
  41. package/src/__tests__/e2e/image.test.ts +108 -0
  42. package/src/__tests__/e2e/line.test.ts +134 -0
  43. package/src/__tests__/e2e/page.test.ts +45 -0
  44. package/src/__tests__/e2e/paragraph.test.ts +210 -0
  45. package/src/__tests__/e2e/path.test.ts +72 -0
  46. package/src/__tests__/e2e/test-helpers.ts +132 -0
  47. package/src/client-v1.ts +673 -0
  48. package/src/exceptions.ts +67 -0
  49. package/src/index.ts +34 -0
  50. package/src/models.ts +476 -0
  51. package/src/paragraph-builder.ts +184 -0
  52. package/tsconfig.json +20 -0
package/example.ts ADDED
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Example usage of the PDFDancer TypeScript client
3
+ */
4
+
5
+ import {
6
+ ClientV1,
7
+ Position,
8
+ Color,
9
+ Font,
10
+ ValidationException,
11
+ HttpClientException
12
+ } from './src/index';
13
+
14
+ async function exampleUsage() {
15
+ try {
16
+ // Create a sample PDF data (in a real application, this would be loaded from a file)
17
+ const pdfData = new Uint8Array([
18
+ 0x25, 0x50, 0x44, 0x46, // %PDF header
19
+ // ... rest of PDF data would go here
20
+ ]);
21
+
22
+ // Initialize the client with authentication token and PDF data
23
+ const client = new ClientV1('your-auth-token', pdfData, 'http://localhost:8080');
24
+
25
+ // Initialize the session (must be called before using the client)
26
+ await client.init();
27
+
28
+ // Example 1: Find all paragraphs on page 1
29
+ const page1Position = Position.fromPageIndex(1);
30
+ const paragraphs = await client.findParagraphs(page1Position);
31
+ console.log(`Found ${paragraphs.length} paragraphs on page 1`);
32
+
33
+ // Example 2: Add a new paragraph using the builder pattern
34
+ const newParagraph = client.paragraphBuilder()
35
+ .fromString('Hello, PDFDancer!', new Color(255, 0, 0)) // Red text
36
+ .withFont(new Font('Arial', 12))
37
+ .withPosition(Position.onPageCoordinates(1, 100, 200))
38
+ .withLineSpacing(1.5)
39
+ .build();
40
+
41
+ await client.addParagraph(newParagraph);
42
+ console.log('Added new paragraph successfully');
43
+
44
+ // Example 3: Find and modify existing text
45
+ const textLines = await client.findTextLines(page1Position);
46
+ if (textLines.length > 0) {
47
+ await client.modifyTextLine(textLines[0], 'Modified text content');
48
+ console.log('Modified first text line');
49
+ }
50
+
51
+ // Example 4: Get the modified PDF
52
+ const modifiedPdf = await client.getPdfFile();
53
+ console.log(`Generated PDF size: ${modifiedPdf.length} bytes`);
54
+
55
+ // Example 5: Save PDF (in browser environment)
56
+ // await client.savePdf('modified-document.pdf');
57
+
58
+ } catch (error) {
59
+ if (error instanceof ValidationException) {
60
+ console.error('Validation error:', error.message);
61
+ } else if (error instanceof HttpClientException) {
62
+ console.error('HTTP error:', error.message, 'Status:', error.statusCode);
63
+ } else {
64
+ console.error('Unexpected error:', error);
65
+ }
66
+ }
67
+ }
68
+
69
+ // Example of using with custom font
70
+ async function exampleWithCustomFont() {
71
+ try {
72
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
73
+ const client = new ClientV1('your-auth-token', pdfData);
74
+ await client.init();
75
+
76
+ // Register a custom font (example with dummy TTF data)
77
+ const customFontData = new Uint8Array([/* TTF font data here */]);
78
+ const fontName = await client.registerFont(customFontData);
79
+ console.log(`Registered font: ${fontName}`);
80
+
81
+ // Use the custom font in a paragraph
82
+ const paragraph = client.paragraphBuilder()
83
+ .fromString('Text with custom font')
84
+ .withFont(new Font(fontName, 14))
85
+ .withPosition(Position.onPageCoordinates(1, 50, 300))
86
+ .build();
87
+
88
+ await client.addParagraph(paragraph);
89
+ console.log('Added paragraph with custom font');
90
+
91
+ } catch (error) {
92
+ console.error('Error with custom font:', error);
93
+ }
94
+ }
95
+
96
+ // Note: These examples show the API usage patterns.
97
+ // In a real application, you would need:
98
+ // 1. A valid authentication token
99
+ // 2. Actual PDF data
100
+ // 3. A running PDFDancer server at the specified URL
101
+ // 4. Proper error handling for your use case
102
+
103
+ export { exampleUsage, exampleWithCustomFont };
Binary file
@@ -0,0 +1,25 @@
1
+ # Test Fixtures
2
+
3
+ This directory contains test fixtures used by the e2e tests.
4
+
5
+ For the e2e tests to run, you need to place the following files here:
6
+
7
+ - `ObviouslyAwesome.pdf` - Main test PDF document
8
+ - `mixed-form-types.pdf` - PDF with form fields for form tests
9
+ - `basic-paths.pdf` - PDF with vector paths for path tests
10
+ - `logo-80.png` - Small PNG image for image tests
11
+ - `DancingScript-Regular.ttf` - TTF font file for custom font tests
12
+
13
+ These files should match the fixtures used by the Python client tests.
14
+
15
+ The e2e tests will be skipped if these files are not present or if the required environment variables are not set:
16
+
17
+ - `PDFDANCER_TOKEN` - Authentication token for the PDFDancer API
18
+ - `PDFDANCER_BASE_URL` - Optional: URL of the PDFDancer server (defaults to http://localhost:8080)
19
+
20
+ ## Running E2E Tests
21
+
22
+ 1. Start the PDFDancer server
23
+ 2. Set the required environment variables
24
+ 3. Place the fixture files in this directory
25
+ 4. Run: `npm test -- --testPathPattern=e2e`
Binary file
Binary file
Binary file
package/jest.config.js ADDED
@@ -0,0 +1,26 @@
1
+ module.exports = {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'node',
4
+ roots: ['<rootDir>/src'],
5
+ testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
6
+ collectCoverageFrom: [
7
+ 'src/**/*.ts',
8
+ '!src/**/*.d.ts',
9
+ '!src/**/*test-helpers.ts',
10
+ ],
11
+ projects: [
12
+ {
13
+ preset: 'ts-jest',
14
+ testEnvironment: 'node',
15
+ displayName: 'unit',
16
+ testMatch: ['<rootDir>/src/**/*.test.ts'],
17
+ testPathIgnorePatterns: ['<rootDir>/src/__tests__/e2e/'],
18
+ },
19
+ {
20
+ preset: 'ts-jest',
21
+ testEnvironment: 'node',
22
+ displayName: 'e2e',
23
+ testMatch: ['<rootDir>/src/__tests__/e2e/**/*.test.ts'],
24
+ }
25
+ ]
26
+ };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "pdfdancer-client-typescript",
3
+ "version": "1.0.1",
4
+ "description": "A TypeScript client library for the PDFDancer PDF manipulation API",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "test": "jest",
10
+ "test:unit": "jest --selectProjects unit",
11
+ "test:e2e": "jest --selectProjects e2e",
12
+ "test:watch": "jest --watch",
13
+ "lint": "eslint src/**/*.ts",
14
+ "prepublishOnly": "npm run build",
15
+ "release": "node scripts/release.js",
16
+ "release:skip-tests": "node scripts/release.js --skip-tests",
17
+ "release:test": "node scripts/test-release.js"
18
+ },
19
+ "keywords": [
20
+ "pdf",
21
+ "manipulation",
22
+ "typescript",
23
+ "client"
24
+ ],
25
+ "author": "",
26
+ "license": "MIT",
27
+ "devDependencies": {
28
+ "@types/jest": "^29.5.0",
29
+ "@types/node": "^20.0.0",
30
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
31
+ "@typescript-eslint/parser": "^6.0.0",
32
+ "eslint": "^8.0.0",
33
+ "jest": "^29.5.0",
34
+ "ts-jest": "^29.1.0",
35
+ "typescript": "^5.0.0"
36
+ },
37
+ "dependencies": {}
38
+ }
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { execSync } = require('child_process');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const packageJsonPath = path.join(__dirname, '..', 'package.json');
8
+
9
+ function run(command, description) {
10
+ console.log(`\n🔧 ${description}...`);
11
+ try {
12
+ execSync(command, { stdio: 'inherit' });
13
+ console.log(`✅ ${description} completed`);
14
+ } catch (error) {
15
+ console.error(`❌ ${description} failed`);
16
+ process.exit(1);
17
+ }
18
+ }
19
+
20
+ function getCurrentVersion() {
21
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
22
+ return packageJson.version;
23
+ }
24
+
25
+ function bumpVersion() {
26
+ console.log('\n📦 Bumping version...');
27
+ const currentVersion = getCurrentVersion();
28
+ console.log(`Current version: ${currentVersion}`);
29
+
30
+ execSync('npm version patch', { stdio: 'inherit' });
31
+
32
+ const newVersion = getCurrentVersion();
33
+ console.log(`New version: ${newVersion}`);
34
+ return newVersion;
35
+ }
36
+
37
+ function main() {
38
+ const args = process.argv.slice(2);
39
+ const skipTests = args.includes('--skip-tests');
40
+
41
+ console.log('🚀 Starting release process...\n');
42
+
43
+ // Ensure we're on a clean working directory
44
+ try {
45
+ execSync('git diff-index --quiet HEAD --', { stdio: 'pipe' });
46
+ } catch (error) {
47
+ console.error('❌ Working directory is not clean. Please commit or stash your changes.');
48
+ process.exit(1);
49
+ }
50
+
51
+ // Run tests (unless skipped)
52
+ if (!skipTests) {
53
+ run('npm run test:unit', 'Running unit tests');
54
+ } else {
55
+ console.log('⚠️ Skipping tests as requested');
56
+ }
57
+
58
+ // Run linting
59
+ run('npm run lint', 'Running linter');
60
+
61
+ // Build the project
62
+ run('npm run build', 'Building project');
63
+
64
+ // Get current version for confirmation
65
+ const currentVersion = getCurrentVersion();
66
+ console.log(`\n📋 About to release version ${currentVersion}`);
67
+ console.log('This will:');
68
+ console.log(' 1. Publish to npm');
69
+ console.log(' 2. Bump the version number');
70
+ console.log(' 3. Create a git tag');
71
+ console.log(' 4. Push changes to git');
72
+
73
+ // Publish to npm
74
+ run('npm publish', 'Publishing to npm');
75
+
76
+ // Bump version and create git tag
77
+ const newVersion = bumpVersion();
78
+
79
+ // Push changes and tags to git
80
+ run('git push', 'Pushing changes to git');
81
+ run('git push --tags', 'Pushing tags to git');
82
+
83
+ console.log(`\n🎉 Release ${newVersion} completed successfully!`);
84
+ console.log(`Package is now available at: https://www.npmjs.com/package/pdfdancer-client-typescript`);
85
+ }
86
+
87
+ if (require.main === module) {
88
+ main();
89
+ }
90
+
91
+ module.exports = { main };
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { execSync } = require('child_process');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const packageJsonPath = path.join(__dirname, '..', 'package.json');
8
+
9
+ function run(command, description) {
10
+ console.log(`\n🔧 ${description}...`);
11
+ try {
12
+ execSync(command, { stdio: 'inherit' });
13
+ console.log(`✅ ${description} completed`);
14
+ } catch (error) {
15
+ console.error(`❌ ${description} failed`);
16
+ process.exit(1);
17
+ }
18
+ }
19
+
20
+ function getCurrentVersion() {
21
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
22
+ return packageJson.version;
23
+ }
24
+
25
+ function main() {
26
+ console.log('🧪 Testing release process (dry run)...\n');
27
+
28
+ // Check working directory status
29
+ try {
30
+ execSync('git diff-index --quiet HEAD --', { stdio: 'pipe' });
31
+ console.log('✅ Working directory is clean');
32
+ } catch (error) {
33
+ console.log('⚠️ Working directory has changes (this would block real release)');
34
+ }
35
+
36
+ // Run unit tests only (e2e tests have external dependencies)
37
+ run('npm run test:unit', 'Running unit tests');
38
+
39
+ // Run linting
40
+ run('npm run lint', 'Running linter');
41
+
42
+ // Build the project
43
+ run('npm run build', 'Building project');
44
+
45
+ // Simulate version check
46
+ const currentVersion = getCurrentVersion();
47
+ console.log(`\n📋 Current version: ${currentVersion}`);
48
+ console.log('✅ Release script validation complete');
49
+
50
+ console.log('\n🎯 To run actual release:');
51
+ console.log(' npm run release');
52
+ console.log('\nNote: Make sure working directory is clean before running actual release');
53
+ }
54
+
55
+ if (require.main === module) {
56
+ main();
57
+ }
58
+
59
+ module.exports = { main };
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Basic tests for the PDFDancer TypeScript client.
3
+ */
4
+
5
+ import { ClientV1 } from '../client-v1';
6
+ import { ValidationException } from '../exceptions';
7
+ import { Position, Color, Font, Paragraph } from '../models';
8
+
9
+ describe('ClientV1', () => {
10
+ const mockToken = 'test-token';
11
+ const mockPdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]); // %PDF header
12
+
13
+ describe('constructor validation', () => {
14
+ test('should throw ValidationException for empty token', () => {
15
+ expect(() => {
16
+ new ClientV1('', mockPdfData);
17
+ }).toThrow(ValidationException);
18
+ });
19
+
20
+ test('should throw ValidationException for null PDF data', () => {
21
+ expect(() => {
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
+ new ClientV1(mockToken, (null as any));
24
+ }).toThrow(ValidationException);
25
+ });
26
+
27
+ test('should throw ValidationException for empty PDF data', () => {
28
+ expect(() => {
29
+ new ClientV1(mockToken, new Uint8Array(0));
30
+ }).toThrow(ValidationException);
31
+ });
32
+
33
+ test('should accept valid parameters', () => {
34
+ expect(() => {
35
+ new ClientV1(mockToken, mockPdfData);
36
+ }).not.toThrow();
37
+ });
38
+ });
39
+
40
+ describe('paragraphBuilder', () => {
41
+ test('should create a ParagraphBuilder instance', () => {
42
+ const client = new ClientV1(mockToken, mockPdfData);
43
+ const builder = client.paragraphBuilder();
44
+ expect(builder).toBeDefined();
45
+ });
46
+ });
47
+ });
48
+
49
+ describe('Position', () => {
50
+ test('should create position from page index', () => {
51
+ const position = Position.fromPageIndex(1);
52
+ expect(position.pageIndex).toBe(1);
53
+ });
54
+
55
+ test('should create position with coordinates', () => {
56
+ const position = Position.onPageCoordinates(1, 100, 200);
57
+ expect(position.pageIndex).toBe(1);
58
+ expect(position.getX()).toBe(100);
59
+ expect(position.getY()).toBe(200);
60
+ });
61
+
62
+ test('should move position', () => {
63
+ const position = Position.onPageCoordinates(1, 100, 200);
64
+ position.moveX(50).moveY(30);
65
+ expect(position.getX()).toBe(150);
66
+ expect(position.getY()).toBe(230);
67
+ });
68
+ });
69
+
70
+ describe('Color', () => {
71
+ test('should create valid color', () => {
72
+ const color = new Color(255, 128, 0, 200);
73
+ expect(color.r).toBe(255);
74
+ expect(color.g).toBe(128);
75
+ expect(color.b).toBe(0);
76
+ expect(color.a).toBe(200);
77
+ });
78
+
79
+ test('should throw error for invalid color values', () => {
80
+ expect(() => new Color(256, 0, 0)).toThrow();
81
+ expect(() => new Color(0, -1, 0)).toThrow();
82
+ });
83
+ });
84
+
85
+ describe('Font', () => {
86
+ test('should create valid font', () => {
87
+ const font = new Font('Arial', 12);
88
+ expect(font.name).toBe('Arial');
89
+ expect(font.size).toBe(12);
90
+ });
91
+
92
+ test('should throw error for invalid font size', () => {
93
+ expect(() => new Font('Arial', 0)).toThrow();
94
+ expect(() => new Font('Arial', -5)).toThrow();
95
+ });
96
+ });
97
+
98
+ describe('Paragraph', () => {
99
+ test('should create paragraph with position', () => {
100
+ const position = Position.fromPageIndex(1);
101
+ const paragraph = new Paragraph(position);
102
+ expect(paragraph.getPosition()).toBe(position);
103
+ });
104
+
105
+ test('should set position', () => {
106
+ const paragraph = new Paragraph();
107
+ const position = Position.fromPageIndex(2);
108
+ paragraph.setPosition(position);
109
+ expect(paragraph.getPosition()).toBe(position);
110
+ });
111
+ });
@@ -0,0 +1,51 @@
1
+ /**
2
+ * E2E tests for form operations
3
+ */
4
+
5
+ import * as fs from 'fs';
6
+ import { ClientV1, Position, ObjectType } from '../../index';
7
+ import { requireEnvAndFixture, createTempPath } from './test-helpers';
8
+
9
+ describe('Form E2E Tests', () => {
10
+ // Tests should fail properly if environment is not configured
11
+
12
+ test('delete forms', async () => {
13
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('mixed-form-types.pdf');
14
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
15
+ await client.init();
16
+
17
+ const forms = await client.findForms();
18
+ expect(forms).toHaveLength(79);
19
+ expect(forms[0].type).toBe(ObjectType.FORM);
20
+
21
+ // Delete all forms
22
+ for (const f of forms) {
23
+ expect(await client.delete(f)).toBe(true);
24
+ }
25
+
26
+ expect(await client.findForms()).toHaveLength(0);
27
+
28
+ // Save PDF to verify operation
29
+ const outPath = createTempPath('forms-after-delete.pdf');
30
+ const outputPdfData = await client.getPdfFile();
31
+ fs.writeFileSync(outPath, outputPdfData);
32
+ expect(fs.existsSync(outPath)).toBe(true);
33
+ expect(fs.statSync(outPath).size).toBeGreaterThan(0);
34
+
35
+ // Cleanup
36
+ fs.unlinkSync(outPath);
37
+ });
38
+
39
+ test('find form by position', async () => {
40
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('mixed-form-types.pdf');
41
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
42
+ await client.init();
43
+
44
+ let forms = await client.findForms(Position.onPageCoordinates(0, 0, 0));
45
+ expect(forms).toHaveLength(0);
46
+
47
+ forms = await client.findForms(Position.onPageCoordinates(0, 17, 447));
48
+ expect(forms).toHaveLength(1);
49
+ expect(forms[0].internalId).toBe('FORM_000001');
50
+ });
51
+ });
@@ -0,0 +1,108 @@
1
+ /**
2
+ * E2E tests for image operations
3
+ */
4
+
5
+ import * as fs from 'fs';
6
+ import { ClientV1, Position, ObjectType, Image } from '../../index';
7
+ import { requireEnvAndFixture, createTempPath, readImageFixture } from './test-helpers';
8
+
9
+ describe('Image E2E Tests', () => {
10
+ // Tests should fail properly if environment is not configured
11
+
12
+ test('find images', async () => {
13
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
14
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
15
+ await client.init();
16
+
17
+ const images = await client.findImages();
18
+ expect(images).toHaveLength(3);
19
+ expect(images[0].type).toBe(ObjectType.IMAGE);
20
+
21
+ const imagesPage0 = await client.findImages(Position.fromPageIndex(0));
22
+ expect(imagesPage0).toHaveLength(2);
23
+ });
24
+
25
+ test('delete images', async () => {
26
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
27
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
28
+ await client.init();
29
+
30
+ const images = await client.findImages();
31
+ for (const obj of images) {
32
+ expect(await client.delete(obj)).toBe(true);
33
+ }
34
+ expect(await client.findImages()).toHaveLength(0);
35
+
36
+ // Save PDF to verify operation
37
+ const outPath = createTempPath('deleteImage.pdf');
38
+ const outputPdfData = await client.getPdfFile();
39
+ fs.writeFileSync(outPath, outputPdfData);
40
+ expect(fs.existsSync(outPath)).toBe(true);
41
+ expect(fs.statSync(outPath).size).toBeGreaterThan(0);
42
+
43
+ // Cleanup
44
+ fs.unlinkSync(outPath);
45
+ });
46
+
47
+ test('move image', async () => {
48
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
49
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
50
+ await client.init();
51
+
52
+ let images = await client.findImages();
53
+ const imageRef = images[2];
54
+ const position = imageRef.position;
55
+ expect(position.boundingRect?.x).toBeCloseTo(54, 0.5);
56
+ expect(position.boundingRect?.y).toBeCloseTo(300, 1);
57
+
58
+ expect(await client.move(imageRef, Position.onPageCoordinates(11, 50.1, 100.0))).toBe(true);
59
+
60
+ images = await client.findImages();
61
+ const movedImageRef = images[2];
62
+ const newPosition = movedImageRef.position;
63
+ expect(newPosition.boundingRect?.x).toBeCloseTo(50.1, 0.05);
64
+ expect(newPosition.boundingRect?.y).toBeCloseTo(100.0, 0.05);
65
+ });
66
+
67
+ test('find image by position', async () => {
68
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
69
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
70
+ await client.init();
71
+
72
+ let images = await client.findImages(Position.onPageCoordinates(11, 0, 0));
73
+ expect(images).toHaveLength(0);
74
+
75
+ images = await client.findImages(Position.onPageCoordinates(11, 55, 310));
76
+ expect(images).toHaveLength(1);
77
+ expect(images[0].internalId).toBe('IMAGE_000003');
78
+ });
79
+
80
+ test('add image', async () => {
81
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
82
+ const client = new ClientV1(token, pdfData, baseUrl, 30000);
83
+ await client.init();
84
+
85
+ let images = await client.findImages();
86
+ expect(images).toHaveLength(3);
87
+
88
+ // Prepare image data
89
+ const imageData = readImageFixture('logo-80.png');
90
+ const image = new Image();
91
+ image.data = imageData;
92
+ const pos = Position.onPageCoordinates(6, 50.1, 98.0);
93
+
94
+ expect(await client.addImage(image, pos)).toBe(true);
95
+
96
+ images = await client.findImages();
97
+ expect(images).toHaveLength(4);
98
+
99
+ const imagesPage6 = await client.findImages(Position.fromPageIndex(6));
100
+ expect(imagesPage6).toHaveLength(1);
101
+
102
+ const newImage = imagesPage6[0];
103
+ expect(newImage.position.pageIndex).toBe(6);
104
+ expect(newImage.internalId).toBe('IMAGE_000003');
105
+ expect(newImage.position.boundingRect?.x).toBeCloseTo(50.1, 0.05);
106
+ expect(newImage.position.boundingRect?.y).toBeCloseTo(98.0, 0.05);
107
+ });
108
+ });