@file-viewer/core 2.2.9 → 2.4.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.
@@ -0,0 +1,156 @@
1
+ const normalizeEncoding = (encoding) => {
2
+ const normalized = String(encoding || 'auto').trim().toLowerCase().replace('_', '-');
3
+ if (normalized === 'utf8' || normalized === 'utf-8') {
4
+ return 'utf-8';
5
+ }
6
+ if (normalized === 'utf16' || normalized === 'utf-16' || normalized === 'utf16le' || normalized === 'utf-16le') {
7
+ return 'utf-16le';
8
+ }
9
+ if (normalized === 'utf16be' || normalized === 'utf-16be') {
10
+ return 'utf-16be';
11
+ }
12
+ if (normalized === 'gbk' || normalized === 'cp936' || normalized === 'gb2312') {
13
+ return 'gbk';
14
+ }
15
+ if (normalized === 'gb18030') {
16
+ return 'gb18030';
17
+ }
18
+ return 'auto';
19
+ };
20
+ const isContinuationByte = (value) => {
21
+ return value !== undefined && value >= 0x80 && value <= 0xbf;
22
+ };
23
+ // Validate bytes directly because older WebViews can accept the fatal option
24
+ // while still replacing malformed input. That would hide the GB18030 fallback.
25
+ export const isValidFileViewerUtf8 = (bytes) => {
26
+ for (let index = 0; index < bytes.length;) {
27
+ const first = bytes[index];
28
+ if (first === undefined) {
29
+ return false;
30
+ }
31
+ if (first <= 0x7f) {
32
+ index += 1;
33
+ continue;
34
+ }
35
+ const second = bytes[index + 1];
36
+ const third = bytes[index + 2];
37
+ const fourth = bytes[index + 3];
38
+ if (first >= 0xc2 && first <= 0xdf && isContinuationByte(second)) {
39
+ index += 2;
40
+ continue;
41
+ }
42
+ if (first === 0xe0 &&
43
+ second !== undefined && second >= 0xa0 && second <= 0xbf &&
44
+ isContinuationByte(third)) {
45
+ index += 3;
46
+ continue;
47
+ }
48
+ if (((first >= 0xe1 && first <= 0xec) || (first >= 0xee && first <= 0xef)) &&
49
+ isContinuationByte(second) &&
50
+ isContinuationByte(third)) {
51
+ index += 3;
52
+ continue;
53
+ }
54
+ if (first === 0xed &&
55
+ second !== undefined && second >= 0x80 && second <= 0x9f &&
56
+ isContinuationByte(third)) {
57
+ index += 3;
58
+ continue;
59
+ }
60
+ if (first === 0xf0 &&
61
+ second !== undefined && second >= 0x90 && second <= 0xbf &&
62
+ isContinuationByte(third) &&
63
+ isContinuationByte(fourth)) {
64
+ index += 4;
65
+ continue;
66
+ }
67
+ if (first >= 0xf1 && first <= 0xf3 &&
68
+ isContinuationByte(second) &&
69
+ isContinuationByte(third) &&
70
+ isContinuationByte(fourth)) {
71
+ index += 4;
72
+ continue;
73
+ }
74
+ if (first === 0xf4 &&
75
+ second !== undefined && second >= 0x80 && second <= 0x8f &&
76
+ isContinuationByte(third) &&
77
+ isContinuationByte(fourth)) {
78
+ index += 4;
79
+ continue;
80
+ }
81
+ return false;
82
+ }
83
+ return true;
84
+ };
85
+ const inferBomlessUtf16 = (bytes) => {
86
+ const sampleLength = Math.min(bytes.length - (bytes.length % 2), 4096);
87
+ if (sampleLength < 4) {
88
+ return null;
89
+ }
90
+ let evenNulls = 0;
91
+ let oddNulls = 0;
92
+ for (let index = 0; index < sampleLength; index += 2) {
93
+ evenNulls += bytes[index] === 0 ? 1 : 0;
94
+ oddNulls += bytes[index + 1] === 0 ? 1 : 0;
95
+ }
96
+ const pairs = sampleLength / 2;
97
+ if (oddNulls / pairs >= 0.3 && evenNulls / pairs <= 0.05) {
98
+ return 'utf-16le';
99
+ }
100
+ if (evenNulls / pairs >= 0.3 && oddNulls / pairs <= 0.05) {
101
+ return 'utf-16be';
102
+ }
103
+ return null;
104
+ };
105
+ export const resolveFileViewerTextEncoding = (bytes, encoding = 'auto') => {
106
+ const normalized = normalizeEncoding(encoding);
107
+ if (normalized !== 'auto') {
108
+ return {
109
+ encoding: normalized === 'gbk' ? 'gb18030' : normalized,
110
+ bomLength: normalized === 'utf-8' && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf
111
+ ? 3
112
+ : (normalized === 'utf-16le' && bytes[0] === 0xff && bytes[1] === 0xfe) ||
113
+ (normalized === 'utf-16be' && bytes[0] === 0xfe && bytes[1] === 0xff)
114
+ ? 2
115
+ : 0
116
+ };
117
+ }
118
+ if (bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf) {
119
+ return { encoding: 'utf-8', bomLength: 3 };
120
+ }
121
+ if (bytes[0] === 0xff && bytes[1] === 0xfe) {
122
+ return { encoding: 'utf-16le', bomLength: 2 };
123
+ }
124
+ if (bytes[0] === 0xfe && bytes[1] === 0xff) {
125
+ return { encoding: 'utf-16be', bomLength: 2 };
126
+ }
127
+ const utf16 = inferBomlessUtf16(bytes);
128
+ if (utf16) {
129
+ return { encoding: utf16, bomLength: 0 };
130
+ }
131
+ return isValidFileViewerUtf8(bytes)
132
+ ? { encoding: 'utf-8', bomLength: 0 }
133
+ : { encoding: 'gb18030', bomLength: 0 };
134
+ };
135
+ export const createFileViewerTextDecoder = (encoding) => {
136
+ if (typeof TextDecoder === 'undefined') {
137
+ throw new Error('Text decoding requires the browser TextDecoder API.');
138
+ }
139
+ try {
140
+ return new TextDecoder(encoding);
141
+ }
142
+ catch (error) {
143
+ if (encoding === 'gb18030' && error instanceof RangeError) {
144
+ throw Object.assign(new Error('This browser does not provide GB18030 text decoding. Use a current browser or select UTF-8 explicitly.'), { cause: error });
145
+ }
146
+ throw error;
147
+ }
148
+ };
149
+ export const decodeFileViewerTextBuffer = (data, encoding = 'auto') => {
150
+ const bytes = new Uint8Array(data);
151
+ const resolved = resolveFileViewerTextEncoding(bytes, encoding);
152
+ return {
153
+ text: createFileViewerTextDecoder(resolved.encoding).decode(bytes.subarray(resolved.bomLength)),
154
+ encoding: resolved.encoding
155
+ };
156
+ };
@@ -31,6 +31,9 @@ const extensionLabel = (extension, i18n) => {
31
31
  if (locale === 'ja-JP') {
32
32
  return '現在のファイル';
33
33
  }
34
+ if (locale === 'de-DE') {
35
+ return 'aktuelle Datei';
36
+ }
34
37
  return 'current file';
35
38
  };
