@file-viewer/doc 2.2.4 → 2.2.6
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/dist/msdoc/constants.d.ts +1 -0
- package/dist/msdoc/constants.js +1 -0
- package/dist/msdoc/parser.js +84 -19
- package/package.json +3 -2
package/dist/msdoc/constants.js
CHANGED
package/dist/msdoc/parser.js
CHANGED
|
@@ -123,13 +123,68 @@ function normalizePlainTextChar(ch) {
|
|
|
123
123
|
case DOC_CONTROL.nonBreakingHyphen:
|
|
124
124
|
return '-';
|
|
125
125
|
case DOC_CONTROL.nonRequiredHyphen:
|
|
126
|
-
return '';
|
|
127
126
|
case DOC_CONTROL.annotationRef:
|
|
127
|
+
case DOC_CONTROL.textBoxPlaceholder:
|
|
128
128
|
return '';
|
|
129
129
|
default:
|
|
130
130
|
return ch;
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
+
function normalizeParagraphText(text) {
|
|
134
|
+
return text.replaceAll(DOC_CONTROL.textBoxPlaceholder, '');
|
|
135
|
+
}
|
|
136
|
+
function getDocumentStoryRanges(fib, documentLength) {
|
|
137
|
+
const storyLengths = [
|
|
138
|
+
['main', fib.fibRgLw.ccpText],
|
|
139
|
+
['footnote', fib.fibRgLw.ccpFtn],
|
|
140
|
+
['header', fib.fibRgLw.ccpHdd],
|
|
141
|
+
['macro', fib.fibRgLw.ccpMcr],
|
|
142
|
+
['annotation', fib.fibRgLw.ccpAtn],
|
|
143
|
+
['endnote', fib.fibRgLw.ccpEdn],
|
|
144
|
+
['textbox', fib.fibRgLw.ccpTxbx],
|
|
145
|
+
['header-textbox', fib.fibRgLw.ccpHdrTxbx],
|
|
146
|
+
];
|
|
147
|
+
const hasDeclaredStories = storyLengths.some(([, length]) => length > 0);
|
|
148
|
+
if (!hasDeclaredStories)
|
|
149
|
+
return [{ kind: 'main', cpStart: 0, cpEnd: documentLength }];
|
|
150
|
+
const ranges = [];
|
|
151
|
+
let cp = 0;
|
|
152
|
+
for (const [kind, rawLength] of storyLengths) {
|
|
153
|
+
const length = Math.max(0, rawLength || 0);
|
|
154
|
+
const cpStart = Math.min(cp, documentLength);
|
|
155
|
+
cp += length;
|
|
156
|
+
const cpEnd = Math.min(cp, documentLength);
|
|
157
|
+
ranges.push({ kind, cpStart, cpEnd });
|
|
158
|
+
}
|
|
159
|
+
return ranges;
|
|
160
|
+
}
|
|
161
|
+
function buildStoryParagraphRanges(story, documentText, papxRuns) {
|
|
162
|
+
if (story.cpEnd <= story.cpStart)
|
|
163
|
+
return [];
|
|
164
|
+
const runs = papxRuns.filter((run) => run.cpStart < story.cpEnd && run.cpEnd > story.cpStart);
|
|
165
|
+
if (runs.length) {
|
|
166
|
+
return runs.map((run) => {
|
|
167
|
+
const cpStart = Math.max(story.cpStart, run.cpStart);
|
|
168
|
+
const cpEnd = Math.min(story.cpEnd, run.cpEnd);
|
|
169
|
+
return {
|
|
170
|
+
cpStart,
|
|
171
|
+
cpEnd,
|
|
172
|
+
terminator: documentText[cpEnd - 1] || '',
|
|
173
|
+
styleId: run.styleId,
|
|
174
|
+
properties: run.properties,
|
|
175
|
+
storyKind: story.kind,
|
|
176
|
+
};
|
|
177
|
+
}).filter((range) => range.cpEnd > range.cpStart);
|
|
178
|
+
}
|
|
179
|
+
return splitParagraphRanges(documentText.slice(story.cpStart, story.cpEnd)).map((range) => ({
|
|
180
|
+
...range,
|
|
181
|
+
cpStart: range.cpStart + story.cpStart,
|
|
182
|
+
cpEnd: range.cpEnd + story.cpStart,
|
|
183
|
+
styleId: 0,
|
|
184
|
+
properties: [],
|
|
185
|
+
storyKind: story.kind,
|
|
186
|
+
}));
|
|
187
|
+
}
|
|
133
188
|
function buildInlineNodes(segments, resolveAsset) {
|
|
134
189
|
const output = [];
|
|
135
190
|
const fieldStack = [];
|
|
@@ -207,7 +262,7 @@ function buildInlineNodes(segments, resolveAsset) {
|
|
|
207
262
|
}
|
|
208
263
|
return output;
|
|
209
264
|
}
|
|
210
|
-
function buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps, resolveFont, cursorRef) {
|
|
265
|
+
function buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps, resolveFont, cursorRef, forceReadableColors = false) {
|
|
211
266
|
const overlaps = getOverlappingRuns(chpxRuns, range.cpStart, range.cpEnd, cursorRef);
|
|
212
267
|
const boundaries = new Set([range.cpStart, range.cpEnd]);
|
|
213
268
|
for (const run of overlaps) {
|
|
@@ -227,6 +282,8 @@ function buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps
|
|
|
227
282
|
const charStyleProps = directState.charStyleId != null ? styles.resolveStyle(directState.charStyleId).charProps : [];
|
|
228
283
|
const finalProps = mergePropertyArrays(baseCharProps, charStyleProps, directProps);
|
|
229
284
|
const state = charPropsToState(finalProps);
|
|
285
|
+
if (forceReadableColors && state.colorIndex === 8)
|
|
286
|
+
state.colorIndex = 1;
|
|
230
287
|
const font = resolveFont(state.fontFamilyId);
|
|
231
288
|
if (font)
|
|
232
289
|
state.fontFamily = font.name || font.altName || undefined;
|
|
@@ -245,6 +302,8 @@ function buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps
|
|
|
245
302
|
}
|
|
246
303
|
if (!segments.length && paragraphText) {
|
|
247
304
|
const state = charPropsToState(baseCharProps);
|
|
305
|
+
if (forceReadableColors && state.colorIndex === 8)
|
|
306
|
+
state.colorIndex = 1;
|
|
248
307
|
const font = resolveFont(state.fontFamilyId);
|
|
249
308
|
if (font)
|
|
250
309
|
state.fontFamily = font.name || font.altName || undefined;
|
|
@@ -264,7 +323,7 @@ function buildParagraphModel(range, paragraphText, styles, fonts, chpxRuns, reso
|
|
|
264
323
|
const tableState = tablePropsToState(tableProps);
|
|
265
324
|
const baseCharProps = paraStyle.charProps;
|
|
266
325
|
const resolveFont = (fontId) => fonts.byIndex(fontId);
|
|
267
|
-
const segments = buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps, resolveFont, chpxCursor);
|
|
326
|
+
const segments = buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps, resolveFont, chpxCursor, range.storyKind === 'textbox' || range.storyKind === 'header-textbox');
|
|
268
327
|
const inlines = buildInlineNodes(segments, resolveAsset);
|
|
269
328
|
return {
|
|
270
329
|
id: uniqueId('para'),
|
|
@@ -601,35 +660,41 @@ export function parseMsDoc(input, options = {}) {
|
|
|
601
660
|
const clx = parseClx(tableBytes, fib.fibRgFcLcb);
|
|
602
661
|
const pieceTexts = buildPieceTextCache(wordBytes, clx);
|
|
603
662
|
const documentText = pieceTexts.join('');
|
|
604
|
-
const
|
|
663
|
+
const stories = getDocumentStoryRanges(fib, documentText.length);
|
|
664
|
+
const mainStory = stories.find((story) => story.kind === 'main') || { kind: 'main', cpStart: 0, cpEnd: documentText.length };
|
|
665
|
+
const supplementalTextStories = stories.filter((story) => ((story.kind === 'textbox' || story.kind === 'header-textbox')
|
|
666
|
+
&& normalizeParagraphText(documentText.slice(story.cpStart, story.cpEnd)).replace(/[\r\u0007]/g, '').trim().length > 0));
|
|
605
667
|
const styles = parseStyles(tableBytes, fib.fibRgFcLcb);
|
|
606
668
|
const fonts = parseFonts(tableBytes, fib.fibRgFcLcb);
|
|
607
|
-
const
|
|
669
|
+
const relevantStoryEnd = Math.max(mainStory.cpEnd, ...supplementalTextStories.map((story) => story.cpEnd));
|
|
670
|
+
const chpxRuns = readChpxRuns(wordBytes, tableBytes, fib, clx)
|
|
671
|
+
.filter((run) => run.cpStart < relevantStoryEnd)
|
|
672
|
+
.map((run) => ({ ...run, cpEnd: Math.min(run.cpEnd, relevantStoryEnd) }));
|
|
608
673
|
const papxRuns = readPapxRuns(wordBytes, tableBytes, fib, clx, dataBytes)
|
|
609
|
-
.filter((run) => run.cpStart <
|
|
610
|
-
.map((run) => ({ ...run, cpEnd: Math.min(run.cpEnd,
|
|
611
|
-
const ranges =
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
styleId: run.styleId,
|
|
617
|
-
properties: run.properties,
|
|
618
|
-
}))
|
|
619
|
-
: splitParagraphRanges(documentText.slice(0, mainStoryEnd)).map((range) => ({ ...range, styleId: 0, properties: [] }));
|
|
674
|
+
.filter((run) => run.cpStart < relevantStoryEnd)
|
|
675
|
+
.map((run) => ({ ...run, cpEnd: Math.min(run.cpEnd, relevantStoryEnd) }));
|
|
676
|
+
const ranges = [mainStory, ...supplementalTextStories]
|
|
677
|
+
.flatMap((story) => buildStoryParagraphRanges(story, documentText, papxRuns));
|
|
678
|
+
if (supplementalTextStories.length) {
|
|
679
|
+
pushWarning(warnings, 'Recovered text from a legacy Word text-box story; text-box positioning is unavailable, so the content was linearized after the main story.', { code: 'MSDOC_TEXTBOX_STORY_LINEARIZED', storyCount: supplementalTextStories.length });
|
|
680
|
+
}
|
|
620
681
|
const objectPool = extractObjectPool(cfb);
|
|
621
682
|
const assets = [];
|
|
622
683
|
const usedAttachmentNames = new Set();
|
|
623
684
|
const assetCache = new Map();
|
|
624
685
|
const resolveAsset = createAssetResolver(dataBytes, objectPool, assets, usedAttachmentNames, assetCache, options);
|
|
625
686
|
const chpxCursor = { index: 0 };
|
|
626
|
-
const paragraphs = ranges.
|
|
687
|
+
const paragraphs = ranges.flatMap((range) => {
|
|
627
688
|
const rawParagraphText = getTextByCp(wordBytes, clx, pieceTexts, range.cpStart, range.cpEnd);
|
|
628
689
|
const terminator = range.terminator === DOC_CONTROL.paragraph || range.terminator === DOC_CONTROL.cellMark ? range.terminator : '';
|
|
629
|
-
const
|
|
690
|
+
const rawContent = terminator && rawParagraphText.endsWith(terminator)
|
|
630
691
|
? rawParagraphText.slice(0, -1)
|
|
631
692
|
: rawParagraphText;
|
|
632
|
-
|
|
693
|
+
const paragraphText = normalizeParagraphText(rawContent);
|
|
694
|
+
const isTextBoxStory = range.storyKind === 'textbox' || range.storyKind === 'header-textbox';
|
|
695
|
+
if ((rawContent.includes(DOC_CONTROL.textBoxPlaceholder) || isTextBoxStory) && !paragraphText.trim())
|
|
696
|
+
return [];
|
|
697
|
+
return [buildParagraphModel({ ...range, terminator }, paragraphText, styles, fonts, chpxRuns, resolveAsset, chpxCursor)];
|
|
633
698
|
});
|
|
634
699
|
normalizeRowEndOnlyTables(paragraphs);
|
|
635
700
|
const blocks = buildBlocks(paragraphs);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@file-viewer/doc",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Browser-native legacy MS-DOC (.doc) parser and HTML renderer extracted from the public msdoc-viewer 0.2.0 line.",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"type-check": "tsc -b tsconfig.json",
|
|
71
71
|
"verify:table-spec": "pnpm build && node scripts/verify-table-spec.mjs",
|
|
72
72
|
"verify:github-34": "pnpm build && node scripts/verify-github-34.mjs",
|
|
73
|
-
"verify:github-87": "pnpm build && node scripts/verify-github-87.mjs"
|
|
73
|
+
"verify:github-87": "pnpm build && node scripts/verify-github-87.mjs",
|
|
74
|
+
"verify:github-171": "pnpm build && node scripts/verify-github-171.mjs"
|
|
74
75
|
}
|
|
75
76
|
}
|