@file-viewer/doc 3.0.1 → 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/dist/msdoc/parser.js +30 -8
- package/dist/msdoc/properties.js +13 -1
- package/dist/msdoc/sprm.js +6 -4
- package/dist/msdoc/styles.js +5 -3
- package/dist/render/fonts.d.ts +5 -0
- package/dist/render/fonts.js +25 -0
- package/dist/render/html.js +78 -13
- package/dist/types.d.ts +17 -0
- package/package.json +4 -4
package/dist/msdoc/parser.js
CHANGED
|
@@ -232,11 +232,11 @@ function buildInlineNodes(segments, resolveAsset) {
|
|
|
232
232
|
continue;
|
|
233
233
|
}
|
|
234
234
|
if (ch === DOC_CONTROL.hardLineBreak) {
|
|
235
|
-
emitInline(fieldStack, output, { type: 'lineBreak' });
|
|
235
|
+
emitInline(fieldStack, output, { type: 'lineBreak', style: segment.state });
|
|
236
236
|
continue;
|
|
237
237
|
}
|
|
238
238
|
if (ch === DOC_CONTROL.pageBreak) {
|
|
239
|
-
emitInline(fieldStack, output, { type: 'pageBreak' });
|
|
239
|
+
emitInline(fieldStack, output, { type: 'pageBreak', style: segment.state });
|
|
240
240
|
continue;
|
|
241
241
|
}
|
|
242
242
|
if (ch === DOC_CONTROL.picture) {
|
|
@@ -263,6 +263,21 @@ function buildInlineNodes(segments, resolveAsset) {
|
|
|
263
263
|
}
|
|
264
264
|
return output;
|
|
265
265
|
}
|
|
266
|
+
function resolveCharacterFonts(state, resolveFont, defaults) {
|
|
267
|
+
for (const [id, name, fallback] of [
|
|
268
|
+
['fontFamilyId', 'fontFamily', defaults?.ftcAsci],
|
|
269
|
+
['fontFamilyEastAsiaId', 'fontFamilyEastAsia', defaults?.ftcFE],
|
|
270
|
+
['fontFamilyOtherId', 'fontFamilyOther', defaults?.ftcOther],
|
|
271
|
+
['fontFamilyBiId', 'fontFamilyBi', undefined],
|
|
272
|
+
]) {
|
|
273
|
+
const index = state[id] ?? fallback;
|
|
274
|
+
const font = resolveFont(index);
|
|
275
|
+
if (font) {
|
|
276
|
+
state[id] = index;
|
|
277
|
+
state[name] = font.name || font.altName || undefined;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
266
281
|
function buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps, resolveFont, cursorRef, forceReadableColors = false) {
|
|
267
282
|
const overlaps = getOverlappingRuns(chpxRuns, range.cpStart, range.cpEnd, cursorRef);
|
|
268
283
|
const boundaries = new Set([range.cpStart, range.cpEnd]);
|
|
@@ -285,9 +300,7 @@ function buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps
|
|
|
285
300
|
const state = charPropsToState(finalProps);
|
|
286
301
|
if (forceReadableColors && state.colorIndex === 8)
|
|
287
302
|
state.colorIndex = 1;
|
|
288
|
-
|
|
289
|
-
if (font)
|
|
290
|
-
state.fontFamily = font.name || font.altName || undefined;
|
|
303
|
+
resolveCharacterFonts(state, resolveFont, styles.header);
|
|
291
304
|
const localStart = cpStart - range.cpStart;
|
|
292
305
|
const localEnd = cpEnd - range.cpStart;
|
|
293
306
|
const text = paragraphText.slice(localStart, localEnd);
|
|
@@ -305,9 +318,7 @@ function buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps
|
|
|
305
318
|
const state = charPropsToState(baseCharProps);
|
|
306
319
|
if (forceReadableColors && state.colorIndex === 8)
|
|
307
320
|
state.colorIndex = 1;
|
|
308
|
-
|
|
309
|
-
if (font)
|
|
310
|
-
state.fontFamily = font.name || font.altName || undefined;
|
|
321
|
+
resolveCharacterFonts(state, resolveFont, styles.header);
|
|
311
322
|
segments.push({ cpStart: range.cpStart, cpEnd: range.cpEnd, text: paragraphText, state });
|
|
312
323
|
}
|
|
313
324
|
return segments;
|
|
@@ -326,11 +337,20 @@ function buildParagraphModel(range, paragraphText, styles, fonts, chpxRuns, reso
|
|
|
326
337
|
const resolveFont = (fontId) => fonts.byIndex(fontId);
|
|
327
338
|
const segments = buildCharSegments(range, paragraphText, chpxRuns, styles, baseCharProps, resolveFont, chpxCursor, range.storyKind === 'textbox' || range.storyKind === 'header-textbox');
|
|
328
339
|
const inlines = buildInlineNodes(segments, resolveAsset);
|
|
340
|
+
// The terminator has its own CHPX run and is not part of paragraphText.
|
|
341
|
+
const markState = range.terminator === DOC_CONTROL.paragraph
|
|
342
|
+
? buildCharSegments({ ...range, cpStart: range.cpEnd - 1 }, range.terminator, chpxRuns, styles, baseCharProps, resolveFont, { index: chpxCursor.index })[0]?.state
|
|
343
|
+
: undefined;
|
|
329
344
|
return {
|
|
330
345
|
id: uniqueId('para'),
|
|
331
346
|
cpStart: range.cpStart,
|
|
332
347
|
cpEnd: range.cpEnd,
|
|
333
348
|
terminator: range.terminator || '',
|
|
349
|
+
paragraphMark: markState && {
|
|
350
|
+
revisionDeleted: markState.revisionDeleted,
|
|
351
|
+
revisionInserted: markState.revisionInserted,
|
|
352
|
+
},
|
|
353
|
+
storyKind: range.storyKind,
|
|
334
354
|
text: paragraphText,
|
|
335
355
|
rawProperties: range.properties || [],
|
|
336
356
|
styleId: paraStyleId,
|
|
@@ -482,6 +502,8 @@ function paragraphToBlock(paragraph) {
|
|
|
482
502
|
paraState: paragraph.paraState,
|
|
483
503
|
inlines: paragraph.inlines,
|
|
484
504
|
text: paragraph.text,
|
|
505
|
+
paragraphMark: paragraph.paragraphMark,
|
|
506
|
+
storyKind: paragraph.storyKind,
|
|
485
507
|
};
|
|
486
508
|
}
|
|
487
509
|
function tableGridSignature(paragraph) {
|
package/dist/msdoc/properties.js
CHANGED
|
@@ -50,6 +50,8 @@ export function charPropsToState(properties) {
|
|
|
50
50
|
}
|
|
51
51
|
break;
|
|
52
52
|
case 'bold':
|
|
53
|
+
case 'revisionDeleted':
|
|
54
|
+
case 'revisionInserted':
|
|
53
55
|
case 'italic':
|
|
54
56
|
case 'strike':
|
|
55
57
|
case 'hidden':
|
|
@@ -110,6 +112,7 @@ export function paraPropsToState(properties) {
|
|
|
110
112
|
spacingBefore: 0,
|
|
111
113
|
spacingAfter: 0,
|
|
112
114
|
lineSpacing: 0,
|
|
115
|
+
lineSpacingRule: 'auto',
|
|
113
116
|
leftIndent: 0,
|
|
114
117
|
rightIndent: 0,
|
|
115
118
|
firstLineIndent: 0,
|
|
@@ -150,9 +153,18 @@ export function paraPropsToState(properties) {
|
|
|
150
153
|
case 'spacingAfter':
|
|
151
154
|
state.spacingAfter = prop.value || 0;
|
|
152
155
|
break;
|
|
153
|
-
case 'lineSpacing':
|
|
156
|
+
case 'lineSpacing': {
|
|
154
157
|
state.lineSpacing = prop.value || 0;
|
|
158
|
+
// LSPD's second short distinguishes 1/240-line multiples from signed twips.
|
|
159
|
+
const bytes = prop.operandBytes;
|
|
160
|
+
const absolute = bytes && bytes.length >= 4
|
|
161
|
+
? bytes[2] === 0 && bytes[3] === 0
|
|
162
|
+
: state.lineSpacing < 0;
|
|
163
|
+
state.lineSpacingRule = absolute
|
|
164
|
+
? state.lineSpacing < 0 ? 'exact' : 'atLeast'
|
|
165
|
+
: 'auto';
|
|
155
166
|
break;
|
|
167
|
+
}
|
|
156
168
|
case 'leftIndent':
|
|
157
169
|
state.leftIndent = prop.value || 0;
|
|
158
170
|
break;
|
package/dist/msdoc/sprm.js
CHANGED
|
@@ -486,6 +486,8 @@ export function decodeSprm(sprm, operandBytes) {
|
|
|
486
486
|
const bytes = operandBytes;
|
|
487
487
|
const raw = sprm;
|
|
488
488
|
switch (sprm) {
|
|
489
|
+
case SprmCodes.sprmCFRMarkDel: return setMeta('char', 'revisionDeleted', boolValue(bytes), raw, bytes);
|
|
490
|
+
case SprmCodes.sprmCFRMark: return setMeta('char', 'revisionInserted', boolValue(bytes), raw, bytes);
|
|
489
491
|
case SprmCodes.sprmCPicLocation: return setMeta('char', 'pictureOffset', u32(bytes, 0) >>> 0, raw, bytes);
|
|
490
492
|
case SprmCodes.sprmCFData: return setMeta('char', 'data', boolValue(bytes), raw, bytes);
|
|
491
493
|
case SprmCodes.sprmCFOle2: return setMeta('char', 'ole2', boolValue(bytes), raw, bytes);
|
|
@@ -512,10 +514,10 @@ export function decodeSprm(sprm, operandBytes) {
|
|
|
512
514
|
case SprmCodes.sprmCHpsBi: return setMeta('char', 'fontSizeHalfPoints', u16(bytes, 0), raw, bytes);
|
|
513
515
|
case SprmCodes.sprmCHpsPos: return setMeta('char', 'positionHalfPoints', i16(bytes, 0), raw, bytes);
|
|
514
516
|
case SprmCodes.sprmCSymbol: return setMeta('char', 'symbol', { font: u16(bytes, 0), charCode: u16(bytes, 2) }, raw, bytes);
|
|
515
|
-
case SprmCodes.sprmCRgFtc0:
|
|
516
|
-
case SprmCodes.sprmCRgFtc1:
|
|
517
|
-
case SprmCodes.sprmCRgFtc2:
|
|
518
|
-
case SprmCodes.sprmCFtcBi: return setMeta('char', '
|
|
517
|
+
case SprmCodes.sprmCRgFtc0: return setMeta('char', 'fontFamilyId', u16(bytes, 0), raw, bytes);
|
|
518
|
+
case SprmCodes.sprmCRgFtc1: return setMeta('char', 'fontFamilyEastAsiaId', u16(bytes, 0), raw, bytes);
|
|
519
|
+
case SprmCodes.sprmCRgFtc2: return setMeta('char', 'fontFamilyOtherId', u16(bytes, 0), raw, bytes);
|
|
520
|
+
case SprmCodes.sprmCFtcBi: return setMeta('char', 'fontFamilyBiId', u16(bytes, 0), raw, bytes);
|
|
519
521
|
case SprmCodes.sprmCCharScale: return setMeta('char', 'scale', u16(bytes, 0), raw, bytes);
|
|
520
522
|
case SprmCodes.sprmCFDStrike: return setMeta('char', 'doubleStrike', boolValue(bytes), raw, bytes);
|
|
521
523
|
case SprmCodes.sprmCFImprint: return setMeta('char', 'imprint', boolValue(bytes), raw, bytes);
|
package/dist/msdoc/styles.js
CHANGED
|
@@ -179,9 +179,11 @@ export function parseStyles(tableBytes, fibRgFcLcb) {
|
|
|
179
179
|
const stshiOffset = 2;
|
|
180
180
|
const cstd = reader.u16(stshiOffset + 0);
|
|
181
181
|
const cbSTDBaseInFile = reader.u16(stshiOffset + 2);
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
const
|
|
182
|
+
const fontAt = (offset) => cbStshi >= offset + 2 && bytes.length >= stshiOffset + offset + 2
|
|
183
|
+
? reader.i16(stshiOffset + offset) : -1;
|
|
184
|
+
const ftcAsci = fontAt(12);
|
|
185
|
+
const ftcFE = fontAt(14);
|
|
186
|
+
const ftcOther = fontAt(16);
|
|
185
187
|
const header = { cbStshi, cstd, cbSTDBaseInFile, ftcAsci, ftcFE, ftcOther };
|
|
186
188
|
let offset = 2 + cbStshi;
|
|
187
189
|
const styles = new Map();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const EAST_ASIAN = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Bopomofo}\u3000-\u303f\uff00-\uffef]/u;
|
|
2
|
+
const INHERIT_FONT = /[\p{Mark}\u200c\u200d]/u;
|
|
3
|
+
export function textFontRuns(text, style) {
|
|
4
|
+
if (!text)
|
|
5
|
+
return [];
|
|
6
|
+
if (style.rtl && style.fontFamilyBi)
|
|
7
|
+
return [{ text, fontFamily: style.fontFamilyBi }];
|
|
8
|
+
const base = style.fontFamily;
|
|
9
|
+
const eastAsia = style.fontFamilyEastAsia || base;
|
|
10
|
+
const other = style.fontFamilyOther || base;
|
|
11
|
+
if (eastAsia === base && other === base)
|
|
12
|
+
return [{ text, fontFamily: base }];
|
|
13
|
+
const runs = [];
|
|
14
|
+
for (const character of text) {
|
|
15
|
+
const previous = runs[runs.length - 1];
|
|
16
|
+
const fontFamily = previous && INHERIT_FONT.test(character) ? previous.fontFamily
|
|
17
|
+
: EAST_ASIAN.test(character) ? eastAsia
|
|
18
|
+
: character.codePointAt(0) < 0x80 ? base : other;
|
|
19
|
+
if (previous && previous.fontFamily === fontFamily)
|
|
20
|
+
previous.text += character;
|
|
21
|
+
else
|
|
22
|
+
runs.push({ text: character, fontFamily });
|
|
23
|
+
}
|
|
24
|
+
return runs;
|
|
25
|
+
}
|
package/dist/render/html.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { escapeHtml, slugify, twipsToPx } from '../core/utils.js';
|
|
2
2
|
import { HIGHLIGHT_COLORS } from '../msdoc/constants.js';
|
|
3
3
|
import { cssTextAlign, cssUnderline, cssVerticalAlign } from '../msdoc/properties.js';
|
|
4
|
+
import { textFontRuns } from './fonts.js';
|
|
4
5
|
const COLOR_INDEX_MAP = {
|
|
5
6
|
1: '#000000',
|
|
6
7
|
2: '#0000ff',
|
|
@@ -154,9 +155,14 @@ function paragraphStyleToCss(paraState) {
|
|
|
154
155
|
if (textIndent)
|
|
155
156
|
style['text-indent'] = `${textIndent}px`;
|
|
156
157
|
if (paraState.lineSpacing) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
if (paraState.lineSpacingRule === 'exact' || paraState.lineSpacingRule === 'atLeast') {
|
|
159
|
+
style['line-height'] = `${twipsToPx(Math.abs(paraState.lineSpacing))}px`;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
const multiple = Math.abs(paraState.lineSpacing) / 240;
|
|
163
|
+
// A font's single line includes its metrics and leading, not just a 1em box.
|
|
164
|
+
style['line-height'] = multiple === 1 ? 'normal' : String(multiple);
|
|
165
|
+
}
|
|
160
166
|
}
|
|
161
167
|
if (paraState.keepLines)
|
|
162
168
|
style['break-inside'] = 'avoid';
|
|
@@ -239,11 +245,30 @@ function inlineStyleToCss(styleState) {
|
|
|
239
245
|
return style;
|
|
240
246
|
}
|
|
241
247
|
function renderTextNode(node, context) {
|
|
242
|
-
|
|
248
|
+
if (context.reviewMode === 'final' && node.style.revisionDeleted)
|
|
249
|
+
return '';
|
|
250
|
+
if (context.reviewMode === 'original' && node.style.revisionInserted)
|
|
251
|
+
return '';
|
|
252
|
+
const content = textFontRuns(node.text, node.style).map(run => {
|
|
253
|
+
const text = escapeHtml(run.text);
|
|
254
|
+
if (!run.fontFamily || run.fontFamily === node.style.fontFamily)
|
|
255
|
+
return text;
|
|
256
|
+
const font = styleObjectToCss({ 'font-family': `${quoteCssString(run.fontFamily)},sans-serif` });
|
|
257
|
+
return `<span style="${font}">${text}</span>`;
|
|
258
|
+
}).join('');
|
|
243
259
|
const inlineStyle = inlineStyleToCss(node.style);
|
|
244
260
|
inlineStyle['white-space'] = 'break-spaces';
|
|
261
|
+
const revision = context.reviewMode === 'all'
|
|
262
|
+
? node.style.revisionDeleted ? 'delete' : node.style.revisionInserted ? 'insert' : null
|
|
263
|
+
: null;
|
|
264
|
+
if (revision) {
|
|
265
|
+
inlineStyle.color = '#c62828';
|
|
266
|
+
inlineStyle['text-decoration-line'] = revision === 'delete' ? 'line-through' : 'underline';
|
|
267
|
+
}
|
|
245
268
|
const style = styleObjectToCss(inlineStyle);
|
|
246
|
-
const
|
|
269
|
+
const tag = revision === 'delete' ? 'del' : revision === 'insert' ? 'ins' : 'span';
|
|
270
|
+
const change = revision ? ` data-msdoc-change="${revision}"` : '';
|
|
271
|
+
const inner = `<${tag}${change}${style ? ` style="${style}"` : ''}>${content}</${tag}>`;
|
|
247
272
|
return renderLink(inner, sanitizeMsDocLinkHref(node.href, context.externalLinkPolicy));
|
|
248
273
|
}
|
|
249
274
|
function inlineImageDisplaySizePx(asset) {
|
|
@@ -382,6 +407,8 @@ function renderAttachmentNode(node, context) {
|
|
|
382
407
|
}
|
|
383
408
|
function renderInlineNodes(nodes, context) {
|
|
384
409
|
return nodes.map((node) => {
|
|
410
|
+
if (hiddenRevision(node.style, context))
|
|
411
|
+
return '';
|
|
385
412
|
if (node.type === 'text')
|
|
386
413
|
return renderTextNode(node, context);
|
|
387
414
|
if (node.type === 'image')
|
|
@@ -395,12 +422,37 @@ function renderInlineNodes(nodes, context) {
|
|
|
395
422
|
return '';
|
|
396
423
|
}).join('');
|
|
397
424
|
}
|
|
425
|
+
function hiddenRevision(revision, context) {
|
|
426
|
+
return !!((context.reviewMode === 'final' && revision?.revisionDeleted)
|
|
427
|
+
|| (context.reviewMode === 'original' && revision?.revisionInserted));
|
|
428
|
+
}
|
|
429
|
+
function reviewParagraphs(paragraphs, context) {
|
|
430
|
+
const result = [];
|
|
431
|
+
for (const paragraph of paragraphs) {
|
|
432
|
+
const previous = result[result.length - 1];
|
|
433
|
+
if (previous && previous.storyKind === paragraph.storyKind
|
|
434
|
+
&& hiddenRevision(previous.paragraphMark, context)) {
|
|
435
|
+
// The surviving paragraph mark owns the merged paragraph's formatting.
|
|
436
|
+
result[result.length - 1] = {
|
|
437
|
+
...paragraph,
|
|
438
|
+
inlines: [...previous.inlines, ...paragraph.inlines],
|
|
439
|
+
text: previous.text + paragraph.text,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
result.push(paragraph);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return result;
|
|
447
|
+
}
|
|
398
448
|
function renderParagraphBlock(block, context, options = {}) {
|
|
399
449
|
const tag = options.inline ? 'div' : 'p';
|
|
400
450
|
const style = styleObjectToCss(paragraphStyleToCss(block.paraState));
|
|
401
451
|
const body = renderInlineNodes(block.inlines || [], context);
|
|
402
452
|
const empty = body || '<br>';
|
|
403
453
|
const classNames = ['msdoc-paragraph'];
|
|
454
|
+
if (block.paraState.lineSpacingRule === 'atLeast')
|
|
455
|
+
classNames.push('msdoc-line-at-least');
|
|
404
456
|
if (block.styleName)
|
|
405
457
|
classNames.push(`msdoc-style-${slugify(block.styleName)}`);
|
|
406
458
|
return `<${tag} class="${classNames.join(' ')}"${style ? ` style="${style}"` : ''}>${empty}</${tag}>`;
|
|
@@ -492,7 +544,8 @@ function renderTableBlock(block, context) {
|
|
|
492
544
|
if ((cell.rowspan ?? 1) > 1)
|
|
493
545
|
attrs.push(` rowspan="${cell.rowspan}"`);
|
|
494
546
|
const style = styleObjectToCss(cellStyle(cell));
|
|
495
|
-
const body = cell.paragraphs
|
|
547
|
+
const body = reviewParagraphs(cell.paragraphs, context)
|
|
548
|
+
.map((paragraph) => renderParagraphBlock(paragraph, context, { inline: true })).join('');
|
|
496
549
|
return `<td class="msdoc-cell"${attrs.join('')}${style ? ` style="${style}"` : ''}>${body || '<div class="msdoc-paragraph"><br></div>'}</td>`;
|
|
497
550
|
})
|
|
498
551
|
.join('');
|
|
@@ -516,6 +569,7 @@ export function defaultMsDocCss() {
|
|
|
516
569
|
.msdoc-root{box-sizing:border-box;max-width:100%;padding:24px;background:#fff;color:#111;font:14px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
|
|
517
570
|
.msdoc-root *{box-sizing:border-box}
|
|
518
571
|
.msdoc-paragraph{margin:0 0 8px;white-space:normal;word-break:break-word;overflow-wrap:anywhere}
|
|
572
|
+
.msdoc-line-at-least span,.msdoc-line-at-least ins,.msdoc-line-at-least del{line-height:normal}
|
|
519
573
|
.msdoc-paragraph:last-child{margin-bottom:0}
|
|
520
574
|
.msdoc-table{margin:12px 0;border-collapse:collapse;border-spacing:0;max-width:100%}
|
|
521
575
|
.msdoc-cell{padding:6px 8px;vertical-align:top;word-break:break-word;overflow-wrap:anywhere}
|
|
@@ -537,18 +591,29 @@ export function defaultMsDocCss() {
|
|
|
537
591
|
export function renderMsDoc(parsed, options = {}) {
|
|
538
592
|
const css = options.css ?? defaultMsDocCss();
|
|
539
593
|
const context = {
|
|
594
|
+
reviewMode: options.reviewMode ?? 'all',
|
|
540
595
|
externalLinkPolicy: options.externalLinkPolicy ?? 'block',
|
|
541
596
|
externalResourcePolicy: options.externalResourcePolicy ?? 'block',
|
|
542
597
|
};
|
|
543
|
-
const
|
|
544
|
-
|
|
545
|
-
|
|
598
|
+
const parts = [];
|
|
599
|
+
let paragraphs = [];
|
|
600
|
+
const flushParagraphs = () => {
|
|
601
|
+
parts.push(...reviewParagraphs(paragraphs, context).map(block => renderParagraphBlock(block, context)));
|
|
602
|
+
paragraphs = [];
|
|
603
|
+
};
|
|
604
|
+
for (const block of parsed.blocks) {
|
|
605
|
+
if (block.type === 'paragraph') {
|
|
606
|
+
paragraphs.push(block);
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
flushParagraphs();
|
|
546
610
|
if (block.type === 'table')
|
|
547
|
-
|
|
611
|
+
parts.push(renderTableBlock(block, context));
|
|
548
612
|
if (block.type === 'attachments')
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
613
|
+
parts.push(renderAttachmentsBlock(block));
|
|
614
|
+
}
|
|
615
|
+
flushParagraphs();
|
|
616
|
+
const html = parts.join('');
|
|
552
617
|
return {
|
|
553
618
|
html,
|
|
554
619
|
css,
|
package/dist/types.d.ts
CHANGED
|
@@ -246,6 +246,8 @@ export interface SymbolInfo {
|
|
|
246
246
|
[key: string]: unknown;
|
|
247
247
|
}
|
|
248
248
|
export interface CharState {
|
|
249
|
+
revisionDeleted?: boolean;
|
|
250
|
+
revisionInserted?: boolean;
|
|
249
251
|
bold: boolean;
|
|
250
252
|
italic: boolean;
|
|
251
253
|
strike: boolean;
|
|
@@ -253,6 +255,12 @@ export interface CharState {
|
|
|
253
255
|
fontSizeHalfPoints?: number;
|
|
254
256
|
fontFamilyId?: number;
|
|
255
257
|
fontFamily?: string;
|
|
258
|
+
fontFamilyEastAsiaId?: number;
|
|
259
|
+
fontFamilyEastAsia?: string;
|
|
260
|
+
fontFamilyOtherId?: number;
|
|
261
|
+
fontFamilyOther?: string;
|
|
262
|
+
fontFamilyBiId?: number;
|
|
263
|
+
fontFamilyBi?: string;
|
|
256
264
|
colorIndex?: number;
|
|
257
265
|
highlight?: HighlightInfo | number;
|
|
258
266
|
spacing: number;
|
|
@@ -293,6 +301,7 @@ export interface ParaState {
|
|
|
293
301
|
spacingBefore: number;
|
|
294
302
|
spacingAfter: number;
|
|
295
303
|
lineSpacing: number;
|
|
304
|
+
lineSpacingRule?: 'auto' | 'atLeast' | 'exact';
|
|
296
305
|
leftIndent: number;
|
|
297
306
|
rightIndent: number;
|
|
298
307
|
firstLineIndent: number;
|
|
@@ -552,9 +561,11 @@ export interface AttachmentInlineNode {
|
|
|
552
561
|
}
|
|
553
562
|
export interface LineBreakInlineNode {
|
|
554
563
|
type: 'lineBreak';
|
|
564
|
+
style?: CharState;
|
|
555
565
|
}
|
|
556
566
|
export interface PageBreakInlineNode {
|
|
557
567
|
type: 'pageBreak';
|
|
568
|
+
style?: CharState;
|
|
558
569
|
}
|
|
559
570
|
export type InlineNode = TextInlineNode | ImageInlineNode | AttachmentInlineNode | LineBreakInlineNode | PageBreakInlineNode;
|
|
560
571
|
export interface ParagraphBlock {
|
|
@@ -565,6 +576,8 @@ export interface ParagraphBlock {
|
|
|
565
576
|
paraState: ParaState;
|
|
566
577
|
inlines: InlineNode[];
|
|
567
578
|
text: string;
|
|
579
|
+
paragraphMark?: Pick<CharState, 'revisionDeleted' | 'revisionInserted'>;
|
|
580
|
+
storyKind?: string;
|
|
568
581
|
}
|
|
569
582
|
export interface TableCellBlock {
|
|
570
583
|
id: string;
|
|
@@ -600,6 +613,8 @@ export interface ParagraphModel {
|
|
|
600
613
|
cpStart: number;
|
|
601
614
|
cpEnd: number;
|
|
602
615
|
terminator: string;
|
|
616
|
+
paragraphMark?: Pick<CharState, 'revisionDeleted' | 'revisionInserted'>;
|
|
617
|
+
storyKind?: string;
|
|
603
618
|
text: string;
|
|
604
619
|
rawProperties: DecodedProperty[];
|
|
605
620
|
styleId: number;
|
|
@@ -617,6 +632,8 @@ export interface MsDocParseOptions {
|
|
|
617
632
|
}
|
|
618
633
|
export interface MsDocRenderOptions {
|
|
619
634
|
css?: string;
|
|
635
|
+
/** Preserve insertion/deletion marks by default; explicitly select final or original text. */
|
|
636
|
+
reviewMode?: 'all' | 'final' | 'original';
|
|
620
637
|
/** External links are blocked by default; internal document bookmarks remain active. */
|
|
621
638
|
externalLinkPolicy?: 'allow' | 'block';
|
|
622
639
|
/** Linked image resources are blocked by default; embedded data/blob images remain available. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@file-viewer/doc",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
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.",
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
},
|
|
28
28
|
"repository": {
|
|
29
29
|
"type": "git",
|
|
30
|
-
"url": "git+https://github.com/flyfish-dev/
|
|
30
|
+
"url": "git+https://github.com/flyfish-dev/file-viewer.git",
|
|
31
31
|
"directory": "packages/renderers/doc"
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://github.com/flyfish-dev/docjs",
|
|
34
34
|
"bugs": {
|
|
35
|
-
"url": "https://github.com/flyfish-dev/
|
|
35
|
+
"url": "https://github.com/flyfish-dev/file-viewer/issues"
|
|
36
36
|
},
|
|
37
37
|
"funding": {
|
|
38
38
|
"type": "individual",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"LICENSE"
|
|
63
63
|
],
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"dompurify": "
|
|
65
|
+
"dompurify": "3.4.15"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"typescript": "^6.0.3"
|