@file-viewer/doc 2.2.5 → 2.2.7
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/html-word.d.ts +3 -0
- package/dist/msdoc/html-word.js +178 -0
- package/dist/msdoc/parser.js +90 -19
- package/package.json +4 -2
package/dist/msdoc/constants.js
CHANGED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { charPropsToState, paraPropsToState } from './properties.js';
|
|
2
|
+
const HTML_PREFIX_RE = /^(?:<!doctype\s+html\b|<html\b|<body\b)/i;
|
|
3
|
+
const BLOCK_RE = /<(div|p|h[1-6]|li|pre|blockquote|tr)\b([^>]*)>([\s\S]*?)<\/\1\s*>/gi;
|
|
4
|
+
const UNSAFE_CONTENT_RE = /<(script|style|noscript|iframe|object|embed|svg)\b[^>]*>[\s\S]*?<\/\1\s*>/gi;
|
|
5
|
+
function asciiPreview(bytes, length = 1024) {
|
|
6
|
+
return Array.from(bytes.subarray(0, Math.min(length, bytes.length)), byte => byte === 0 ? '' : String.fromCharCode(byte)).join('');
|
|
7
|
+
}
|
|
8
|
+
export function isHtmlWordDocumentStream(bytes) {
|
|
9
|
+
const preview = asciiPreview(bytes)
|
|
10
|
+
.replace(/^\uFEFF/, '')
|
|
11
|
+
.trimStart();
|
|
12
|
+
return HTML_PREFIX_RE.test(preview);
|
|
13
|
+
}
|
|
14
|
+
function decodeWith(bytes, label, fatal = false) {
|
|
15
|
+
try {
|
|
16
|
+
return new TextDecoder(label, { fatal }).decode(bytes);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function decodeHtmlWordDocument(bytes) {
|
|
23
|
+
let end = bytes.length;
|
|
24
|
+
while (end > 0 && bytes[end - 1] === 0)
|
|
25
|
+
end -= 1;
|
|
26
|
+
const content = bytes.subarray(0, end);
|
|
27
|
+
const preview = asciiPreview(content, 4096);
|
|
28
|
+
const declared = /charset\s*=\s*["']?([a-z0-9._-]+)/i.exec(preview)?.[1]?.toLowerCase();
|
|
29
|
+
const declaredEncoding = declared === 'gb2312' || declared === 'gbk' ? 'gb18030' : declared;
|
|
30
|
+
if (declaredEncoding) {
|
|
31
|
+
const decoded = decodeWith(content, declaredEncoding);
|
|
32
|
+
if (decoded != null)
|
|
33
|
+
return decoded;
|
|
34
|
+
}
|
|
35
|
+
const utf8 = decodeWith(content, 'utf-8', true);
|
|
36
|
+
if (utf8 != null)
|
|
37
|
+
return utf8;
|
|
38
|
+
return decodeWith(content, 'gb18030') ?? decodeWith(content, 'utf-8') ?? '';
|
|
39
|
+
}
|
|
40
|
+
function decodeEntities(value) {
|
|
41
|
+
const named = {
|
|
42
|
+
amp: '&',
|
|
43
|
+
apos: "'",
|
|
44
|
+
gt: '>',
|
|
45
|
+
lt: '<',
|
|
46
|
+
nbsp: '\u00a0',
|
|
47
|
+
quot: '"',
|
|
48
|
+
};
|
|
49
|
+
return value.replace(/&(#(?:x[0-9a-f]+|\d+)|[a-z][a-z0-9]+);/gi, (match, entity) => {
|
|
50
|
+
if (entity[0] === '#') {
|
|
51
|
+
const hexadecimal = entity[1]?.toLowerCase() === 'x';
|
|
52
|
+
const parsed = Number.parseInt(entity.slice(hexadecimal ? 2 : 1), hexadecimal ? 16 : 10);
|
|
53
|
+
if (Number.isFinite(parsed) && parsed >= 0 && parsed <= 0x10ffff) {
|
|
54
|
+
try {
|
|
55
|
+
return String.fromCodePoint(parsed);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return match;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return match;
|
|
62
|
+
}
|
|
63
|
+
return named[entity.toLowerCase()] ?? match;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function fragmentToText(fragment) {
|
|
67
|
+
return decodeEntities(fragment
|
|
68
|
+
.replace(UNSAFE_CONTENT_RE, '')
|
|
69
|
+
.replace(/<!--[\s\S]*?-->/g, '')
|
|
70
|
+
.replace(/<br\s*\/?>/gi, '\n')
|
|
71
|
+
.replace(/<\/(?:div|p|h[1-6]|li|pre|blockquote|tr|table)>/gi, '\n')
|
|
72
|
+
.replace(/<\/?(?:td|th)\b[^>]*>/gi, '\t')
|
|
73
|
+
.replace(/<[^>]+>/g, '')
|
|
74
|
+
.replace(/\u0000/g, ''))
|
|
75
|
+
.replace(/\r\n?/g, '\n')
|
|
76
|
+
.replace(/[\t\f\v ]+/g, ' ')
|
|
77
|
+
.replace(/ *\n */g, '\n')
|
|
78
|
+
.trim();
|
|
79
|
+
}
|
|
80
|
+
function extractParagraphs(html) {
|
|
81
|
+
const safe = html.replace(UNSAFE_CONTENT_RE, '').replace(/<!--[\s\S]*?-->/g, '');
|
|
82
|
+
const paragraphs = [];
|
|
83
|
+
for (const match of safe.matchAll(BLOCK_RE)) {
|
|
84
|
+
const content = match[3] || '';
|
|
85
|
+
const text = fragmentToText(content);
|
|
86
|
+
if (!text)
|
|
87
|
+
continue;
|
|
88
|
+
paragraphs.push({ text, attributes: match[2] || '', content });
|
|
89
|
+
}
|
|
90
|
+
if (paragraphs.length)
|
|
91
|
+
return paragraphs;
|
|
92
|
+
const text = fragmentToText(safe);
|
|
93
|
+
return text ? [{ text, attributes: '', content: safe }] : [];
|
|
94
|
+
}
|
|
95
|
+
function readStyle(attributes) {
|
|
96
|
+
return /\bstyle\s*=\s*(["'])([\s\S]*?)\1/i.exec(attributes)?.[2] || '';
|
|
97
|
+
}
|
|
98
|
+
function createParagraph(source, index) {
|
|
99
|
+
const paraState = paraPropsToState([]);
|
|
100
|
+
const charState = charPropsToState([]);
|
|
101
|
+
const style = readStyle(source.attributes);
|
|
102
|
+
const alignment = /text-align\s*:\s*(center|right|justify)/i.exec(style)?.[1]?.toLowerCase();
|
|
103
|
+
if (alignment === 'center')
|
|
104
|
+
paraState.alignment = 1;
|
|
105
|
+
if (alignment === 'right')
|
|
106
|
+
paraState.alignment = 2;
|
|
107
|
+
if (alignment === 'justify')
|
|
108
|
+
paraState.alignment = 3;
|
|
109
|
+
const indentPoints = Number.parseFloat(/text-indent\s*:\s*([\d.]+)pt/i.exec(style)?.[1] || '');
|
|
110
|
+
if (Number.isFinite(indentPoints))
|
|
111
|
+
paraState.firstLineIndent = Math.round(indentPoints * 20);
|
|
112
|
+
const fontSizePoints = Number.parseFloat(/font-size\s*:\s*([\d.]+)pt/i.exec(style)?.[1] || '');
|
|
113
|
+
if (Number.isFinite(fontSizePoints)) {
|
|
114
|
+
charState.fontSizeHalfPoints = Math.round(Math.min(96, Math.max(8, fontSizePoints)) * 2);
|
|
115
|
+
}
|
|
116
|
+
if (/font-weight\s*:\s*(?:bold|[6-9]00)/i.test(style) ||
|
|
117
|
+
/<(?:b|strong)\b/i.test(source.content)) {
|
|
118
|
+
charState.bold = true;
|
|
119
|
+
}
|
|
120
|
+
if (/font-style\s*:\s*italic/i.test(style) || /<(?:i|em)\b/i.test(source.content)) {
|
|
121
|
+
charState.italic = true;
|
|
122
|
+
}
|
|
123
|
+
const inlines = source.text
|
|
124
|
+
.split('\n')
|
|
125
|
+
.flatMap((line, lineIndex) => [
|
|
126
|
+
...(lineIndex ? [{ type: 'lineBreak' }] : []),
|
|
127
|
+
...(line ? [{ type: 'text', text: line, style: { ...charState } }] : []),
|
|
128
|
+
]);
|
|
129
|
+
return {
|
|
130
|
+
type: 'paragraph',
|
|
131
|
+
id: `html-word-paragraph-${index + 1}`,
|
|
132
|
+
styleId: 0,
|
|
133
|
+
styleName: 'Normal',
|
|
134
|
+
paraState,
|
|
135
|
+
inlines,
|
|
136
|
+
text: source.text,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
export function parseHtmlWordDocumentStream(bytes) {
|
|
140
|
+
if (!isHtmlWordDocumentStream(bytes))
|
|
141
|
+
return null;
|
|
142
|
+
const paragraphs = extractParagraphs(decodeHtmlWordDocument(bytes));
|
|
143
|
+
if (!paragraphs.length)
|
|
144
|
+
return null;
|
|
145
|
+
const blocks = paragraphs.map(createParagraph);
|
|
146
|
+
const textLength = paragraphs.reduce((total, paragraph) => total + paragraph.text.length, 0);
|
|
147
|
+
return {
|
|
148
|
+
kind: 'msdoc',
|
|
149
|
+
version: 1,
|
|
150
|
+
warnings: [
|
|
151
|
+
{
|
|
152
|
+
message: 'Recovered an HTML document stored inside the legacy WordDocument stream.',
|
|
153
|
+
code: 'MSDOC_HTML_WORDDOCUMENT_RECOVERED',
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
meta: {
|
|
157
|
+
fib: {
|
|
158
|
+
wIdent: 0,
|
|
159
|
+
nFib: 0,
|
|
160
|
+
fWhichTblStm: 0,
|
|
161
|
+
fComplex: false,
|
|
162
|
+
fEncrypted: false,
|
|
163
|
+
ccpText: textLength,
|
|
164
|
+
},
|
|
165
|
+
counts: {
|
|
166
|
+
paragraphs: blocks.length,
|
|
167
|
+
blocks: blocks.length,
|
|
168
|
+
assets: 0,
|
|
169
|
+
styles: 0,
|
|
170
|
+
fonts: 0,
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
fonts: [],
|
|
174
|
+
styles: [],
|
|
175
|
+
blocks,
|
|
176
|
+
assets: [],
|
|
177
|
+
};
|
|
178
|
+
}
|
package/dist/msdoc/parser.js
CHANGED
|
@@ -5,6 +5,7 @@ import { DOC_CONTROL } from './constants.js';
|
|
|
5
5
|
import { parseFib } from './fib.js';
|
|
6
6
|
import { readChpxRuns, readPapxRuns } from './fkp.js';
|
|
7
7
|
import { parseFonts } from './fonts.js';
|
|
8
|
+
import { parseHtmlWordDocumentStream } from './html-word.js';
|
|
8
9
|
import { extractObjectPool, extractPictureAsset } from './objects.js';
|
|
9
10
|
import { applyTableStateToCells, charPropsToState, getTableDepth, paraPropsToState, tablePropsToState, } from './properties.js';
|
|
10
11
|
import { mergePropertyArrays, parseStyles, splitPropertiesByKind } from './styles.js';
|
|
@@ -123,13 +124,68 @@ function normalizePlainTextChar(ch) {
|
|
|
123
124
|
case DOC_CONTROL.nonBreakingHyphen:
|
|
124
125
|
return '-';
|
|
125
126
|
case DOC_CONTROL.nonRequiredHyphen:
|
|
126
|
-
return '';
|
|
127
127
|
case DOC_CONTROL.annotationRef:
|
|
128
|
+
case DOC_CONTROL.textBoxPlaceholder:
|
|
128
129
|
return '';
|
|
129
130
|
default:
|
|
130
131
|
return ch;
|
|
131
132
|
}
|
|
132
133
|
}
|
|
134
|
+
function normalizeParagraphText(text) {
|
|
135
|
+
return text.replaceAll(DOC_CONTROL.textBoxPlaceholder, '');
|
|
136
|
+
}
|
|
137
|
+
function getDocumentStoryRanges(fib, documentLength) {
|
|
138
|
+
const storyLengths = [
|
|
139
|
+
['main', fib.fibRgLw.ccpText],
|
|
140
|
+
['footnote', fib.fibRgLw.ccpFtn],
|
|
141
|
+
['header', fib.fibRgLw.ccpHdd],
|
|
142
|
+
['macro', fib.fibRgLw.ccpMcr],
|
|
143
|
+
['annotation', fib.fibRgLw.ccpAtn],
|
|
144
|
+
['endnote', fib.fibRgLw.ccpEdn],
|
|
145
|
+
['textbox', fib.fibRgLw.ccpTxbx],
|
|
146
|
+
['header-textbox', fib.fibRgLw.ccpHdrTxbx],
|
|
147
|
+
];
|
|
148
|
+
const hasDeclaredStories = storyLengths.some(([, length]) => length > 0);
|
|
149
|
+
if (!hasDeclaredStories)
|
|
150
|
+
return [{ kind: 'main', cpStart: 0, cpEnd: documentLength }];
|
|
151
|
+
const ranges = [];
|
|
152
|
+
let cp = 0;
|
|
153
|
+
for (const [kind, rawLength] of storyLengths) {
|
|
154
|
+
const length = Math.max(0, rawLength || 0);
|
|
155
|
+
const cpStart = Math.min(cp, documentLength);
|
|
156
|
+
cp += length;
|
|
157
|
+
const cpEnd = Math.min(cp, documentLength);
|
|
158
|
+
ranges.push({ kind, cpStart, cpEnd });
|
|
159
|
+
}
|
|
160
|
+
return ranges;
|
|
161
|
+
}
|
|
162
|
+
function buildStoryParagraphRanges(story, documentText, papxRuns) {
|
|
163
|
+
if (story.cpEnd <= story.cpStart)
|
|
164
|
+
return [];
|
|
165
|
+
const runs = papxRuns.filter((run) => run.cpStart < story.cpEnd && run.cpEnd > story.cpStart);
|
|
166
|
+
if (runs.length) {
|
|
167
|
+
return runs.map((run) => {
|
|
168
|
+
const cpStart = Math.max(story.cpStart, run.cpStart);
|
|
169
|
+
const cpEnd = Math.min(story.cpEnd, run.cpEnd);
|
|
170
|
+
return {
|
|
171
|
+
cpStart,
|
|
172
|
+
cpEnd,
|
|
173
|
+
terminator: documentText[cpEnd - 1] || '',
|
|
174
|
+
styleId: run.styleId,
|
|
175
|
+
properties: run.properties,
|
|
176
|
+
storyKind: story.kind,
|
|
177
|
+
};
|
|
178
|
+
}).filter((range) => range.cpEnd > range.cpStart);
|
|
179
|
+
}
|
|
180
|
+
return splitParagraphRanges(documentText.slice(story.cpStart, story.cpEnd)).map((range) => ({
|
|
181
|
+
...range,
|
|
182
|
+
cpStart: range.cpStart + story.cpStart,
|
|
183
|
+
cpEnd: range.cpEnd + story.cpStart,
|
|
184
|
+
styleId: 0,
|
|
185
|
+
properties: [],
|
|
186
|
+
storyKind: story.kind,
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
133
189
|
function buildInlineNodes(segments, resolveAsset) {
|
|
134
190
|
const output = [];
|
|
135
191
|
const fieldStack = [];
|
|
@@ -207,7 +263,7 @@ function buildInlineNodes(segments, resolveAsset) {
|
|
|
207
263
|
}
|
|
208
264
|
return output;
|
|
209
265
|
}
|
|
210
|
-
function buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps, resolveFont, cursorRef) {
|
|
266
|
+
function buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps, resolveFont, cursorRef, forceReadableColors = false) {
|
|
211
267
|
const overlaps = getOverlappingRuns(chpxRuns, range.cpStart, range.cpEnd, cursorRef);
|
|
212
268
|
const boundaries = new Set([range.cpStart, range.cpEnd]);
|
|
213
269
|
for (const run of overlaps) {
|
|
@@ -227,6 +283,8 @@ function buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps
|
|
|
227
283
|
const charStyleProps = directState.charStyleId != null ? styles.resolveStyle(directState.charStyleId).charProps : [];
|
|
228
284
|
const finalProps = mergePropertyArrays(baseCharProps, charStyleProps, directProps);
|
|
229
285
|
const state = charPropsToState(finalProps);
|
|
286
|
+
if (forceReadableColors && state.colorIndex === 8)
|
|
287
|
+
state.colorIndex = 1;
|
|
230
288
|
const font = resolveFont(state.fontFamilyId);
|
|
231
289
|
if (font)
|
|
232
290
|
state.fontFamily = font.name || font.altName || undefined;
|
|
@@ -245,6 +303,8 @@ function buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps
|
|
|
245
303
|
}
|
|
246
304
|
if (!segments.length && paragraphText) {
|
|
247
305
|
const state = charPropsToState(baseCharProps);
|
|
306
|
+
if (forceReadableColors && state.colorIndex === 8)
|
|
307
|
+
state.colorIndex = 1;
|
|
248
308
|
const font = resolveFont(state.fontFamilyId);
|
|
249
309
|
if (font)
|
|
250
310
|
state.fontFamily = font.name || font.altName || undefined;
|
|
@@ -264,7 +324,7 @@ function buildParagraphModel(range, paragraphText, styles, fonts, chpxRuns, reso
|
|
|
264
324
|
const tableState = tablePropsToState(tableProps);
|
|
265
325
|
const baseCharProps = paraStyle.charProps;
|
|
266
326
|
const resolveFont = (fontId) => fonts.byIndex(fontId);
|
|
267
|
-
const segments = buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps, resolveFont, chpxCursor);
|
|
327
|
+
const segments = buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps, resolveFont, chpxCursor, range.storyKind === 'textbox' || range.storyKind === 'header-textbox');
|
|
268
328
|
const inlines = buildInlineNodes(segments, resolveAsset);
|
|
269
329
|
return {
|
|
270
330
|
id: uniqueId('para'),
|
|
@@ -587,6 +647,11 @@ export function parseMsDoc(input, options = {}) {
|
|
|
587
647
|
const wordBytes = cfb.getStream('/WordDocument');
|
|
588
648
|
if (!wordBytes)
|
|
589
649
|
throw new Error('Missing WordDocument stream');
|
|
650
|
+
const htmlDocument = parseHtmlWordDocumentStream(wordBytes);
|
|
651
|
+
if (htmlDocument) {
|
|
652
|
+
htmlDocument.warnings.unshift(...(cfb.warnings || []));
|
|
653
|
+
return htmlDocument;
|
|
654
|
+
}
|
|
590
655
|
const fib = parseFib(wordBytes);
|
|
591
656
|
if (fib.base.wIdent !== 0xA5EC) {
|
|
592
657
|
pushWarning(warnings, `Unexpected FIB identifier: 0x${fib.base.wIdent.toString(16)}`);
|
|
@@ -601,35 +666,41 @@ export function parseMsDoc(input, options = {}) {
|
|
|
601
666
|
const clx = parseClx(tableBytes, fib.fibRgFcLcb);
|
|
602
667
|
const pieceTexts = buildPieceTextCache(wordBytes, clx);
|
|
603
668
|
const documentText = pieceTexts.join('');
|
|
604
|
-
const
|
|
669
|
+
const stories = getDocumentStoryRanges(fib, documentText.length);
|
|
670
|
+
const mainStory = stories.find((story) => story.kind === 'main') || { kind: 'main', cpStart: 0, cpEnd: documentText.length };
|
|
671
|
+
const supplementalTextStories = stories.filter((story) => ((story.kind === 'textbox' || story.kind === 'header-textbox')
|
|
672
|
+
&& normalizeParagraphText(documentText.slice(story.cpStart, story.cpEnd)).replace(/[\r\u0007]/g, '').trim().length > 0));
|
|
605
673
|
const styles = parseStyles(tableBytes, fib.fibRgFcLcb);
|
|
606
674
|
const fonts = parseFonts(tableBytes, fib.fibRgFcLcb);
|
|
607
|
-
const
|
|
675
|
+
const relevantStoryEnd = Math.max(mainStory.cpEnd, ...supplementalTextStories.map((story) => story.cpEnd));
|
|
676
|
+
const chpxRuns = readChpxRuns(wordBytes, tableBytes, fib, clx)
|
|
677
|
+
.filter((run) => run.cpStart < relevantStoryEnd)
|
|
678
|
+
.map((run) => ({ ...run, cpEnd: Math.min(run.cpEnd, relevantStoryEnd) }));
|
|
608
679
|
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: [] }));
|
|
680
|
+
.filter((run) => run.cpStart < relevantStoryEnd)
|
|
681
|
+
.map((run) => ({ ...run, cpEnd: Math.min(run.cpEnd, relevantStoryEnd) }));
|
|
682
|
+
const ranges = [mainStory, ...supplementalTextStories]
|
|
683
|
+
.flatMap((story) => buildStoryParagraphRanges(story, documentText, papxRuns));
|
|
684
|
+
if (supplementalTextStories.length) {
|
|
685
|
+
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 });
|
|
686
|
+
}
|
|
620
687
|
const objectPool = extractObjectPool(cfb);
|
|
621
688
|
const assets = [];
|
|
622
689
|
const usedAttachmentNames = new Set();
|
|
623
690
|
const assetCache = new Map();
|
|
624
691
|
const resolveAsset = createAssetResolver(dataBytes, objectPool, assets, usedAttachmentNames, assetCache, options);
|
|
625
692
|
const chpxCursor = { index: 0 };
|
|
626
|
-
const paragraphs = ranges.
|
|
693
|
+
const paragraphs = ranges.flatMap((range) => {
|
|
627
694
|
const rawParagraphText = getTextByCp(wordBytes, clx, pieceTexts, range.cpStart, range.cpEnd);
|
|
628
695
|
const terminator = range.terminator === DOC_CONTROL.paragraph || range.terminator === DOC_CONTROL.cellMark ? range.terminator : '';
|
|
629
|
-
const
|
|
696
|
+
const rawContent = terminator && rawParagraphText.endsWith(terminator)
|
|
630
697
|
? rawParagraphText.slice(0, -1)
|
|
631
698
|
: rawParagraphText;
|
|
632
|
-
|
|
699
|
+
const paragraphText = normalizeParagraphText(rawContent);
|
|
700
|
+
const isTextBoxStory = range.storyKind === 'textbox' || range.storyKind === 'header-textbox';
|
|
701
|
+
if ((rawContent.includes(DOC_CONTROL.textBoxPlaceholder) || isTextBoxStory) && !paragraphText.trim())
|
|
702
|
+
return [];
|
|
703
|
+
return [buildParagraphModel({ ...range, terminator }, paragraphText, styles, fonts, chpxRuns, resolveAsset, chpxCursor)];
|
|
633
704
|
});
|
|
634
705
|
normalizeRowEndOnlyTables(paragraphs);
|
|
635
706
|
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.7",
|
|
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,8 @@
|
|
|
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",
|
|
75
|
+
"verify:github-192": "pnpm build && node scripts/verify-github-192.mjs"
|
|
74
76
|
}
|
|
75
77
|
}
|