pdfdancer-client-typescript 1.0.13 → 1.0.15

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 (45) hide show
  1. package/.eslintrc.js +26 -18
  2. package/.github/workflows/ci.yml +51 -2
  3. package/.github/workflows/daily-tests.yml +54 -0
  4. package/README.md +50 -6
  5. package/dist/__tests__/e2e/test-helpers.d.ts.map +1 -1
  6. package/dist/__tests__/e2e/test-helpers.js +17 -5
  7. package/dist/__tests__/e2e/test-helpers.js.map +1 -1
  8. package/dist/fingerprint.d.ts.map +1 -1
  9. package/dist/fingerprint.js +16 -5
  10. package/dist/fingerprint.js.map +1 -1
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +4 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/models.d.ts +18 -15
  16. package/dist/models.d.ts.map +1 -1
  17. package/dist/models.js +21 -18
  18. package/dist/models.js.map +1 -1
  19. package/dist/pdfdancer_v1.d.ts +71 -3
  20. package/dist/pdfdancer_v1.d.ts.map +1 -1
  21. package/dist/pdfdancer_v1.js +301 -35
  22. package/dist/pdfdancer_v1.js.map +1 -1
  23. package/docs/openapi.yml +637 -73
  24. package/jest.config.js +1 -1
  25. package/package.json +2 -2
  26. package/src/__tests__/e2e/acroform.test.ts +58 -0
  27. package/src/__tests__/e2e/form_x_object.test.ts +29 -0
  28. package/src/__tests__/e2e/image-showcase.test.ts +6 -6
  29. package/src/__tests__/e2e/image.test.ts +39 -5
  30. package/src/__tests__/e2e/line-showcase.test.ts +6 -11
  31. package/src/__tests__/e2e/line.test.ts +63 -7
  32. package/src/__tests__/e2e/page-showcase.test.ts +12 -12
  33. package/src/__tests__/e2e/page.test.ts +3 -3
  34. package/src/__tests__/e2e/paragraph-showcase.test.ts +0 -8
  35. package/src/__tests__/e2e/paragraph.test.ts +64 -8
  36. package/src/__tests__/e2e/path.test.ts +33 -4
  37. package/src/__tests__/e2e/snapshot-showcase.test.ts +10 -10
  38. package/src/__tests__/e2e/snapshot.test.ts +18 -18
  39. package/src/__tests__/e2e/test-helpers.ts +16 -5
  40. package/src/__tests__/e2e/token_from_env.test.ts +0 -15
  41. package/src/__tests__/retry-mechanism.test.ts +420 -0
  42. package/src/fingerprint.ts +20 -7
  43. package/src/index.ts +3 -1
  44. package/src/models.ts +21 -17
  45. package/src/pdfdancer_v1.ts +467 -71
