@file-viewer/renderer-ofd 3.0.2 → 3.0.3

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": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Standalone OFD renderer plugin for File Viewer powered by DLTech21/ofd.js.",
@@ -54,10 +54,11 @@
54
54
  "LICENSE"
55
55
  ],
56
56
  "dependencies": {
57
- "@file-viewer/core": "3.0.2",
58
- "jszip": "^3.10.1"
57
+ "@file-viewer/core": "3.0.3",
58
+ "jszip": "3.10.2"
59
59
  },
60
60
  "devDependencies": {
61
+ "@xmldom/xmldom": "0.9.12",
61
62
  "linkedom": "^0.18.12",
62
63
  "typescript": "^6.0.3"
63
64
  },
@@ -43,6 +43,12 @@ const appendChildValue = function (target, key, value) {
43
43
  target[key] = value;
44
44
  }
45
45
 
46
+ const xmlElementName = function (element) {
47
+ return element.namespaceURI === 'http://www.ofdspec.org/2016'
48
+ ? `ofd:${element.localName}`
49
+ : element.nodeName;
50
+ }
51
+
46
52
  const parseXmlElement = function (element, options, order) {
47
53
  const attrPrefix = options.attributeNamePrefix ?? '@_';
48
54
  const result = {};
@@ -64,7 +70,7 @@ const parseXmlElement = function (element, options, order) {
64
70
  if (parsedChild && typeof parsedChild === 'object' && pfIndex !== undefined) {
65
71
  parsedChild.pfIndex = pfIndex;
66
72
  }
67
- appendChildValue(result, child.nodeName, parsedChild);
73
+ appendChildValue(result, xmlElementName(child), parsedChild);
68
74
  continue;
69
75
  }
70
76
  if (child.nodeType === 3 || child.nodeType === 4) {
@@ -103,7 +109,7 @@ const parseXmlToJson = function (xmlData, options = {}) {
103
109
  return {};
104
110
  }
105
111
  return {
106
- [root.nodeName]: parseXmlElement(root, options, { next: 0 })
112
+ [xmlElementName(root)]: parseXmlElement(root, options, { next: 0 })
107
113
  };
108
114
  }
109
115
 
@@ -159,18 +165,13 @@ const joinZipPath = function (...parts) {
159
165
  }
160
166
 
161
167
  const startsWithZipRoot = function (path, root) {
162
- const normalizedPath = normalizeZipPath(path);
163
- const normalizedRoot = normalizeZipPath(root);
168
+ const normalizedPath = normalizeZipPath(path).toLowerCase();
169
+ const normalizedRoot = normalizeZipPath(root).toLowerCase();
164
170
  return normalizedPath === normalizedRoot || normalizedPath.startsWith(`${normalizedRoot}/`);
165
171
  }
166
172
 
167
173
  const getZipEntry = function (zip, candidates) {
168
174
  const paths = asArray(candidates).map(normalizeZipPath).filter(Boolean);
169
- for (const path of paths) {
170
- if (zip.files[path]) {
171
- return zip.files[path];
172
- }
173
- }
174
175
  let lowerMap = zipEntryMapCache.get(zip);
175
176
  if (!lowerMap) {
176
177
  lowerMap = new Map();
@@ -180,7 +181,7 @@ const getZipEntry = function (zip, candidates) {
180
181
  zipEntryMapCache.set(zip, lowerMap);
181
182
  }
182
183
  for (const path of paths) {
183
- const entry = lowerMap.get(path.toLowerCase());
184
+ const entry = zip.files[path] || lowerMap.get(path.toLowerCase());
184
185
  if (entry) {
185
186
  return entry;
186
187
  }
@@ -190,15 +191,22 @@ const getZipEntry = function (zip, candidates) {
190
191
 
191
192
  const resolveDocumentPath = function (path, doc) {
192
193
  const normalizedPath = normalizeZipPath(path);
193
- if (!normalizedPath || startsWithZipRoot(normalizedPath, doc)) {
194
+ if (!normalizedPath || !doc || /^[\\/]/.test(String(path).trim()) || startsWithZipRoot(normalizedPath, doc)) {
194
195
  return normalizedPath;
195
196
  }
196
- return joinZipPath(doc, normalizedPath);
197
+ return joinZipPath(doc, path);
197
198
  }
198
199
 
199
- const resolveResourceCandidates = function (file, baseLoc, doc) {
200
+ const resolveResourceCandidates = function (file, baseLoc, doc, resourcePath) {
200
201
  const mediaFile = normalizeZipPath(file);
201
202
  const base = normalizeZipPath(baseLoc);
203
+ const rawFile = String(file || '').replace(/\\/g, '/').trim();
204
+ const rawBase = String(baseLoc || '').replace(/\\/g, '/').trim();
205
+ const resourceDirectory = normalizeZipPath(resourcePath).split('/').slice(0, -1).join('/');
206
+ const resourceBase = rawBase.startsWith('/') ? normalizeZipPath(rawBase) : joinZipPath(resourceDirectory, rawBase);
207
+ // Resolve relative components before normalizing '..'. Declared locations
208
+ // outrank compatibility fallbacks, including their case-insensitive matches.
209
+ const declaredPath = rawFile.startsWith('/') ? mediaFile : joinZipPath(resourceBase, rawFile);
202
210
  const candidates = [mediaFile];
203
211
 
204
212
  if (base && !startsWithZipRoot(mediaFile, base)) {
@@ -213,7 +221,7 @@ const resolveResourceCandidates = function (file, baseLoc, doc) {
213
221
  }
214
222
  }
215
223
 
216
- return [...new Set(docCandidates.filter(Boolean))];
224
+ return [...new Set([declaredPath, ...docCandidates].filter(Boolean))];
217
225
  }
218
226
 
219
227
  const getImageMime = function (format) {
@@ -295,7 +303,7 @@ const sealImageMime = function (type) {
295
303
  export const doGetDocRoot = async function (zip, docbody) {
296
304
  let docRoot = docbody['ofd:DocRoot'];
297
305
  docRoot = normalizeZipPath(replaceFirstSlash(docRoot));
298
- const doc = docRoot.split('/')[0];
306
+ const doc = docRoot.split('/').slice(0, -1).join('/');
299
307
  const signatures = docbody['ofd:Signatures'];
300
308
  const stampAnnot = await getSignature(zip, signatures, doc);
301
309
  let stampAnnotArray = {};
@@ -422,7 +430,7 @@ export const getDocumentRes = async function ([zip, doc, Document, stampAnnot, a
422
430
  const documentResObj = data['json']['ofd:Res'];
423
431
  fontResObj = await getFont(documentResObj);
424
432
  drawParamResObj = await getDrawParam(documentResObj);
425
- multiMediaResObj = await getMultiMediaRes(zip, documentResObj, doc);
433
+ multiMediaResObj = await getMultiMediaRes(zip, documentResObj, doc, documentResPath);
426
434
  }
427
435
  }
428
436
  return [zip, doc, Document, stampAnnot, annotationObjs, fontResObj, drawParamResObj, multiMediaResObj];
@@ -439,7 +447,7 @@ export const getPublicRes = async function ([zip, doc, Document, stampAnnot, ann
439
447
  fontResObj = Object.assign(fontResObj, fontObj);
440
448
  let drawParamObj = await getDrawParam(publicResObj);
441
449
  drawParamResObj = Object.assign(drawParamResObj, drawParamObj);
442
- let multiMediaObj = await getMultiMediaRes(zip, publicResObj, doc);
450
+ let multiMediaObj = await getMultiMediaRes(zip, publicResObj, doc, publicResPath);
443
451
  multiMediaResObj = Object.assign(multiMediaResObj, multiMediaObj);
444
452
  }
445
453
  }
@@ -529,14 +537,14 @@ const getDrawParam = async function (res) {
529
537
  return drawParamResObj;
530
538
  }
531
539
 
532
- const getMultiMediaRes = async function (zip, res, doc) {
540
+ const getMultiMediaRes = async function (zip, res, doc, resourcePath) {
533
541
  const multiMedias = res['ofd:MultiMedias'];
534
542
  let multiMediaResObj = {};
535
543
  if (multiMedias) {
536
544
  let array = collectGroupedItems(multiMedias, 'ofd:MultiMedia');
537
545
  for (const item of array) {
538
546
  if (item && item['ofd:MediaFile']) {
539
- const candidates = resolveResourceCandidates(item['ofd:MediaFile'], res['@_BaseLoc'], doc);
547
+ const candidates = resolveResourceCandidates(item['ofd:MediaFile'], res['@_BaseLoc'], doc, resourcePath);
540
548
  const file = getZipEntry(zip, candidates) ? candidates : null;
541
549
  const type = item['@_Type'] ? item['@_Type'].toLowerCase() : '';
542
550
  if (type === 'image') {
@@ -663,44 +671,32 @@ const getSealDocumentObj = function (stampAnnot) {
663
671
  }
664
672
 
665
673
  const getJsonFromXmlContent = async function (zip, xmlName) {
666
- return new Promise((resolve, reject) => {
667
- const entry = getZipEntry(zip, xmlName);
668
- if (!entry) {
669
- reject(new Error(`OFD XML resource not found: ${normalizeZipPath(xmlName)}`));
670
- return;
671
- }
672
- entry.async('string').then(function (content) {
673
- ensureBrowserGlobal();
674
- let ops = {
675
- attributeNamePrefix: "@_",
676
- ignoreAttributes: false,
677
- parseNodeValue: false,
678
- trimValues: false
679
- };
680
- let jsonObj = parseXmlToJson(content, ops);
681
- let result = {'xml': content, 'json': jsonObj};
682
- resolve(result);
683
- }, function error(e) {
684
- reject(e);
685
- })
686
- });
674
+ const entry = getZipEntry(zip, xmlName);
675
+ if (!entry) {
676
+ throw new Error(`OFD XML resource not found: ${normalizeZipPath(xmlName)}`);
677
+ }
678
+ const content = await entry.async('string');
679
+ ensureBrowserGlobal();
680
+ try {
681
+ const json = parseXmlToJson(content, {
682
+ attributeNamePrefix: "@_",
683
+ ignoreAttributes: false,
684
+ parseNodeValue: false,
685
+ trimValues: false
686
+ });
687
+ return {xml: content, json};
688
+ } catch (error) {
689
+ throw new Error(`OFD XML parse failed (${normalizeZipPath(xmlName)}): ${error.message}`);
690
+ }
687
691
  }
688
692
 
689
693
  const parseJbig2ImageFromZip = async function (zip, name) {
690
- return new Promise((resolve, reject) => {
691
- const entry = getZipEntry(zip, name);
692
- if (!entry) {
693
- resolve(null);
694
- return;
695
- }
696
- entry.async('uint8array').then(function (bytes) {
697
- let jbig2 = new Jbig2Image();
698
- const img = jbig2.parse(bytes);
699
- resolve({img, width: jbig2.width, height: jbig2.height, format: 'gbig2'});
700
- }, function error(e) {
701
- reject(e);
702
- })
703
- });
694
+ const entry = getZipEntry(zip, name);
695
+ if (!entry) return null;
696
+ const bytes = await entry.async('uint8array');
697
+ const jbig2 = new Jbig2Image();
698
+ const img = jbig2.parse(bytes);
699
+ return {img, width: jbig2.width, height: jbig2.height, format: 'gbig2'};
704
700
  }
705
701
 
706
702
  const parseOtherImageFromZip = async function (zip, name, mime = 'image/png') {
@@ -311,6 +311,9 @@ let FONT_FAMILY = {
311
311
  };
312
312
 
313
313
  export const getFontFamily = function (font) {
314
+ if (typeof font !== 'string' || !font.trim()) {
315
+ return 'sans-serif';
316
+ }
314
317
  if (FONT_FAMILY[font.toLowerCase()]) {
315
318
  font = FONT_FAMILY[font.toLowerCase()];
316
319
  }