@file-viewer/renderer-ofd 2.1.8 → 2.1.9

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@file-viewer/renderer-ofd",
3
- "version": "2.1.8",
3
+ "version": "2.1.9",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Standalone OFD renderer plugin for Flyfish File Viewer powered by DLTech21/ofd.js.",
@@ -53,7 +53,7 @@
53
53
  "LICENSE"
54
54
  ],
55
55
  "dependencies": {
56
- "@file-viewer/core": "^2.1.8",
56
+ "@file-viewer/core": "^2.1.9",
57
57
  "jszip": "^3.10.1",
58
58
  "ofd-xml-parser": "^0.0.6"
59
59
  },
@@ -49,6 +49,98 @@ const collectGroupedItems = function (groupOrGroups, childKey) {
49
49
  return items;
50
50
  }
51
51
 
52
+ const zipEntryMapCache = new WeakMap();
53
+
54
+ const normalizeZipPath = function (value) {
55
+ if (!value) {
56
+ return '';
57
+ }
58
+ let normalized = String(value).replace(/\\/g, '/').trim();
59
+ try {
60
+ normalized = decodeURIComponent(normalized);
61
+ } catch {
62
+ // Keep the original path when it is not URI-encoded.
63
+ }
64
+ normalized = normalized
65
+ .replace(/^\/+/, '')
66
+ .replace(/^\.\//, '')
67
+ .replace(/\/+/g, '/');
68
+ const segments = [];
69
+ for (const segment of normalized.split('/')) {
70
+ if (!segment || segment === '.') {
71
+ continue;
72
+ }
73
+ if (segment === '..') {
74
+ segments.pop();
75
+ continue;
76
+ }
77
+ segments.push(segment);
78
+ }
79
+ return segments.join('/');
80
+ }
81
+
82
+ const joinZipPath = function (...parts) {
83
+ return normalizeZipPath(parts.filter(Boolean).join('/'));
84
+ }
85
+
86
+ const startsWithZipRoot = function (path, root) {
87
+ const normalizedPath = normalizeZipPath(path);
88
+ const normalizedRoot = normalizeZipPath(root);
89
+ return normalizedPath === normalizedRoot || normalizedPath.startsWith(`${normalizedRoot}/`);
90
+ }
91
+
92
+ const getZipEntry = function (zip, candidates) {
93
+ const paths = asArray(candidates).map(normalizeZipPath).filter(Boolean);
94
+ for (const path of paths) {
95
+ if (zip.files[path]) {
96
+ return zip.files[path];
97
+ }
98
+ }
99
+ let lowerMap = zipEntryMapCache.get(zip);
100
+ if (!lowerMap) {
101
+ lowerMap = new Map();
102
+ for (const name of Object.keys(zip.files)) {
103
+ lowerMap.set(normalizeZipPath(name).toLowerCase(), zip.files[name]);
104
+ }
105
+ zipEntryMapCache.set(zip, lowerMap);
106
+ }
107
+ for (const path of paths) {
108
+ const entry = lowerMap.get(path.toLowerCase());
109
+ if (entry) {
110
+ return entry;
111
+ }
112
+ }
113
+ return null;
114
+ }
115
+
116
+ const resolveDocumentPath = function (path, doc) {
117
+ const normalizedPath = normalizeZipPath(path);
118
+ if (!normalizedPath || startsWithZipRoot(normalizedPath, doc)) {
119
+ return normalizedPath;
120
+ }
121
+ return joinZipPath(doc, normalizedPath);
122
+ }
123
+
124
+ const resolveResourceCandidates = function (file, baseLoc, doc) {
125
+ const mediaFile = normalizeZipPath(file);
126
+ const base = normalizeZipPath(baseLoc);
127
+ const candidates = [mediaFile];
128
+
129
+ if (base && !startsWithZipRoot(mediaFile, base)) {
130
+ candidates.push(joinZipPath(base, mediaFile));
131
+ }
132
+
133
+ const docCandidates = [];
134
+ for (const candidate of candidates) {
135
+ docCandidates.push(candidate);
136
+ if (candidate && !startsWithZipRoot(candidate, doc)) {
137
+ docCandidates.push(joinZipPath(doc, candidate));
138
+ }
139
+ }
140
+
141
+ return [...new Set(docCandidates.filter(Boolean))];
142
+ }
143
+
52
144
  const getImageMime = function (format) {
53
145
  const normalized = format ? format.toLowerCase() : '';
54
146
  if (normalized === 'jpg' || normalized === 'jpeg') {
@@ -103,7 +195,7 @@ export const parseSingleDoc = async function ([zip, array]) {
103
195
 
104
196
  export const doGetDocRoot = async function (zip, docbody) {
105
197
  let docRoot = docbody['ofd:DocRoot'];
106
- docRoot = replaceFirstSlash(docRoot);
198
+ docRoot = normalizeZipPath(replaceFirstSlash(docRoot));
107
199
  const doc = docRoot.split('/')[0];
108
200
  const signatures = docbody['ofd:Signatures'];
109
201
  const stampAnnot = await getSignature(zip, signatures, doc);
@@ -153,7 +245,7 @@ export const getDocument = async function ([zip, doc, docRoot, stampAnnot]) {
153
245
  if (annotations.indexOf(doc) === -1) {
154
246
  annotations = `${doc}/${annotations}`;
155
247
  }
156
- if (zip.files[annotations]) {
248
+ if (getZipEntry(zip, annotations)) {
157
249
  annotations = await getJsonFromXmlContent(zip, annotations);
158
250
  array = array.concat(annotations['json']['ofd:Annotations']['ofd:Page']);
159
251
  }
@@ -170,7 +262,10 @@ const getAnnotations = async function (annoBase, annotations, doc, zip) {
170
262
  }
171
263
  const pageId = anno['@_PageID'];
172
264
  let fileLoc = anno['ofd:FileLoc'];
173
- fileLoc = replaceFirstSlash(fileLoc);
265
+ if (!fileLoc) {
266
+ continue
267
+ }
268
+ fileLoc = normalizeZipPath(replaceFirstSlash(fileLoc));
174
269
  if (annoBase && fileLoc.indexOf(annoBase) === -1) {
175
270
  fileLoc = `${annoBase}/${fileLoc}`;
176
271
  }
@@ -178,7 +273,7 @@ const getAnnotations = async function (annoBase, annotations, doc, zip) {
178
273
  fileLoc = `${doc}/${fileLoc}`;
179
274
  }
180
275
 
181
- if (zip.files[fileLoc]) {
276
+ if (getZipEntry(zip, fileLoc)) {
182
277
  const data = await getJsonFromXmlContent(zip, fileLoc);
183
278
 
184
279
  let array = [];
@@ -207,10 +302,8 @@ export const getDocumentRes = async function ([zip, doc, Document, stampAnnot, a
207
302
  let drawParamResObj = {};
208
303
  let multiMediaResObj = {};
209
304
  if (documentResPath) {
210
- if (documentResPath.indexOf(doc) == -1) {
211
- documentResPath = `${doc}/${documentResPath}`;
212
- }
213
- if (zip.files[documentResPath]) {
305
+ documentResPath = resolveDocumentPath(documentResPath, doc);
306
+ if (getZipEntry(zip, documentResPath)) {
214
307
  const data = await getJsonFromXmlContent(zip, documentResPath);
215
308
  const documentResObj = data['json']['ofd:Res'];
216
309
  fontResObj = await getFont(documentResObj);
@@ -224,10 +317,8 @@ export const getDocumentRes = async function ([zip, doc, Document, stampAnnot, a
224
317
  export const getPublicRes = async function ([zip, doc, Document, stampAnnot, annotationObjs, fontResObj, drawParamResObj, multiMediaResObj]) {
225
318
  let publicResPath = Document['ofd:CommonData']['ofd:PublicRes'];
226
319
  if (publicResPath) {
227
- if (publicResPath.indexOf(doc) == -1) {
228
- publicResPath = `${doc}/${publicResPath}`;
229
- }
230
- if (zip.files[publicResPath]) {
320
+ publicResPath = resolveDocumentPath(publicResPath, doc);
321
+ if (getZipEntry(zip, publicResPath)) {
231
322
  const data = await getJsonFromXmlContent(zip, publicResPath);
232
323
  const publicResObj = data['json']['ofd:Res'];
233
324
  let fontObj = await getFont(publicResObj);
@@ -331,29 +422,26 @@ const getMultiMediaRes = async function (zip, res, doc) {
331
422
  let array = collectGroupedItems(multiMedias, 'ofd:MultiMedia');
332
423
  for (const item of array) {
333
424
  if (item && item['ofd:MediaFile']) {
334
- let file = item['ofd:MediaFile'];
335
- if (res['@_BaseLoc']) {
336
- if (file.indexOf(res['@_BaseLoc']) === -1) {
337
- file = `${res['@_BaseLoc']}/${file}`
338
- }
339
- }
340
- if (file.indexOf(doc) === -1) {
341
- file = `${doc}/${file}`
342
- }
425
+ const candidates = resolveResourceCandidates(item['ofd:MediaFile'], res['@_BaseLoc'], doc);
426
+ const file = getZipEntry(zip, candidates) ? candidates : null;
343
427
  const type = item['@_Type'] ? item['@_Type'].toLowerCase() : '';
344
428
  if (type === 'image') {
345
429
  const format = item['@_Format'];
346
- const ext = getExtensionByPath(file);
430
+ const ext = getExtensionByPath(candidates[0]);
347
431
  if ((format && (format.toLowerCase() === 'gbig2' || format.toLowerCase() === 'jb2')) || ext && (ext.toLowerCase() === 'jb2' || ext.toLowerCase() === 'gbig2')) {
348
- const jbig2 = await parseJbig2ImageFromZip(zip, file);
349
- multiMediaResObj[item['@_ID']] = jbig2;
432
+ const jbig2 = await parseJbig2ImageFromZip(zip, candidates);
433
+ if (jbig2) {
434
+ multiMediaResObj[item['@_ID']] = jbig2;
435
+ }
350
436
  } else {
351
437
  const imageFormat = format || ext || 'png';
352
- const img = await parseOtherImageFromZip(zip, file, getImageMime(imageFormat));
353
- multiMediaResObj[item['@_ID']] = {img, 'format': imageFormat.toLowerCase()};
438
+ const img = await parseOtherImageFromZip(zip, candidates, getImageMime(imageFormat));
439
+ if (img) {
440
+ multiMediaResObj[item['@_ID']] = {img, 'format': imageFormat.toLowerCase()};
441
+ }
354
442
  }
355
443
  } else {
356
- multiMediaResObj[item['@_ID']] = file;
444
+ multiMediaResObj[item['@_ID']] = file ? file[0] : candidates[0];
357
445
  }
358
446
  }
359
447
  }
@@ -363,9 +451,7 @@ const getMultiMediaRes = async function (zip, res, doc) {
363
451
 
364
452
  const parsePage = async function (zip, obj, doc) {
365
453
  let pagePath = obj['@_BaseLoc'];
366
- if (pagePath.indexOf(doc) == -1) {
367
- pagePath = `${doc}/${pagePath}`;
368
- }
454
+ pagePath = resolveDocumentPath(pagePath, doc);
369
455
  const data = await getJsonFromXmlContent(zip, pagePath);
370
456
  let pageObj = {};
371
457
  pageObj[obj['@_ID']] = {'json': data['json']['ofd:Page'], 'xml': data['xml']};
@@ -387,7 +473,12 @@ const getSealDocumentObj = function () {
387
473
 
388
474
  const getJsonFromXmlContent = async function (zip, xmlName) {
389
475
  return new Promise((resolve, reject) => {
390
- zip.files[xmlName].async('string').then(function (content) {
476
+ const entry = getZipEntry(zip, xmlName);
477
+ if (!entry) {
478
+ reject(new Error(`OFD XML resource not found: ${normalizeZipPath(xmlName)}`));
479
+ return;
480
+ }
481
+ entry.async('string').then(function (content) {
391
482
  ensureBrowserGlobal();
392
483
  let ops = {
393
484
  attributeNamePrefix: "@_",
@@ -406,7 +497,12 @@ const getJsonFromXmlContent = async function (zip, xmlName) {
406
497
 
407
498
  const parseJbig2ImageFromZip = async function (zip, name) {
408
499
  return new Promise((resolve, reject) => {
409
- zip.files[name].async('uint8array').then(function (bytes) {
500
+ const entry = getZipEntry(zip, name);
501
+ if (!entry) {
502
+ resolve(null);
503
+ return;
504
+ }
505
+ entry.async('uint8array').then(function (bytes) {
410
506
  let jbig2 = new Jbig2Image();
411
507
  const img = jbig2.parse(bytes);
412
508
  resolve({img, width: jbig2.width, height: jbig2.height, format: 'gbig2'});
@@ -418,7 +514,12 @@ const parseJbig2ImageFromZip = async function (zip, name) {
418
514
 
419
515
  const parseOtherImageFromZip = async function (zip, name, mime = 'image/png') {
420
516
  return new Promise((resolve, reject) => {
421
- zip.files[name].async('base64').then(function (bytes) {
517
+ const entry = getZipEntry(zip, name);
518
+ if (!entry) {
519
+ resolve(null);
520
+ return;
521
+ }
522
+ entry.async('base64').then(function (bytes) {
422
523
  const img = `data:${mime};base64,` + bytes;
423
524
  resolve(img);
424
525
  }, function error(e) {