@@ -0,0 +1,420 @@
1
+ /**
2
+ * Tests for the retry mechanism in REST API calls.
3
+ */
4
+
5
+ import {PDFDancer} from '../pdfdancer_v1';
6
+ import {RetryConfig} from '../pdfdancer_v1';
7
+
8
+ // Mock the fetch function
9
+ global.fetch = jest.fn();
10
+
11
+ // Helper to create a mock response
12
+ function createMockResponse(status: number, body: unknown = {}): Response {
13
+ const bodyString = typeof body === 'string' ? body : JSON.stringify(body);
14
+ return {
15
+ ok: status >= 200 && status < 300,
16
+ status,
17
+ statusText: status === 200 ? 'OK' : 'Error',
18
+ headers: new Headers(),
19
+ text: async () => bodyString,
20
+ json: async () => typeof body === 'object' ? body : JSON.parse(body as string),
21
+ arrayBuffer: async () => new ArrayBuffer(0),
22
+ blob: async () => new Blob(),
23
+ formData: async () => new FormData(),
24
+ clone: function() {
25
+ return createMockResponse(status, body);
26
+ },
27
+ body: null,
28
+ bodyUsed: false,
29
+ redirected: false,
30
+ type: 'basic',
31
+ url: ''
32
+ } as Response;
33
+ }
34
+
35
+ describe('Retry Mechanism', () => {
36
+ beforeEach(() => {
37
+ jest.clearAllMocks();
38
+ (global.fetch as jest.Mock).mockReset();
39
+ });
40
+
41
+ describe('RetryConfig', () => {
42
+ test('should use default retry config when none provided', async () => {
43
+ // Mock successful responses
44
+ (global.fetch as jest.Mock)
45
+ .mockResolvedValueOnce(createMockResponse(200, {token: 'test-token'}))
46
+ .mockResolvedValueOnce(createMockResponse(200, 'session-123'));
47
+
48
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]); // PDF header
49
+
50
+ // Should use default retry config
51
+ await expect(PDFDancer.open(pdfData, 'test-token')).resolves.toBeDefined();
52
+ });
53
+
54
+ test('should accept custom retry config', async () => {
55
+ const customRetryConfig: RetryConfig = {
56
+ maxRetries: 5,
57
+ initialDelay: 500,
58
+ maxDelay: 5000,
59
+ retryableStatusCodes: [429, 503],
60
+ retryOnNetworkError: true,
61
+ backoffMultiplier: 3,
62
+ useJitter: false
63
+ };
64
+
65
+ (global.fetch as jest.Mock)
66
+ .mockResolvedValueOnce(createMockResponse(200, {token: 'test-token'}))
67
+ .mockResolvedValueOnce(createMockResponse(200, 'session-123'));
68
+
69
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
70
+
71
+ await expect(
72
+ PDFDancer.open(pdfData, 'test-token', undefined, undefined, customRetryConfig)
73
+ ).resolves.toBeDefined();
74
+ });
75
+ });
76
+
77
+ describe('Retryable Status Codes', () => {
78
+ test('should retry on 429 (rate limit)', async () => {
79
+ const mockFetch = global.fetch as jest.Mock;
80
+
81
+ // First call returns 429, second call succeeds
82
+ // When token is provided, no token fetch call is made
83
+ mockFetch
84
+ .mockResolvedValueOnce(createMockResponse(429, 'Rate limit exceeded'))
85
+ .mockResolvedValueOnce(createMockResponse(200, 'session-123'));
86
+
87
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
88
+ const retryConfig: RetryConfig = {
89
+ maxRetries: 2,
90
+ initialDelay: 10, // Use short delay for tests
91
+ useJitter: false
92
+ };
93
+
94
+ await expect(
95
+ PDFDancer.open(pdfData, 'test-token', undefined, undefined, retryConfig)
96
+ ).resolves.toBeDefined();
97
+
98
+ // Should have made 2 fetch calls: 1 failed session + 1 retry
99
+ expect(mockFetch).toHaveBeenCalledTimes(2);
100
+ });
101
+
102
+ test('should retry on 500 (server error)', async () => {
103
+ const mockFetch = global.fetch as jest.Mock;
104
+
105
+ mockFetch
106
+ .mockResolvedValueOnce(createMockResponse(500, 'Internal server error'))
107
+ .mockResolvedValueOnce(createMockResponse(200, 'session-123'));
108
+
109
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
110
+ const retryConfig: RetryConfig = {
111
+ maxRetries: 2,
112
+ initialDelay: 10,
113
+ useJitter: false
114
+ };
115
+
116
+ await expect(
117
+ PDFDancer.open(pdfData, 'test-token', undefined, undefined, retryConfig)
118
+ ).resolves.toBeDefined();
119
+
120
+ expect(mockFetch).toHaveBeenCalledTimes(2);
121
+ });
122
+
123
+ test('should retry on 502, 503, 504', async () => {
124
+ for (const statusCode of [502, 503, 504]) {
125
+ jest.clearAllMocks();
126
+ const mockFetch = global.fetch as jest.Mock;
127
+
128
+ mockFetch
129
+ .mockResolvedValueOnce(createMockResponse(statusCode, 'Service unavailable'))
130
+ .mockResolvedValueOnce(createMockResponse(200, 'session-123'));
131
+
132
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
133
+ const retryConfig: RetryConfig = {
134
+ maxRetries: 2,
135
+ initialDelay: 10,
136
+ useJitter: false
137
+ };
138
+
139
+ await expect(
140
+ PDFDancer.open(pdfData, 'test-token', undefined, undefined, retryConfig)
141
+ ).resolves.toBeDefined();
142
+
143
+ expect(mockFetch).toHaveBeenCalledTimes(2);
144
+ }
145
+ });
146
+
147
+ test('should NOT retry on 400 (bad request)', async () => {
148
+ const mockFetch = global.fetch as jest.Mock;
149
+
150
+ mockFetch
151
+ .mockResolvedValueOnce(createMockResponse(400, 'Bad request'));
152
+
153
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
154
+ const retryConfig: RetryConfig = {
155
+ maxRetries: 3,
156
+ initialDelay: 10,
157
+ useJitter: false
158
+ };
159
+
160
+ await expect(
161
+ PDFDancer.open(pdfData, 'test-token', undefined, undefined, retryConfig)
162
+ ).rejects.toThrow();
163
+
164
+ // Should only make 1 call (no retries for 400)
165
+ expect(mockFetch).toHaveBeenCalledTimes(1);
166
+ });
167
+
168
+ test('should NOT retry on 404 (not found)', async () => {
169
+ const mockFetch = global.fetch as jest.Mock;
170
+
171
+ mockFetch
172
+ .mockResolvedValueOnce(createMockResponse(404, 'Not found'));
173
+
174
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
175
+ const retryConfig: RetryConfig = {
176
+ maxRetries: 3,
177
+ initialDelay: 10,
178
+ useJitter: false
179
+ };
180
+
181
+ await expect(
182
+ PDFDancer.open(pdfData, 'test-token', undefined, undefined, retryConfig)
183
+ ).rejects.toThrow();
184
+
185
+ expect(mockFetch).toHaveBeenCalledTimes(1);
186
+ });
187
+ });
188
+
189
+ describe('Network Errors', () => {
190
+ test('should retry on network errors when retryOnNetworkError is true', async () => {
191
+ const mockFetch = global.fetch as jest.Mock;
192
+
193
+ mockFetch
194
+ .mockRejectedValueOnce(new Error('Network error'))
195
+ .mockResolvedValueOnce(createMockResponse(200, 'session-123'));
196
+
197
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
198
+ const retryConfig: RetryConfig = {
199
+ maxRetries: 2,
200
+ initialDelay: 10,
201
+ retryOnNetworkError: true,
202
+ useJitter: false
203
+ };
204
+
205
+ await expect(
206
+ PDFDancer.open(pdfData, 'test-token', undefined, undefined, retryConfig)
207
+ ).resolves.toBeDefined();
208
+
209
+ expect(mockFetch).toHaveBeenCalledTimes(2);
210
+ });
211
+
212
+ test('should NOT retry on network errors when retryOnNetworkError is false', async () => {
213
+ const mockFetch = global.fetch as jest.Mock;
214
+
215
+ mockFetch
216
+ .mockRejectedValueOnce(new Error('Network error'));
217
+
218
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
219
+ const retryConfig: RetryConfig = {
220
+ maxRetries: 3,
221
+ initialDelay: 10,
222
+ retryOnNetworkError: false,
223
+ useJitter: false
224
+ };
225
+
226
+ await expect(
227
+ PDFDancer.open(pdfData, 'test-token', undefined, undefined, retryConfig)
228
+ ).rejects.toThrow('Network error');
229
+
230
+ expect(mockFetch).toHaveBeenCalledTimes(1);
231
+ });
232
+ });
233
+
234
+ describe('Max Retries', () => {
235
+ test('should respect maxRetries limit', async () => {
236
+ const mockFetch = global.fetch as jest.Mock;
237
+
238
+ // All calls fail with 503
239
+ mockFetch
240
+ .mockResolvedValue(createMockResponse(503, 'Service unavailable'));
241
+
242
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
243
+ const retryConfig: RetryConfig = {
244
+ maxRetries: 3,
245
+ initialDelay: 10,
246
+ useJitter: false
247
+ };
248
+
249
+ await expect(
250
+ PDFDancer.open(pdfData, 'test-token', undefined, undefined, retryConfig)
251
+ ).rejects.toThrow();
252
+
253
+ // Should make: 1 initial session call + 3 retries = 4 total
254
+ expect(mockFetch).toHaveBeenCalledTimes(4);
255
+ });
256
+
257
+ test('should not retry when maxRetries is 0', async () => {
258
+ const mockFetch = global.fetch as jest.Mock;
259
+
260
+ mockFetch
261
+ .mockResolvedValueOnce(createMockResponse(503, 'Service unavailable'));
262
+
263
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
264
+ const retryConfig: RetryConfig = {
265
+ maxRetries: 0,
266
+ initialDelay: 10,
267
+ useJitter: false
268
+ };
269
+
270
+ await expect(
271
+ PDFDancer.open(pdfData, 'test-token', undefined, undefined, retryConfig)
272
+ ).rejects.toThrow();
273
+
274
+ // Should only make 1 call (no retries)
275
+ expect(mockFetch).toHaveBeenCalledTimes(1);
276
+ });
277
+ });
278
+
279
+ describe('Exponential Backoff', () => {
280
+ test('should apply exponential backoff between retries', async () => {
281
+ const mockFetch = global.fetch as jest.Mock;
282
+ const delays: number[] = [];
283
+ const originalSetTimeout = global.setTimeout;
284
+
285
+ // Mock setTimeout to capture delays
286
+ global.setTimeout = jest.fn((callback: () => void, delay?: number) => {
287
+ if (delay) delays.push(delay);
288
+ return originalSetTimeout(callback, 0);
289
+ }) as unknown as typeof setTimeout;
290
+
291
+ mockFetch
292
+ .mockResolvedValueOnce(createMockResponse(503, 'Unavailable'))
293
+ .mockResolvedValueOnce(createMockResponse(503, 'Unavailable'))
294
+ .mockResolvedValueOnce(createMockResponse(200, 'session-123'));
295
+
296
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
297
+ const retryConfig: RetryConfig = {
298
+ maxRetries: 3,
299
+ initialDelay: 100,
300
+ backoffMultiplier: 2,
301
+ useJitter: false
302
+ };
303
+
304
+ await PDFDancer.open(pdfData, 'test-token', undefined, undefined, retryConfig);
305
+
306
+ // Restore original setTimeout
307
+ global.setTimeout = originalSetTimeout;
308
+
309
+ // Should have 2 delays (for 2 retries that eventually succeeded)
310
+ expect(delays.length).toBeGreaterThanOrEqual(2);
311
+
312
+ // First retry delay should be around initialDelay (100ms)
313
+ expect(delays[0]).toBeGreaterThanOrEqual(100);
314
+ expect(delays[0]).toBeLessThan(110);
315
+
316
+ // Second retry delay should be around initialDelay * backoffMultiplier (200ms)
317
+ expect(delays[1]).toBeGreaterThanOrEqual(200);
318
+ expect(delays[1]).toBeLessThan(210);
319
+ });
320
+
321
+ test('should cap delay at maxDelay', async () => {
322
+ const mockFetch = global.fetch as jest.Mock;
323
+ const delays: number[] = [];
324
+ const originalSetTimeout = global.setTimeout;
325
+
326
+ global.setTimeout = jest.fn((callback: () => void, delay?: number) => {
327
+ if (delay) delays.push(delay);
328
+ return originalSetTimeout(callback, 0);
329
+ }) as unknown as typeof setTimeout;
330
+
331
+ mockFetch
332
+ .mockResolvedValueOnce(createMockResponse(503, 'Unavailable'))
333
+ .mockResolvedValueOnce(createMockResponse(503, 'Unavailable'))
334
+ .mockResolvedValueOnce(createMockResponse(503, 'Unavailable'))
335
+ .mockResolvedValueOnce(createMockResponse(200, 'session-123'));
336
+
337
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
338
+ const retryConfig: RetryConfig = {
339
+ maxRetries: 4,
340
+ initialDelay: 1000,
341
+ maxDelay: 2000,
342
+ backoffMultiplier: 2,
343
+ useJitter: false
344
+ };
345
+
346
+ await PDFDancer.open(pdfData, 'test-token', undefined, undefined, retryConfig);
347
+
348
+ global.setTimeout = originalSetTimeout;
349
+
350
+ // All delays should be capped at maxDelay (2000ms)
351
+ delays.forEach(delay => {
352
+ expect(delay).toBeLessThanOrEqual(2000);
353
+ });
354
+ });
355
+ });
356
+
357
+ describe('Jitter', () => {
358
+ test('should apply jitter when useJitter is true', async () => {
359
+ const mockFetch = global.fetch as jest.Mock;
360
+ const delays: number[] = [];
361
+ const originalSetTimeout = global.setTimeout;
362
+
363
+ global.setTimeout = jest.fn((callback: () => void, delay?: number) => {
364
+ if (delay) delays.push(delay);
365
+ return originalSetTimeout(callback, 0);
366
+ }) as unknown as typeof setTimeout;
367
+
368
+ mockFetch
369
+ .mockResolvedValueOnce(createMockResponse(503, 'Unavailable'))
370
+ .mockResolvedValueOnce(createMockResponse(200, 'session-123'));
371
+
372
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
373
+ const retryConfig: RetryConfig = {
374
+ maxRetries: 2,
375
+ initialDelay: 1000,
376
+ backoffMultiplier: 2,
377
+ useJitter: true
378
+ };
379
+
380
+ await PDFDancer.open(pdfData, 'test-token', undefined, undefined, retryConfig);
381
+
382
+ global.setTimeout = originalSetTimeout;
383
+
384
+ // With jitter, delay should be between 50% and 100% of calculated delay
385
+ // For first retry: should be between 500 (50% of 1000) and 1000
386
+ expect(delays[0]).toBeGreaterThanOrEqual(500);
387
+ expect(delays[0]).toBeLessThanOrEqual(1000);
388
+ });
389
+
390
+ test('should not apply jitter when useJitter is false', async () => {
391
+ const mockFetch = global.fetch as jest.Mock;
392
+ const delays: number[] = [];
393
+ const originalSetTimeout = global.setTimeout;
394
+
395
+ global.setTimeout = jest.fn((callback: () => void, delay?: number) => {
396
+ if (delay) delays.push(delay);
397
+ return originalSetTimeout(callback, 0);
398
+ }) as unknown as typeof setTimeout;
399
+
400
+ mockFetch
401
+ .mockResolvedValueOnce(createMockResponse(503, 'Unavailable'))
402
+ .mockResolvedValueOnce(createMockResponse(200, 'session-123'));
403
+
404
+ const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
405
+ const retryConfig: RetryConfig = {
406
+ maxRetries: 2,
407
+ initialDelay: 1000,
408
+ backoffMultiplier: 2,
409
+ useJitter: false
410
+ };
411
+
412
+ await PDFDancer.open(pdfData, 'test-token', undefined, undefined, retryConfig);
413
+
414
+ global.setTimeout = originalSetTimeout;
415
+
416
+ // Without jitter, delay should be exactly the calculated value
417
+ expect(delays[0]).toBe(1000);
418
+ });
419
+ });
420
+ });
@@ -32,7 +32,7 @@ function getInstallSalt(): string {
32
32
 
33
33
  // Create directory if it doesn't exist
34
34
  if (!fs.existsSync(saltDir)) {
35
- fs.mkdirSync(saltDir, { recursive: true, mode: 0o700 });
35
+ fs.mkdirSync(saltDir, {recursive: true, mode: 0o700});
36
36
  }
37
37
 
38
38
  // Read existing salt or generate new one
@@ -40,7 +40,7 @@ function getInstallSalt(): string {
40
40
  return fs.readFileSync(saltFile, 'utf8').trim();
41
41
  } else {
42
42
  const salt = crypto.randomBytes(16).toString('hex');
43
- fs.writeFileSync(saltFile, salt, { mode: 0o600 });
43
+ fs.writeFileSync(saltFile, salt, {mode: 0o600});
44
44
  return salt;
45
45
  }
46
46
  } catch (error) {
@@ -54,13 +54,26 @@ function getInstallSalt(): string {
54
54
  * Note: This is limited on the client side and may not always be accurate
55
55
  */
56
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 {
57
+ // In the browser, just return a placeholder
58
+ if (typeof window !== 'undefined') {
62
59
  return 'client-unknown';
63
60
  }
61
+
62
+ // --- Running on server side ---
63
+ // Try to find a non-internal IPv4 address from network interfaces
64
+ const interfaces = os.networkInterfaces();
65
+ for (const name of Object.keys(interfaces)) {
66
+ const netList = interfaces[name];
67
+ if (!netList) continue;
68
+ for (const net of netList) {
69
+ if (net.family === 'IPv4' && !net.internal) {
70
+ return net.address;
71
+ }
72
+ }
73
+ }
74
+
75
+ // Fallback if nothing found
76
+ return 'server-unknown';
64
77
  }
65
78
 
66
79
  /**
package/src/index.ts CHANGED
@@ -38,10 +38,12 @@ export {
38
38
  Orientation,
39
39
  CommandResult,
40
40
  TextStatus,
41
- FontRecommendation,
41
+ DocumentFontInfo,
42
42
  FontType,
43
43
  DocumentSnapshot,
44
44
  PageSnapshot
45
45
  } from './models';
46
46
 
47
+ export { DocumentFontInfo as FontRecommendation } from './models';
48
+
47
49
  export const VERSION = "1.0.0";
package/src/models.ts CHANGED
@@ -255,28 +255,25 @@ export enum FontType {
255
255
  }
256
256
 
257
257
  /**
258
- * Represents a font recommendation with similarity score.
258
+ * Represents font mapping information for a document font.
259
259
  */
260
- export class FontRecommendation {
260
+ export class DocumentFontInfo {
261
261
  constructor(
262
- public fontName: string,
263
- public fontType: FontType,
264
- public similarityScore: number
262
+ public documentFontName: string,
263
+ public systemFontName: string
265
264
  ) {}
266
265
 
267
- getFontName(): string {
268
- return this.fontName;
269
- }
270
-
271
- getFontType(): FontType {
272
- return this.fontType;
266
+ getDocumentFontName(): string {
267
+ return this.documentFontName;
273
268
  }
274
269
 
275
- getSimilarityScore(): number {
276
- return this.similarityScore;
270
+ getSystemFontName(): string {
271
+ return this.systemFontName;
277
272
  }
278
273
  }
279
274
 
275
+ export { DocumentFontInfo as FontRecommendation };
276
+
280
277
  /**
281
278
  * Status information for text objects.
282
279
  */
@@ -285,7 +282,7 @@ export class TextStatus {
285
282
  public modified: boolean,
286
283
  public encodable: boolean,
287
284
  public fontType: FontType,
288
- public fontRecommendation: FontRecommendation
285
+ public fontInfo?: DocumentFontInfo
289
286
  ) {}
290
287
 
291
288
  isModified(): boolean {
@@ -300,8 +297,15 @@ export class TextStatus {
300
297
  return this.fontType;
301
298
  }
302
299
 
303
- getFontRecommendation(): FontRecommendation {
304
- return this.fontRecommendation;
300
+ getFontInfo(): DocumentFontInfo | undefined {
301
+ return this.fontInfo;
302
+ }
303
+
304
+ /**
305
+ * @deprecated Use getFontInfo() instead.
306
+ */
307
+ getFontRecommendation(): DocumentFontInfo | undefined {
308
+ return this.getFontInfo();
305
309
  }
306
310
  }
307
311
 
@@ -986,7 +990,7 @@ export class PageSnapshot {
986
990
  export class DocumentSnapshot {
987
991
  constructor(
988
992
  public pageCount: number,
989
- public fonts: FontRecommendation[],
993
+ public fonts: DocumentFontInfo[],
990
994
  public pages: PageSnapshot[]
991
995
  ) {}
992
996