@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 +2 -2
- package/vendor/dltech/ofd/ofd_parser.js +134 -33
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@file-viewer/renderer-ofd",
|
|
3
|
-
"version": "2.1.
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
211
|
-
|
|
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
|
-
|
|
228
|
-
|
|
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
|
-
|
|
335
|
-
|
|
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(
|
|
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,
|
|
349
|
-
|
|
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,
|
|
353
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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) {
|