fontastic 1.1.0 → 1.3.0

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 (87) hide show
  1. package/.github/workflows/claude-code-review.yml +0 -6
  2. package/.github/workflows/release-please.yml +25 -0
  3. package/.github/workflows/release.yml +5 -109
  4. package/.release-please-manifest.json +3 -0
  5. package/CHANGELOG.md +39 -0
  6. package/README.md +6 -0
  7. package/app/Application.js +4 -4
  8. package/app/Application.ts +13 -13
  9. package/app/config/database.js +1 -1
  10. package/app/config/database.ts +1 -1
  11. package/app/config/mimes.js +23 -23
  12. package/app/config/mimes.ts +35 -29
  13. package/app/core/ConfigManager.js +27 -0
  14. package/app/core/ConfigManager.ts +28 -0
  15. package/app/core/FontFinder.js +66 -15
  16. package/app/core/FontFinder.ts +81 -22
  17. package/app/core/FontManager.js +20 -18
  18. package/app/core/FontManager.ts +21 -19
  19. package/app/core/FontObject.js +44 -24
  20. package/app/core/FontObject.ts +47 -27
  21. package/app/core/MessageHandler.js +70 -19
  22. package/app/core/MessageHandler.ts +82 -21
  23. package/app/core/SystemManager.js +5 -1
  24. package/app/core/SystemManager.ts +5 -1
  25. package/app/database/entity/Collection.schema.js +20 -18
  26. package/app/database/entity/Collection.schema.ts +22 -21
  27. package/app/database/repository/Collection.repository.js +17 -18
  28. package/app/database/repository/Collection.repository.ts +27 -18
  29. package/app/database/repository/Store.repository.js +13 -18
  30. package/app/database/repository/Store.repository.ts +13 -21
  31. package/app/enums/ChannelType.js +18 -0
  32. package/app/enums/ChannelType.ts +24 -0
  33. package/app/main.js +98 -10
  34. package/app/main.ts +126 -19
  35. package/app/package.json +1 -1
  36. package/app/types/NativeThemeState.js +3 -0
  37. package/app/types/NativeThemeState.ts +4 -0
  38. package/app/types/ScanProgress.js +3 -0
  39. package/app/types/ScanProgress.ts +6 -0
  40. package/app/types/SystemPreferencesState.js +3 -0
  41. package/app/types/SystemPreferencesState.ts +4 -0
  42. package/app/types/index.js +3 -0
  43. package/app/types/index.ts +3 -0
  44. package/package.json +2 -2
  45. package/release-please-config.json +20 -0
  46. package/scripts/patch-electron-plist.js +41 -0
  47. package/src/app/core/services/database/database.service.ts +6 -0
  48. package/src/app/core/services/message/message.service.ts +33 -1
  49. package/src/app/core/services/presentation/presentation.service.ts +100 -1
  50. package/src/app/layout/footer/footer.component.html +13 -2
  51. package/src/app/layout/footer/footer.component.ts +18 -2
  52. package/src/app/layout/header/header.component.html +0 -10
  53. package/src/app/layout/header/header.component.ts +4 -23
  54. package/src/app/layout/navigation/navigation.component.html +66 -16
  55. package/src/app/layout/navigation/navigation.component.ts +65 -12
  56. package/src/app/settings/ai-keys/ai-keys.component.ts +13 -18
  57. package/src/app/settings/danger-zone/danger-zone.component.html +8 -0
  58. package/src/app/settings/danger-zone/danger-zone.component.ts +12 -0
  59. package/src/app/settings/news-api/news-api.component.ts +6 -8
  60. package/src/app/settings/theme/theme.component.html +15 -2
  61. package/src/app/settings/theme/theme.component.ts +4 -0
  62. package/src/app/shared/components/datagrid/datagrid.component.html +8 -17
  63. package/src/app/shared/components/datagrid/datagrid.component.ts +6 -10
  64. package/src/app/shared/components/glyphs/glyphs.component.html +5 -15
  65. package/src/app/shared/components/glyphs/glyphs.component.ts +3 -0
  66. package/src/app/shared/components/preview/preview.component.html +1 -1
  67. package/src/app/shared/components/preview/preview.component.ts +3 -8
  68. package/src/app/shared/components/prompt-dialog/prompt-dialog.component.html +2 -2
  69. package/src/app/shared/components/prompt-dialog/prompt-dialog.component.ts +2 -1
  70. package/src/app/shared/components/rule-builder/rule-builder.component.html +18 -6
  71. package/src/app/shared/components/rule-builder/rule-builder.component.ts +34 -2
  72. package/src/app/shared/components/search/search.component.html +9 -36
  73. package/src/app/shared/components/search/search.component.ts +2 -1
  74. package/src/app/shared/components/waterfall/waterfall.component.html +1 -3
  75. package/src/app/shared/components/waterfall/waterfall.component.ts +2 -1
  76. package/src/app/shared/directives/disabled-opacity/disabled-opacity.directive.ts +18 -0
  77. package/src/app/shared/directives/hover-highlight/hover-highlight.directive.ts +38 -0
  78. package/src/app/shared/directives/index.ts +5 -0
  79. package/src/app/shared/directives/modal-backdrop/modal-backdrop.directive.ts +18 -0
  80. package/src/app/shared/directives/scroll-reset/scroll-reset.directive.ts +15 -0
  81. package/src/app/shared/directives/stop-propagation/stop-propagation.directive.ts +12 -0
  82. package/src/assets/icons/favicon.256x256.png +0 -0
  83. package/src/assets/icons/favicon.512x512.png +0 -0
  84. package/src/assets/icons/favicon.icns +0 -0
  85. package/src/assets/icons/favicon.ico +0 -0
  86. package/src/assets/icons/favicon.png +0 -0
  87. package/src/favicon.ico +0 -0