36
39
  const rendererPackageById = {
@@ -178,7 +181,9 @@ export const createFileViewerUnsupportedState = (extension = '', theme = DEFAULT
178
181
  ? `;如果需要极致裁剪,也可以只安装 ${installHint.rendererPackage}`
179
182
  : locale === 'ja-JP'
180
183
  ? `;厳密に最小構成へ絞る場合は ${installHint.rendererPackage} のみをインストールすることもできます`
181
- : `; for a strict custom cut, install only ${installHint.rendererPackage}`
184
+ : locale === 'de-DE'
185
+ ? `; für eine strikt minimale Konfiguration installieren Sie nur ${installHint.rendererPackage}`
186
+ : `; for a strict custom cut, install only ${installHint.rendererPackage}`
182
187
  : '';
183
188
  return createFileViewerStateDescriptor({
184
189
  state: 'unsupported',
@@ -270,9 +275,9 @@ export const formatFileViewerErrorMessage = (prefix, error, i18n) => {
270
275
  if (!detail) {
271
276
  return prefix;
272
277
  }
273
- return resolveFileViewerLocale(i18n) === 'en-US'
274
- ? `${prefix}: ${detail}`
275
- : `${prefix}:${detail}`;
278
+ return resolveFileViewerLocale(i18n) === 'zh-CN' || resolveFileViewerLocale(i18n) === 'ja-JP'
279
+ ? `${prefix}:${detail}`
280
+ : `${prefix}: ${detail}`;
276
281
  };
277
282
  export const createFileViewerErrorState = (extension = '', error = undefined, theme = DEFAULT_FILE_VIEWER_STATE_THEME, i18n) => createFileViewerStateDescriptor({
278
283
  state: 'error',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@file-viewer/core",
3
- "version": "2.2.9",
3
+ "version": "2.4.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Framework-neutral modular TypeScript core for File Viewer renderer orchestration, lifecycle APIs, search, zoom, print, and export",