@@ -13,58 +13,109 @@ const FontObject_1 = require("./FontObject");
13
13
  const fs = require("fs/promises");
14
14
  const path = require("path");
15
15
  const mimes_1 = require("../config/mimes");
16
- const prettyBytes = require("pretty-bytes");
17
- const mime = require("mime");
16
+ const prettyBytes = require('pretty-bytes');
17
+ const mime = require('mime');
18
+ const SCAN_CONCURRENCY = 10;
18
19
  class FontFinder {
19
- constructor(connectionManager) {
20
+ constructor(connectionManager, onProgress) {
20
21
  this.errors = [];
21
22
  this.counter = 0;
23
+ this.onProgress = null;
22
24
  this.connectionManager = connectionManager;
25
+ this.onProgress = onProgress !== null && onProgress !== void 0 ? onProgress : null;
23
26
  }
24
- isFontFile(filePath) {
27
+ getFontMimeType(filePath) {
25
28
  const fileType = mime.getType(filePath);
26
- return fileType && mimes_1.mimeTypes.includes(fileType);
29
+ return fileType && mimes_1.mimeTypes.includes(fileType) ? fileType : null;
27
30
  }
28
31
  scanFiles(files, options) {
29
32
  return __awaiter(this, void 0, void 0, function* () {
33
+ this.errors = [];
34
+ this.counter = 0;
35
+ const fontFiles = [];
30
36
  for (const fp of files) {
31
- if (this.isFontFile(fp)) {
32
- const stat = yield fs.stat(fp);
33
- yield this.processFont(fp, stat, options);
37
+ const fileType = this.getFontMimeType(fp);
38
+ if (fileType) {
39
+ fontFiles.push({ fp, fileType });
34
40
  }
35
41
  }
42
+ yield this.processInBatches(fontFiles, options);
36
43
  });
37
44
  }
38
45
  scanFolder(dir, options) {
39
46
  return __awaiter(this, void 0, void 0, function* () {
47
+ this.errors = [];
48
+ this.counter = 0;
49
+ const fontFiles = yield this.collectFontFiles(dir);
50
+ yield this.processInBatches(fontFiles, options);
51
+ });
52
+ }
53
+ collectFontFiles(dir) {
54
+ return __awaiter(this, void 0, void 0, function* () {
55
+ const results = [];
40
56
  const entries = yield fs.readdir(dir, { withFileTypes: true });
57
+ const subdirPromises = [];
41
58
  for (const entry of entries) {
42
59
  const fp = path.join(dir, entry.name);
43
60
  if (entry.isDirectory()) {
44
- yield this.scanFolder(fp, options);
61
+ subdirPromises.push(this.collectFontFiles(fp));
62
+ }
63
+ else {
64
+ const fileType = this.getFontMimeType(fp);
65
+ if (fileType) {
66
+ results.push({ fp, fileType });
67
+ }
45
68
  }
46
- else if (this.isFontFile(fp)) {
47
- const stat = yield fs.stat(fp);
48
- yield this.processFont(fp, stat, options);
69
+ }
70
+ if (subdirPromises.length > 0) {
71
+ const subdirResults = yield Promise.all(subdirPromises);
72
+ for (const subResults of subdirResults) {
73
+ results.push(...subResults);
49
74
  }
50
75
  }
76
+ return results;
51
77
  });
52
78
  }
53
- processFont(fp, stat, options) {
79
+ processInBatches(fontFiles, options) {
80
+ return __awaiter(this, void 0, void 0, function* () {
81
+ const total = fontFiles.length;
82
+ for (let i = 0; i < fontFiles.length; i += SCAN_CONCURRENCY) {
83
+ const batch = fontFiles.slice(i, i + SCAN_CONCURRENCY);
84
+ yield Promise.all(batch.map(({ fp, fileType }) => this.processFont(fp, fileType, options)));
85
+ if (this.onProgress) {
86
+ const lastFile = batch[batch.length - 1];
87
+ this.onProgress({
88
+ processed: Math.min(i + SCAN_CONCURRENCY, total),
89
+ total,
90
+ currentFile: path.basename(lastFile.fp),
91
+ errors: this.errors.length,
92
+ });
93
+ }
94
+ }
95
+ });
96
+ }
97
+ processFont(fp, fileType, options) {
54
98
  return __awaiter(this, void 0, void 0, function* () {
55
99
  const font = new FontObject_1.default(fp);
56
100
  if (font.hasError()) {
57
101
  this.errors.push(font.getError());
58
102
  return;
59
103
  }
60
- const fileType = mime.getType(fp);
104
+ let stat;
105
+ try {
106
+ stat = yield fs.stat(fp);
107
+ }
108
+ catch (err) {
109
+ this.errors.push({ file: fp, message: err.message });
110
+ return;
111
+ }
61
112
  const data = Object.assign(Object.assign({ file_path: fp, file_name: path.basename(fp), file_size: stat.size, file_size_pretty: prettyBytes(stat.size), file_type: fileType, installable: mimes_1.installable.includes(fileType) }, options), font.getNamesTable());
62
113
  try {
63
114
  yield this.connectionManager.getStoreRepository().create(data);
64
115
  this.counter++;
65
116
  }
66
117
  catch (err) {
67
- this.errors.push(err.message);
118
+ this.errors.push({ file: fp, message: err.message });
68
119
  }
69
120
  });
70
121
  }
@@ -1,50 +1,103 @@
1
- import ConnectionManager from "./ConnectionManager";
2
- import FontObject from "./FontObject";
3
- import * as fs from "fs/promises";
4
- import * as path from "path";
5
- import { mimeTypes, installable } from "../config/mimes";
1
+ import ConnectionManager from './ConnectionManager';
2
+ import FontObject from './FontObject';
3
+ import * as fs from 'fs/promises';
4
+ import * as path from 'path';
5
+ import { mimeTypes, installable } from '../config/mimes';
6
+ import type { ScanProgress } from '../types';
6
7
 
7
- const prettyBytes = require("pretty-bytes");
8
- const mime = require("mime");
8
+ const prettyBytes = require('pretty-bytes');
9
+ const mime = require('mime');
9
10
 
10
- export default class FontFinder {
11
+ const SCAN_CONCURRENCY = 10;
12
+
13
+ export type ProgressCallback = (progress: ScanProgress) => void;
11
14
 
15
+ export default class FontFinder {
12
16
  connectionManager: ConnectionManager;
13
17
  errors: any[] = [];
14
18
  counter: number = 0;
19
+ private onProgress: ProgressCallback | null = null;
15
20
 
16
- constructor(connectionManager: ConnectionManager) {
21
+ constructor(connectionManager: ConnectionManager, onProgress?: ProgressCallback) {
17
22
  this.connectionManager = connectionManager;
23
+ this.onProgress = onProgress ?? null;
18
24
  }
19
25
 
20
- private isFontFile(filePath: string): boolean {
26
+ private getFontMimeType(filePath: string): string | null {
21
27
  const fileType = mime.getType(filePath);
22
- return fileType && mimeTypes.includes(fileType);
28
+ return fileType && mimeTypes.includes(fileType) ? fileType : null;
23
29
  }
24
30
 
25
31
  async scanFiles(files: string[], options: any) {
32
+ this.errors = [];
33
+ this.counter = 0;
34
+
35
+ const fontFiles: { fp: string; fileType: string }[] = [];
26
36
  for (const fp of files) {
27
- if (this.isFontFile(fp)) {
28
- const stat = await fs.stat(fp);
29
- await this.processFont(fp, stat, options);
37
+ const fileType = this.getFontMimeType(fp);
38
+ if (fileType) {
39
+ fontFiles.push({ fp, fileType });
30
40
  }
31
41
  }
42
+
43
+ await this.processInBatches(fontFiles, options);
32
44
  }
33
45
 
34
46
  async scanFolder(dir: string, options: any) {
47
+ this.errors = [];
48
+ this.counter = 0;
49
+
50
+ const fontFiles = await this.collectFontFiles(dir);
51
+ await this.processInBatches(fontFiles, options);
52
+ }
53
+
54
+ private async collectFontFiles(dir: string): Promise<{ fp: string; fileType: string }[]> {
55
+ const results: { fp: string; fileType: string }[] = [];
35
56
  const entries = await fs.readdir(dir, { withFileTypes: true });
57
+
58
+ const subdirPromises: Promise<{ fp: string; fileType: string }[]>[] = [];
59
+
36
60
  for (const entry of entries) {
37
61
  const fp = path.join(dir, entry.name);
38
62
  if (entry.isDirectory()) {
39
- await this.scanFolder(fp, options);
40
- } else if (this.isFontFile(fp)) {
41
- const stat = await fs.stat(fp);
42
- await this.processFont(fp, stat, options);
63
+ subdirPromises.push(this.collectFontFiles(fp));
64
+ } else {
65
+ const fileType = this.getFontMimeType(fp);
66
+ if (fileType) {
67
+ results.push({ fp, fileType });
68
+ }
69
+ }
70
+ }
71
+
72
+ if (subdirPromises.length > 0) {
73
+ const subdirResults = await Promise.all(subdirPromises);
74
+ for (const subResults of subdirResults) {
75
+ results.push(...subResults);
43
76
  }
44
77
  }
78
+
79
+ return results;
45
80
  }
46
81
 
47
- private async processFont(fp: string, stat: any, options: any) {
82
+ private async processInBatches(fontFiles: { fp: string; fileType: string }[], options: any) {
83
+ const total = fontFiles.length;
84
+ for (let i = 0; i < fontFiles.length; i += SCAN_CONCURRENCY) {
85
+ const batch = fontFiles.slice(i, i + SCAN_CONCURRENCY);
86
+ await Promise.all(batch.map(({ fp, fileType }) => this.processFont(fp, fileType, options)));
87
+
88
+ if (this.onProgress) {
89
+ const lastFile = batch[batch.length - 1];
90
+ this.onProgress({
91
+ processed: Math.min(i + SCAN_CONCURRENCY, total),
92
+ total,
93
+ currentFile: path.basename(lastFile.fp),
94
+ errors: this.errors.length,
95
+ });
96
+ }
97
+ }
98
+ }
99
+
100
+ private async processFont(fp: string, fileType: string, options: any) {
48
101
  const font = new FontObject(fp);
49
102
 
50
103
  if (font.hasError()) {
@@ -52,7 +105,13 @@ export default class FontFinder {
52
105
  return;
53
106
  }
54
107
 
55
- const fileType = mime.getType(fp);
108
+ let stat;
109
+ try {
110
+ stat = await fs.stat(fp);
111
+ } catch (err: any) {
112
+ this.errors.push({ file: fp, message: err.message });
113
+ return;
114
+ }
56
115
 
57
116
  const data = {
58
117
  file_path: fp,
@@ -68,8 +127,8 @@ export default class FontFinder {
68
127
  try {
69
128
  await this.connectionManager.getStoreRepository().create(data);
70
129
  this.counter++;
71
- } catch (err) {
72
- this.errors.push(err.message);
130
+ } catch (err: any) {
131
+ this.errors.push({ file: fp, message: err.message });
73
132
  }
74
133
  }
75
134
  }
@@ -18,13 +18,25 @@ const path = require("path");
18
18
  const fetch = require('node-fetch');
19
19
  class FontManager {
20
20
  constructor(systemManager, configManager, connectionManager) {
21
+ this.catalog = new FontCatalog_1.default();
21
22
  this.systemManager = systemManager;
22
23
  this.configManager = configManager;
23
24
  this.connectionManager = connectionManager;
24
25
  }
25
26
  fetchLatestNews(args) {
26
27
  return __awaiter(this, void 0, void 0, function* () {
27
- const response = yield fetch(args.endpoint);
28
+ // Retrieve API key securely from main process storage
29
+ let apiKey = args.apiKey;
30
+ if (!apiKey) {
31
+ const secureKey = this.configManager.getSecure('secure.news.apiKey');
32
+ if (secureKey) {
33
+ apiKey = secureKey;
34
+ }
35
+ }
36
+ if (!apiKey)
37
+ return { articles: [] };
38
+ const endpoint = `https://newsapi.org/v2/top-headlines?country=${args.country || 'us'}&apiKey=${apiKey}`;
39
+ const response = yield fetch(endpoint);
28
40
  const data = yield response.json();
29
41
  if (data === null || data === void 0 ? void 0 : data.articles) {
30
42
  this.configManager.set(StorageType_1.StorageType.News, Object.assign(Object.assign({}, this.configManager.get(StorageType_1.StorageType.News)), { articles: data.articles, ts: Date.now() }));
@@ -55,37 +67,27 @@ class FontManager {
55
67
  copyFiles(files, collectionId) {
56
68
  return __awaiter(this, void 0, void 0, function* () {
57
69
  const dest = this.getDestinationFolder(collectionId);
58
- console.log('[FontManager.copyFiles] dest:', dest, 'files:', files);
59
- const catalog = new FontCatalog_1.default();
60
- yield catalog.copyFiles(files, dest);
61
- const catalogFiles = files.map((file) => path.join(dest, path.basename(file)));
62
- console.log('[FontManager.copyFiles] catalogFiles:', catalogFiles);
63
- return catalogFiles;
70
+ yield this.catalog.copyFiles(files, dest);
71
+ return files.map((file) => path.join(dest, path.basename(file)));
64
72
  });
65
73
  }
66
74
  copyFolder(src, collectionId) {
67
75
  return __awaiter(this, void 0, void 0, function* () {
68
76
  const dest = this.getDestinationFolder(collectionId);
69
- console.log('[FontManager.copyFolder] src:', src, 'dest:', dest);
70
- const catalog = new FontCatalog_1.default();
71
- yield catalog.copyFolder(src, dest);
77
+ yield this.catalog.copyFolder(src, dest);
72
78
  return dest;
73
79
  });
74
80
  }
75
- scanFiles(files, options) {
81
+ scanFiles(files, options, onProgress) {
76
82
  return __awaiter(this, void 0, void 0, function* () {
77
- console.log('[FontManager.scanFiles] files:', files, 'options:', options);
78
- const finder = new FontFinder_1.default(this.connectionManager);
83
+ const finder = new FontFinder_1.default(this.connectionManager, onProgress);
79
84
  yield finder.scanFiles(files, options);
80
- console.log('[FontManager.scanFiles] done, processed:', finder.counter, 'errors:', finder.errors);
81
85
  });
82
86
  }
83
- scanFolder(dir, options) {
87
+ scanFolder(dir, options, onProgress) {
84
88
  return __awaiter(this, void 0, void 0, function* () {
85
- console.log('[FontManager.scanFolder] dir:', dir, 'options:', options);
86
- const finder = new FontFinder_1.default(this.connectionManager);
89
+ const finder = new FontFinder_1.default(this.connectionManager, onProgress);
87
90
  yield finder.scanFolder(dir, options);
88
- console.log('[FontManager.scanFolder] done, processed:', finder.counter, 'errors:', finder.errors);
89
91
  });
90
92
  }
91
93
  showMessageBox(options) {
@@ -5,7 +5,7 @@ import ConfigManager from './ConfigManager';
5
5
  import ConnectionManager from './ConnectionManager';
6
6
 
7
7
  import FontCatalog from './FontCatalog';
8
- import FontFinder from './FontFinder';
8
+ import FontFinder, { ProgressCallback } from './FontFinder';
9
9
  import { execute } from '../helpers/command';
10
10
 
11
11
  import { StorageType } from '../enums/StorageType';
@@ -18,6 +18,7 @@ export default class FontManager {
18
18
  systemManager: SystemManager;
19
19
  configManager: ConfigManager;
20
20
  connectionManager: ConnectionManager;
21
+ private catalog = new FontCatalog();
21
22
 
22
23
  constructor(systemManager: SystemManager, configManager: ConfigManager, connectionManager: ConnectionManager) {
23
24
  this.systemManager = systemManager;
@@ -26,7 +27,18 @@ export default class FontManager {
26
27
  }
27
28
 
28
29
  async fetchLatestNews(args: any) {
29
- const response = await fetch(args.endpoint);
30
+ // Retrieve API key securely from main process storage
31
+ let apiKey = args.apiKey;
32
+ if (!apiKey) {
33
+ const secureKey = this.configManager.getSecure('secure.news.apiKey');
34
+ if (secureKey) {
35
+ apiKey = secureKey;
36
+ }
37
+ }
38
+ if (!apiKey) return { articles: [] };
39
+
40
+ const endpoint = `https://newsapi.org/v2/top-headlines?country=${args.country || 'us'}&apiKey=${apiKey}`;
41
+ const response = await fetch(endpoint);
30
42
  const data = await response.json();
31
43
  if (data?.articles) {
32
44
  this.configManager.set(StorageType.News, {
@@ -58,34 +70,24 @@ export default class FontManager {
58
70
 
59
71
  async copyFiles(files: string[], collectionId: number): Promise<string[]> {
60
72
  const dest = this.getDestinationFolder(collectionId);
61
- console.log('[FontManager.copyFiles] dest:', dest, 'files:', files);
62
- const catalog = new FontCatalog();
63
- await catalog.copyFiles(files, dest);
64
- const catalogFiles = files.map((file) => path.join(dest, path.basename(file)));
65
- console.log('[FontManager.copyFiles] catalogFiles:', catalogFiles);
66
- return catalogFiles;
73
+ await this.catalog.copyFiles(files, dest);
74
+ return files.map((file) => path.join(dest, path.basename(file)));
67
75
  }
68
76
 
69
77
  async copyFolder(src: string, collectionId: number): Promise<string> {
70
78
  const dest = this.getDestinationFolder(collectionId);
71
- console.log('[FontManager.copyFolder] src:', src, 'dest:', dest);
72
- const catalog = new FontCatalog();
73
- await catalog.copyFolder(src, dest);
79
+ await this.catalog.copyFolder(src, dest);
74
80
  return dest;
75
81
  }
76
82
 
77
- async scanFiles(files: string[], options: any) {
78
- console.log('[FontManager.scanFiles] files:', files, 'options:', options);
79
- const finder = new FontFinder(this.connectionManager);
83
+ async scanFiles(files: string[], options: any, onProgress?: ProgressCallback) {
84
+ const finder = new FontFinder(this.connectionManager, onProgress);
80
85
  await finder.scanFiles(files, options);
81
- console.log('[FontManager.scanFiles] done, processed:', finder.counter, 'errors:', finder.errors);
82
86
  }
83
87
 
84
- async scanFolder(dir: string, options: any) {
85
- console.log('[FontManager.scanFolder] dir:', dir, 'options:', options);
86
- const finder = new FontFinder(this.connectionManager);
88
+ async scanFolder(dir: string, options: any, onProgress?: ProgressCallback) {
89
+ const finder = new FontFinder(this.connectionManager, onProgress);
87
90
  await finder.scanFolder(dir, options);
88
- console.log('[FontManager.scanFolder] done, processed:', finder.counter, 'errors:', finder.errors);
89
91
  }
90
92
 
91
93
  showMessageBox(options: any) {
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const fontkit = require("fontkit");
3
+ const fontkit = require('fontkit');
4
+ const MAX_CACHE_SIZE = 50;
5
+ const fontCache = new Map();
4
6
  class FontObject {
5
7
  constructor(fp) {
6
8
  try {
@@ -10,6 +12,20 @@ class FontObject {
10
12
  this.setError(fp, err.message);
11
13
  }
12
14
  }
15
+ static fromCache(fp) {
16
+ const cached = fontCache.get(fp);
17
+ if (cached)
18
+ return cached;
19
+ const obj = new FontObject(fp);
20
+ if (!obj.hasError()) {
21
+ if (fontCache.size >= MAX_CACHE_SIZE) {
22
+ const firstKey = fontCache.keys().next().value;
23
+ fontCache.delete(firstKey);
24
+ }
25
+ fontCache.set(fp, obj);
26
+ }
27
+ return obj;
28
+ }
13
29
  setFont(font) {
14
30
  this.font = font;
15
31
  }
@@ -23,31 +39,35 @@ class FontObject {
23
39
  return this.error;
24
40
  }
25
41
  hasError() {
26
- return this.error ? true : false;
42
+ return !!this.error;
27
43
  }
28
44
  getNamesTable() {
29
- const names = (this.font && this.font.name && this.font.name.records) ? this.font.name.records : false;
30
- let item = {};
31
- item.compatible_full_name = (names && names.compatibleFullName) ? names.compatibleFullName.en : "";
32
- item.copyright = (names && names.copyright) ? names.copyright.en : "";
33
- item.description = (names && names.description) ? names.description.en : "";
34
- item.designer = (names && names.designer) ? names.designer.en : "";
35
- item.designer_url = (names && names.designerURL) ? names.designerURL.en : "";
36
- item.font_family = (names && names.fontFamily) ? names.fontFamily.en : "";
37
- item.font_subfamily = (names && names.fontSubfamily) ? names.fontSubfamily.en : "";
38
- item.full_name = (names && names.fullName) ? names.fullName.en : "";
39
- item.license = (names && names.license) ? names.license.en : "";
40
- item.license_url = (names && names.licenseURL) ? names.licenseURL.en : "";
41
- item.manufacturer = (names && names.manufacturer) ? names.manufacturer.en : "";
42
- item.manufacturer_url = (names && names.manufacturerURL) ? names.manufacturerURL.en : "";
43
- item.post_script_name = (names && names.postScriptName) ? names.postScriptName.en : "";
44
- item.preferred_family = (names && names.preferredFamily) ? names.preferredFamily.en : "";
45
- item.preferred_sub_family = (names && names.preferredSubfamily) ? names.preferredSubfamily.en : "";
46
- item.sample_text = (names && names.sampleText) ? names.sampleText.en : "";
47
- item.trademark = (names && names.trademark) ? names.trademark.en : "";
48
- item.unique_id = (names && names.uniqueSubfamily) ? names.uniqueSubfamily.en : "";
49
- item.version = (names && names.version) ? names.version.en : "";
50
- return item;
45
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15;
46
+ if (this.namesTable)
47
+ return this.namesTable;
48
+ const names = (_b = (_a = this.font) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.records;
49
+ this.namesTable = {
50
+ compatible_full_name: (_d = (_c = names === null || names === void 0 ? void 0 : names.compatibleFullName) === null || _c === void 0 ? void 0 : _c.en) !== null && _d !== void 0 ? _d : '',
51
+ copyright: (_f = (_e = names === null || names === void 0 ? void 0 : names.copyright) === null || _e === void 0 ? void 0 : _e.en) !== null && _f !== void 0 ? _f : '',
52
+ description: (_h = (_g = names === null || names === void 0 ? void 0 : names.description) === null || _g === void 0 ? void 0 : _g.en) !== null && _h !== void 0 ? _h : '',
53
+ designer: (_k = (_j = names === null || names === void 0 ? void 0 : names.designer) === null || _j === void 0 ? void 0 : _j.en) !== null && _k !== void 0 ? _k : '',
54
+ designer_url: (_m = (_l = names === null || names === void 0 ? void 0 : names.designerURL) === null || _l === void 0 ? void 0 : _l.en) !== null && _m !== void 0 ? _m : '',
55
+ font_family: (_p = (_o = names === null || names === void 0 ? void 0 : names.fontFamily) === null || _o === void 0 ? void 0 : _o.en) !== null && _p !== void 0 ? _p : '',
56
+ font_subfamily: (_r = (_q = names === null || names === void 0 ? void 0 : names.fontSubfamily) === null || _q === void 0 ? void 0 : _q.en) !== null && _r !== void 0 ? _r : '',
57
+ full_name: (_t = (_s = names === null || names === void 0 ? void 0 : names.fullName) === null || _s === void 0 ? void 0 : _s.en) !== null && _t !== void 0 ? _t : '',
58
+ license: (_v = (_u = names === null || names === void 0 ? void 0 : names.license) === null || _u === void 0 ? void 0 : _u.en) !== null && _v !== void 0 ? _v : '',
59
+ license_url: (_x = (_w = names === null || names === void 0 ? void 0 : names.licenseURL) === null || _w === void 0 ? void 0 : _w.en) !== null && _x !== void 0 ? _x : '',
60
+ manufacturer: (_z = (_y = names === null || names === void 0 ? void 0 : names.manufacturer) === null || _y === void 0 ? void 0 : _y.en) !== null && _z !== void 0 ? _z : '',
61
+ manufacturer_url: (_1 = (_0 = names === null || names === void 0 ? void 0 : names.manufacturerURL) === null || _0 === void 0 ? void 0 : _0.en) !== null && _1 !== void 0 ? _1 : '',
62
+ post_script_name: (_3 = (_2 = names === null || names === void 0 ? void 0 : names.postScriptName) === null || _2 === void 0 ? void 0 : _2.en) !== null && _3 !== void 0 ? _3 : '',
63
+ preferred_family: (_5 = (_4 = names === null || names === void 0 ? void 0 : names.preferredFamily) === null || _4 === void 0 ? void 0 : _4.en) !== null && _5 !== void 0 ? _5 : '',
64
+ preferred_sub_family: (_7 = (_6 = names === null || names === void 0 ? void 0 : names.preferredSubfamily) === null || _6 === void 0 ? void 0 : _6.en) !== null && _7 !== void 0 ? _7 : '',
65
+ sample_text: (_9 = (_8 = names === null || names === void 0 ? void 0 : names.sampleText) === null || _8 === void 0 ? void 0 : _8.en) !== null && _9 !== void 0 ? _9 : '',
66
+ trademark: (_11 = (_10 = names === null || names === void 0 ? void 0 : names.trademark) === null || _10 === void 0 ? void 0 : _10.en) !== null && _11 !== void 0 ? _11 : '',
67
+ unique_id: (_13 = (_12 = names === null || names === void 0 ? void 0 : names.uniqueSubfamily) === null || _12 === void 0 ? void 0 : _12.en) !== null && _13 !== void 0 ? _13 : '',
68
+ version: (_15 = (_14 = names === null || names === void 0 ? void 0 : names.version) === null || _14 === void 0 ? void 0 : _14.en) !== null && _15 !== void 0 ? _15 : '',
69
+ };
70
+ return this.namesTable;
51
71
  }
52
72
  }
53
73
  exports.default = FontObject;
@@ -1,9 +1,12 @@
1
- const fontkit = require("fontkit");
1
+ const fontkit = require('fontkit');
2
2
 
3
- export default class FontObject {
3
+ const MAX_CACHE_SIZE = 50;
4
+ const fontCache = new Map<string, FontObject>();
4
5
 
6
+ export default class FontObject {
5
7
  font: any;
6
8
  error: any;
9
+ private namesTable: any;
7
10
 
8
11
  constructor(fp: string) {
9
12
  try {
@@ -13,6 +16,20 @@ export default class FontObject {
13
16
  }
14
17
  }
15
18
 
19
+ static fromCache(fp: string): FontObject {
20
+ const cached = fontCache.get(fp);
21
+ if (cached) return cached;
22
+ const obj = new FontObject(fp);
23
+ if (!obj.hasError()) {
24
+ if (fontCache.size >= MAX_CACHE_SIZE) {
25
+ const firstKey = fontCache.keys().next().value;
26
+ fontCache.delete(firstKey);
27
+ }
28
+ fontCache.set(fp, obj);
29
+ }
30
+ return obj;
31
+ }
32
+
16
33
  setFont(font: any) {
17
34
  this.font = font;
18
35
  }
@@ -30,33 +47,36 @@ export default class FontObject {
30
47
  }
31
48
 
32
49
  hasError() {
33
- return this.error ? true : false;
50
+ return !!this.error;
34
51
  }
35
52
 
36
53
  getNamesTable() {
37
- const names = (this.font && this.font.name && this.font.name.records) ? this.font.name.records : false;
38
-
39
- let item: any = {};
40
- item.compatible_full_name = (names && names.compatibleFullName) ? names.compatibleFullName.en : "";
41
- item.copyright = (names && names.copyright) ? names.copyright.en : "";
42
- item.description = (names && names.description) ? names.description.en : "";
43
- item.designer = (names && names.designer) ? names.designer.en : "";
44
- item.designer_url = (names && names.designerURL) ? names.designerURL.en : "";
45
- item.font_family = (names && names.fontFamily) ? names.fontFamily.en : "";
46
- item.font_subfamily = (names && names.fontSubfamily) ? names.fontSubfamily.en : "";
47
- item.full_name = (names && names.fullName) ? names.fullName.en : "";
48
- item.license = (names && names.license) ? names.license.en : "";
49
- item.license_url = (names && names.licenseURL) ? names.licenseURL.en : "";
50
- item.manufacturer = (names && names.manufacturer) ? names.manufacturer.en : "";
51
- item.manufacturer_url = (names && names.manufacturerURL) ? names.manufacturerURL.en : "";
52
- item.post_script_name = (names && names.postScriptName) ? names.postScriptName.en : "";
53
- item.preferred_family = (names && names.preferredFamily) ? names.preferredFamily.en : "";
54
- item.preferred_sub_family = (names && names.preferredSubfamily) ? names.preferredSubfamily.en : "";
55
- item.sample_text = (names && names.sampleText) ? names.sampleText.en : "";
56
- item.trademark = (names && names.trademark) ? names.trademark.en : "";
57
- item.unique_id = (names && names.uniqueSubfamily) ? names.uniqueSubfamily.en : "";
58
- item.version = (names && names.version) ? names.version.en : "";
59
-
60
- return item;
54
+ if (this.namesTable) return this.namesTable;
55
+
56
+ const names = this.font?.name?.records;
57
+
58
+ this.namesTable = {
59
+ compatible_full_name: names?.compatibleFullName?.en ?? '',
60
+ copyright: names?.copyright?.en ?? '',
61
+ description: names?.description?.en ?? '',
62
+ designer: names?.designer?.en ?? '',
63
+ designer_url: names?.designerURL?.en ?? '',
64
+ font_family: names?.fontFamily?.en ?? '',
65
+ font_subfamily: names?.fontSubfamily?.en ?? '',
66
+ full_name: names?.fullName?.en ?? '',
67
+ license: names?.license?.en ?? '',
68
+ license_url: names?.licenseURL?.en ?? '',
69
+ manufacturer: names?.manufacturer?.en ?? '',
70
+ manufacturer_url: names?.manufacturerURL?.en ?? '',
71
+ post_script_name: names?.postScriptName?.en ?? '',
72
+ preferred_family: names?.preferredFamily?.en ?? '',
73
+ preferred_sub_family: names?.preferredSubfamily?.en ?? '',
74
+ sample_text: names?.sampleText?.en ?? '',
75
+ trademark: names?.trademark?.en ?? '',
76
+ unique_id: names?.uniqueSubfamily?.en ?? '',
77
+ version: names?.version?.en ?? '',
78
+ };
79
+
80
+ return this.namesTable;
61
81
  }
62
82
  }