@file-viewer/doc 2.1.25 → 2.1.27
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.d.ts +2 -1
- package/dist/msdoc/parser.js +60 -9
- package/dist/msdoc/properties.js +130 -12
- package/dist/msdoc/sprm.d.ts +6 -1
- package/dist/msdoc/sprm.js +191 -44
- package/dist/msdoc/styles.js +21 -1
- package/dist/render/html.js +103 -21
- package/dist/types.d.ts +30 -6
- package/package.json +2 -1
package/dist/msdoc/parser.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { MsDocParseOptions, MsDocParseResult } from '../types.js';
|
|
1
|
+
import type { MsDocParseOptions, MsDocParseResult, TableRowBlock } from '../types.js';
|
|
2
|
+
export declare function finalizeTableGrid(rows: TableRowBlock[]): void;
|
|
2
3
|
/**
|
|
3
4
|
* Main MS-DOC entry point.
|
|
4
5
|
* It parses the OLE container, restores text through the piece table, resolves
|
package/dist/msdoc/parser.js
CHANGED
|
@@ -283,26 +283,41 @@ function buildParagraphModel(range, paragraphText, styles, fonts, chpxRuns, reso
|
|
|
283
283
|
inlines,
|
|
284
284
|
};
|
|
285
285
|
}
|
|
286
|
-
function finalizeTableGrid(rows) {
|
|
286
|
+
export function finalizeTableGrid(rows) {
|
|
287
287
|
for (const row of rows) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const merge = cell.meta?.merge || 0;
|
|
291
|
-
cell.colIndex = i;
|
|
288
|
+
row.cells.forEach((cell, index) => {
|
|
289
|
+
cell.colIndex = index;
|
|
292
290
|
cell.colspan = 1;
|
|
293
291
|
cell.rowspan = 1;
|
|
294
292
|
cell.hidden = false;
|
|
293
|
+
});
|
|
294
|
+
for (let i = 0; i < row.cells.length; i += 1) {
|
|
295
|
+
const cell = row.cells[i];
|
|
296
|
+
const merge = cell.meta?.merge || 0;
|
|
295
297
|
if (merge === 1) {
|
|
296
|
-
cell.hidden = true;
|
|
297
298
|
continue;
|
|
298
299
|
}
|
|
299
300
|
if (merge > 1) {
|
|
300
301
|
let j = i + 1;
|
|
301
302
|
while (j < row.cells.length && (row.cells[j].meta?.merge || 0) === 1) {
|
|
302
303
|
row.cells[j].hidden = true;
|
|
303
|
-
cell.colspan
|
|
304
|
+
cell.colspan = (cell.colspan || 1) + 1;
|
|
304
305
|
j += 1;
|
|
305
306
|
}
|
|
307
|
+
const lastCell = row.cells[j - 1];
|
|
308
|
+
if (cell.meta && lastCell?.meta && lastCell !== cell) {
|
|
309
|
+
cell.meta.rightBoundary = lastCell.meta.rightBoundary;
|
|
310
|
+
if (cell.meta.leftBoundary != null && cell.meta.rightBoundary != null) {
|
|
311
|
+
cell.meta.width = Math.max(0, cell.meta.rightBoundary - cell.meta.leftBoundary);
|
|
312
|
+
}
|
|
313
|
+
const rightBorder = lastCell.meta.borders?.right;
|
|
314
|
+
if (rightBorder) {
|
|
315
|
+
cell.meta.borders = {
|
|
316
|
+
...(cell.meta.borders || {}),
|
|
317
|
+
right: rightBorder,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
}
|
|
306
321
|
}
|
|
307
322
|
}
|
|
308
323
|
}
|
|
@@ -313,7 +328,6 @@ function finalizeTableGrid(rows) {
|
|
|
313
328
|
continue;
|
|
314
329
|
const vertMerge = cell.meta?.vertMerge || 0;
|
|
315
330
|
if (vertMerge === 1) {
|
|
316
|
-
cell.hidden = true;
|
|
317
331
|
continue;
|
|
318
332
|
}
|
|
319
333
|
if (vertMerge > 1) {
|
|
@@ -335,10 +349,46 @@ function finalizeTableGrid(rows) {
|
|
|
335
349
|
cell.rowspan = (cell.rowspan || 1) + 1;
|
|
336
350
|
nextIndex += 1;
|
|
337
351
|
}
|
|
352
|
+
const bottomCell = rows[nextIndex - 1]?.cells[cell.colIndex || 0];
|
|
353
|
+
const bottomBorder = bottomCell?.meta?.borders?.bottom;
|
|
354
|
+
if (cell.meta && bottomCell?.meta && bottomCell !== cell && bottomBorder) {
|
|
355
|
+
cell.meta.borders = {
|
|
356
|
+
...(cell.meta.borders || {}),
|
|
357
|
+
bottom: bottomBorder,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
338
360
|
}
|
|
339
361
|
}
|
|
340
362
|
}
|
|
341
363
|
}
|
|
364
|
+
function resolveTableGridBorders(rows) {
|
|
365
|
+
const hasOwnBorder = (borders, side) => (Object.prototype.hasOwnProperty.call(borders, side) && (() => {
|
|
366
|
+
const border = borders[side];
|
|
367
|
+
// An all-zero BRC is an empty cell-level value and still inherits the
|
|
368
|
+
// table edge/inside border. NilBrc is the explicit "no border" value.
|
|
369
|
+
return Boolean(border?.nil || border?.borderType);
|
|
370
|
+
})());
|
|
371
|
+
rows.forEach((row, rowIndex) => {
|
|
372
|
+
row.cells.forEach((cell) => {
|
|
373
|
+
if (cell.hidden || !cell.meta)
|
|
374
|
+
return;
|
|
375
|
+
const explicit = { ...(cell.meta.borders || {}) };
|
|
376
|
+
const table = row.state.borders || {};
|
|
377
|
+
const startColumn = cell.colIndex || 0;
|
|
378
|
+
const endColumn = startColumn + (cell.colspan || 1);
|
|
379
|
+
const endRow = rowIndex + (cell.rowspan || 1);
|
|
380
|
+
const assignFallback = (side, border) => {
|
|
381
|
+
if (!hasOwnBorder(explicit, side) && border)
|
|
382
|
+
explicit[side] = border;
|
|
383
|
+
};
|
|
384
|
+
assignFallback('top', rowIndex === 0 ? table.top : table.horizontalInside);
|
|
385
|
+
assignFallback('bottom', endRow >= rows.length ? table.bottom : table.horizontalInside);
|
|
386
|
+
assignFallback('left', startColumn === 0 ? table.left : table.verticalInside);
|
|
387
|
+
assignFallback('right', endColumn >= row.cells.length ? table.right : table.verticalInside);
|
|
388
|
+
cell.meta.borders = explicit;
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
}
|
|
342
392
|
function paragraphToBlock(paragraph) {
|
|
343
393
|
return {
|
|
344
394
|
type: 'paragraph',
|
|
@@ -353,7 +403,7 @@ function paragraphToBlock(paragraph) {
|
|
|
353
403
|
function normalizeRowEndOnlyTables(paragraphs) {
|
|
354
404
|
for (let index = 0; index < paragraphs.length; index += 1) {
|
|
355
405
|
const rowEnd = paragraphs[index];
|
|
356
|
-
const cellCount = rowEnd.tableState.
|
|
406
|
+
const cellCount = applyTableStateToCells(rowEnd.tableState).length;
|
|
357
407
|
if (!rowEnd.paraState.tableRowEnd || !cellCount)
|
|
358
408
|
continue;
|
|
359
409
|
rowEnd.paraState.inTable = true;
|
|
@@ -416,6 +466,7 @@ function buildTableBlock(tableParagraphs) {
|
|
|
416
466
|
rows.push({ id: uniqueId('row'), cells: pendingRow.cells, state: tableParagraphs[0]?.tableState || tablePropsToState([]), gridWidthTwips: 0 });
|
|
417
467
|
}
|
|
418
468
|
finalizeTableGrid(rows);
|
|
469
|
+
resolveTableGridBorders(rows);
|
|
419
470
|
const gridWidthTwips = rows.find((row) => row.gridWidthTwips)?.gridWidthTwips || 0;
|
|
420
471
|
const depth = Math.max(...tableParagraphs.map((paragraph) => getTableDepth(paragraph.paraState)), 1);
|
|
421
472
|
return {
|
package/dist/msdoc/properties.js
CHANGED
|
@@ -251,6 +251,7 @@ export function tablePropsToState(properties) {
|
|
|
251
251
|
autoFit: undefined,
|
|
252
252
|
widthBefore: undefined,
|
|
253
253
|
widthAfter: undefined,
|
|
254
|
+
borders: {},
|
|
254
255
|
defTable: undefined,
|
|
255
256
|
operations: [],
|
|
256
257
|
};
|
|
@@ -303,6 +304,9 @@ export function tablePropsToState(properties) {
|
|
|
303
304
|
case 'widthAfter':
|
|
304
305
|
state.widthAfter = prop.value;
|
|
305
306
|
break;
|
|
307
|
+
case 'tableBorders':
|
|
308
|
+
state.borders = prop.value || {};
|
|
309
|
+
break;
|
|
306
310
|
case 'defTable':
|
|
307
311
|
state.defTable = prop.value;
|
|
308
312
|
break;
|
|
@@ -337,11 +341,11 @@ export function rangeApply(list, range, callback) {
|
|
|
337
341
|
}
|
|
338
342
|
export function applyTableStateToCells(tableState) {
|
|
339
343
|
const def = tableState?.defTable;
|
|
340
|
-
|
|
341
|
-
return [];
|
|
342
|
-
const cells = def.cells.map((cell, index) => ({
|
|
344
|
+
const cells = (def?.cells || []).map((cell, index) => ({
|
|
343
345
|
index,
|
|
344
|
-
width:
|
|
346
|
+
width: def?.rgdxaCenter?.[index + 1] != null && def?.rgdxaCenter?.[index] != null
|
|
347
|
+
? Math.max(0, def.rgdxaCenter[index + 1] - def.rgdxaCenter[index])
|
|
348
|
+
: cell?.wWidth,
|
|
345
349
|
ftsWidth: cell?.tcgrf?.ftsWidth,
|
|
346
350
|
borders: (cell?.borders || {}),
|
|
347
351
|
merge: cell?.tcgrf?.horzMerge || 0,
|
|
@@ -351,11 +355,52 @@ export function applyTableStateToCells(tableState) {
|
|
|
351
355
|
noWrap: Boolean(cell?.tcgrf?.noWrap),
|
|
352
356
|
hideMark: Boolean(cell?.tcgrf?.hideMark),
|
|
353
357
|
textFlow: cell?.tcgrf?.textFlow || 0,
|
|
354
|
-
rightBoundary: def
|
|
355
|
-
leftBoundary: def
|
|
358
|
+
rightBoundary: def?.rgdxaCenter?.[index + 1],
|
|
359
|
+
leftBoundary: def?.rgdxaCenter?.[index],
|
|
356
360
|
}));
|
|
361
|
+
let geometryChanged = false;
|
|
362
|
+
const initialLeftBoundary = cells[0]?.leftBoundary || 0;
|
|
357
363
|
for (const op of tableState.operations || []) {
|
|
358
364
|
switch (op.name) {
|
|
365
|
+
case 'insertCells': {
|
|
366
|
+
// Word commonly emits TDefTable for legacy readers and TInsert for
|
|
367
|
+
// readers that process sprmPTableProps. They are alternative cell
|
|
368
|
+
// definitions, not cumulative instructions.
|
|
369
|
+
if (def?.cells?.length)
|
|
370
|
+
break;
|
|
371
|
+
const value = op.value;
|
|
372
|
+
const first = Math.min(cells.length, Math.max(0, value?.itcFirst || 0));
|
|
373
|
+
const count = Math.min(63 - cells.length, Math.max(0, value?.ctc || 0));
|
|
374
|
+
const width = Math.max(0, value?.dxaCol || 0);
|
|
375
|
+
if (count) {
|
|
376
|
+
cells.splice(first, 0, ...Array.from({ length: count }, (_, offset) => ({
|
|
377
|
+
index: first + offset,
|
|
378
|
+
width,
|
|
379
|
+
borders: {},
|
|
380
|
+
merge: 0,
|
|
381
|
+
vertMerge: 0,
|
|
382
|
+
vertAlign: 0,
|
|
383
|
+
fitText: false,
|
|
384
|
+
noWrap: false,
|
|
385
|
+
hideMark: false,
|
|
386
|
+
textFlow: 0,
|
|
387
|
+
})));
|
|
388
|
+
geometryChanged = true;
|
|
389
|
+
}
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
case 'deleteCells': {
|
|
393
|
+
const range = op.value;
|
|
394
|
+
if (range) {
|
|
395
|
+
const first = Math.max(0, range.first || 0);
|
|
396
|
+
const count = Math.max(0, (range.lim || first) - first);
|
|
397
|
+
if (count) {
|
|
398
|
+
cells.splice(first, count);
|
|
399
|
+
geometryChanged = true;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
359
404
|
case 'merge':
|
|
360
405
|
rangeApply(cells, op.value, (cell, idx) => {
|
|
361
406
|
const range = op.value;
|
|
@@ -369,25 +414,88 @@ export function applyTableStateToCells(tableState) {
|
|
|
369
414
|
rangeApply(cells, op.value, (cell) => { cell.merge = 0; });
|
|
370
415
|
break;
|
|
371
416
|
case 'cellWidth':
|
|
372
|
-
case 'columnWidth':
|
|
373
417
|
rangeApply(cells, op.value.range, (cell) => {
|
|
374
418
|
const value = op.value;
|
|
375
|
-
cell.width = value.width;
|
|
376
419
|
cell.ftsWidth = value.ftsWidth;
|
|
420
|
+
// ftsNil and ftsAuto carry no usable preferred width. Preserve the
|
|
421
|
+
// TDefTable boundary width instead of collapsing the cell to zero.
|
|
422
|
+
if ((value.ftsWidth === 2 || value.ftsWidth === 3) && value.width != null) {
|
|
423
|
+
cell.width = value.width;
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
break;
|
|
427
|
+
case 'columnWidth':
|
|
428
|
+
rangeApply(cells, op.value.range, (cell) => {
|
|
429
|
+
cell.width = Math.max(0, op.value.width || 0);
|
|
430
|
+
geometryChanged = true;
|
|
377
431
|
});
|
|
378
432
|
break;
|
|
379
|
-
case 'vertMerge':
|
|
380
|
-
|
|
433
|
+
case 'vertMerge': {
|
|
434
|
+
const value = op.value;
|
|
435
|
+
const cell = value?.index != null ? cells[value.index] : undefined;
|
|
436
|
+
if (cell)
|
|
437
|
+
cell.vertMerge = value?.value || 0;
|
|
381
438
|
break;
|
|
439
|
+
}
|
|
382
440
|
case 'vertAlign':
|
|
383
441
|
rangeApply(cells, op.value.range, (cell) => { cell.vertAlign = op.value.value; });
|
|
384
442
|
break;
|
|
385
443
|
case 'setBorder':
|
|
386
|
-
rangeApply(cells, op.value.range, (cell) => {
|
|
444
|
+
rangeApply(cells, op.value.range, (cell) => {
|
|
445
|
+
const value = op.value;
|
|
446
|
+
const borders = { ...(cell.borders || {}) };
|
|
447
|
+
if (value.bordersToApply & 0x01)
|
|
448
|
+
borders.top = value.border;
|
|
449
|
+
if (value.bordersToApply & 0x02)
|
|
450
|
+
borders.left = value.border;
|
|
451
|
+
if (value.bordersToApply & 0x04)
|
|
452
|
+
borders.bottom = value.border;
|
|
453
|
+
if (value.bordersToApply & 0x08)
|
|
454
|
+
borders.right = value.border;
|
|
455
|
+
if (value.bordersToApply & 0x10)
|
|
456
|
+
borders.diagonalDown = value.border;
|
|
457
|
+
if (value.bordersToApply & 0x20)
|
|
458
|
+
borders.diagonalUp = value.border;
|
|
459
|
+
cell.borders = borders;
|
|
460
|
+
});
|
|
387
461
|
break;
|
|
388
462
|
case 'setShading':
|
|
389
|
-
rangeApply(cells, op.value.range, (cell) => {
|
|
463
|
+
rangeApply(cells, op.value.range, (cell, index) => {
|
|
464
|
+
const value = op.value;
|
|
465
|
+
if (!value.odd || (index - value.range.first) % 2 === 0)
|
|
466
|
+
cell.shading = value.shading;
|
|
467
|
+
});
|
|
390
468
|
break;
|
|
469
|
+
case 'cellPadding':
|
|
470
|
+
case 'cellSpacing': {
|
|
471
|
+
const value = op.value;
|
|
472
|
+
const isTwips = value?.ftsWidth === 3 || (op.name === 'cellSpacing' && value?.ftsWidth === 0x13);
|
|
473
|
+
if (!value?.range || !isTwips || value.width == null)
|
|
474
|
+
break;
|
|
475
|
+
rangeApply(cells, value.range, (cell) => {
|
|
476
|
+
const key = op.name === 'cellPadding' ? 'paddingTwips' : 'spacingTwips';
|
|
477
|
+
const sides = { ...(cell[key] || {}) };
|
|
478
|
+
if ((value.sides || 0) & 0x01)
|
|
479
|
+
sides.top = value.width;
|
|
480
|
+
if ((value.sides || 0) & 0x02)
|
|
481
|
+
sides.left = value.width;
|
|
482
|
+
if ((value.sides || 0) & 0x04)
|
|
483
|
+
sides.bottom = value.width;
|
|
484
|
+
if ((value.sides || 0) & 0x08)
|
|
485
|
+
sides.right = value.width;
|
|
486
|
+
cell[key] = sides;
|
|
487
|
+
});
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
case 'defaultShading': {
|
|
491
|
+
const value = op.value;
|
|
492
|
+
(value?.values || []).forEach((shading, offset) => {
|
|
493
|
+
const cell = cells[(value?.start || 0) + offset];
|
|
494
|
+
if (cell)
|
|
495
|
+
cell.shading = shading;
|
|
496
|
+
});
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
391
499
|
case 'fitText':
|
|
392
500
|
rangeApply(cells, op.value.range, (cell) => { cell.fitText = Boolean(op.value.value); });
|
|
393
501
|
break;
|
|
@@ -401,5 +509,15 @@ export function applyTableStateToCells(tableState) {
|
|
|
401
509
|
break;
|
|
402
510
|
}
|
|
403
511
|
}
|
|
512
|
+
if (geometryChanged) {
|
|
513
|
+
let boundary = initialLeftBoundary;
|
|
514
|
+
cells.forEach((cell) => {
|
|
515
|
+
const width = Math.max(0, cell.width || 0);
|
|
516
|
+
cell.leftBoundary = boundary;
|
|
517
|
+
boundary += width;
|
|
518
|
+
cell.rightBoundary = boundary;
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
cells.forEach((cell, index) => { cell.index = index; });
|
|
404
522
|
return cells;
|
|
405
523
|
}
|
package/dist/msdoc/sprm.d.ts
CHANGED
|
@@ -126,6 +126,7 @@ export declare const SprmCodes: {
|
|
|
126
126
|
readonly sprmTTableBorders: 54803;
|
|
127
127
|
readonly sprmTTableWidth: 62996;
|
|
128
128
|
readonly sprmTFAutofit: 13845;
|
|
129
|
+
readonly sprmTDefTableShd2nd: 54806;
|
|
129
130
|
readonly sprmTWidthBefore: 62999;
|
|
130
131
|
readonly sprmTWidthAfter: 63000;
|
|
131
132
|
readonly sprmTSetBrc80: 54816;
|
|
@@ -141,11 +142,15 @@ export declare const SprmCodes: {
|
|
|
141
142
|
readonly sprmTSetShdOdd: 54830;
|
|
142
143
|
readonly sprmTSetBrc: 54831;
|
|
143
144
|
readonly sprmTCellPadding: 54834;
|
|
144
|
-
readonly
|
|
145
|
+
readonly sprmTCellSpacingDefault: 54835;
|
|
146
|
+
readonly sprmTCellPaddingDefault: 54836;
|
|
145
147
|
readonly sprmTCellWidth: 54837;
|
|
146
148
|
readonly sprmTFitText: 63030;
|
|
147
149
|
readonly sprmTFCellNoWrap: 54841;
|
|
148
150
|
readonly sprmTIstd: 22074;
|
|
151
|
+
readonly sprmTDefTableShdRaw: 54896;
|
|
152
|
+
readonly sprmTDefTableShdRaw2nd: 54897;
|
|
153
|
+
readonly sprmTDefTableShdRaw3rd: 54898;
|
|
149
154
|
};
|
|
150
155
|
export declare function getSprmGroup(sprm: number): number;
|
|
151
156
|
export declare function getSprmOperandLength(buffer: Uint8Array, offset: number, sprm: number): number;
|
package/dist/msdoc/sprm.js
CHANGED
|
@@ -126,6 +126,7 @@ export const SprmCodes = {
|
|
|
126
126
|
sprmTTableBorders: 0xd613,
|
|
127
127
|
sprmTTableWidth: 0xf614,
|
|
128
128
|
sprmTFAutofit: 0x3615,
|
|
129
|
+
sprmTDefTableShd2nd: 0xd616,
|
|
129
130
|
sprmTWidthBefore: 0xf617,
|
|
130
131
|
sprmTWidthAfter: 0xf618,
|
|
131
132
|
sprmTSetBrc80: 0xd620,
|
|
@@ -141,11 +142,15 @@ export const SprmCodes = {
|
|
|
141
142
|
sprmTSetShdOdd: 0xd62e,
|
|
142
143
|
sprmTSetBrc: 0xd62f,
|
|
143
144
|
sprmTCellPadding: 0xd632,
|
|
144
|
-
|
|
145
|
+
sprmTCellSpacingDefault: 0xd633,
|
|
146
|
+
sprmTCellPaddingDefault: 0xd634,
|
|
145
147
|
sprmTCellWidth: 0xd635,
|
|
146
148
|
sprmTFitText: 0xf636,
|
|
147
149
|
sprmTFCellNoWrap: 0xd639,
|
|
148
150
|
sprmTIstd: 0x563a,
|
|
151
|
+
sprmTDefTableShdRaw: 0xd670,
|
|
152
|
+
sprmTDefTableShdRaw2nd: 0xd671,
|
|
153
|
+
sprmTDefTableShdRaw3rd: 0xd672,
|
|
149
154
|
};
|
|
150
155
|
const VARIABLE_OPERAND_CODES = new Set([
|
|
151
156
|
SprmCodes.sprmPChgTabs,
|
|
@@ -156,17 +161,20 @@ const VARIABLE_OPERAND_CODES = new Set([
|
|
|
156
161
|
SprmCodes.sprmTDefTableShd,
|
|
157
162
|
SprmCodes.sprmTDefTableShd80,
|
|
158
163
|
SprmCodes.sprmTDefTableShd3rd,
|
|
164
|
+
SprmCodes.sprmTDefTableShd2nd,
|
|
165
|
+
SprmCodes.sprmTDefTableShdRaw,
|
|
166
|
+
SprmCodes.sprmTDefTableShdRaw2nd,
|
|
167
|
+
SprmCodes.sprmTDefTableShdRaw3rd,
|
|
159
168
|
SprmCodes.sprmTSetBrc80,
|
|
160
169
|
SprmCodes.sprmTSetBrc,
|
|
161
170
|
SprmCodes.sprmTSetShd,
|
|
162
171
|
SprmCodes.sprmTSetShdOdd,
|
|
163
172
|
SprmCodes.sprmTCellPadding,
|
|
173
|
+
SprmCodes.sprmTCellSpacingDefault,
|
|
164
174
|
SprmCodes.sprmTCellPaddingDefault,
|
|
165
175
|
SprmCodes.sprmTCellWidth,
|
|
166
176
|
SprmCodes.sprmTVertAlign,
|
|
167
177
|
SprmCodes.sprmTVertMerge,
|
|
168
|
-
SprmCodes.sprmTTextFlow,
|
|
169
|
-
SprmCodes.sprmTDxaCol,
|
|
170
178
|
]);
|
|
171
179
|
export function getSprmGroup(sprm) {
|
|
172
180
|
return (sprm >> 13) & 0x7;
|
|
@@ -198,10 +206,38 @@ function parseBrc80(bytes, offset = 0) {
|
|
|
198
206
|
if (offset + 4 > bytes.length)
|
|
199
207
|
return null;
|
|
200
208
|
const brc = u32(bytes, offset) >>> 0;
|
|
209
|
+
if (brc === 0xffffffff)
|
|
210
|
+
return { raw: brc, nil: true };
|
|
201
211
|
const lineWidth = brc & 0xff;
|
|
202
212
|
const borderType = (brc >> 8) & 0xff;
|
|
203
213
|
const color = (brc >> 16) & 0xff;
|
|
204
|
-
|
|
214
|
+
const flags = (brc >>> 24) & 0xff;
|
|
215
|
+
return {
|
|
216
|
+
raw: brc,
|
|
217
|
+
lineWidth,
|
|
218
|
+
borderType,
|
|
219
|
+
color,
|
|
220
|
+
space: flags & 0x1f,
|
|
221
|
+
shadow: Boolean(flags & 0x20),
|
|
222
|
+
frame: Boolean(flags & 0x40),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function parseBrc(bytes, offset = 0) {
|
|
226
|
+
if (offset + 8 > bytes.length)
|
|
227
|
+
return null;
|
|
228
|
+
const colorRef = u32(bytes, offset) >>> 0;
|
|
229
|
+
const packed = u32(bytes, offset + 4) >>> 0;
|
|
230
|
+
if (packed === 0xffffffff)
|
|
231
|
+
return { raw: packed, colorRef, nil: true };
|
|
232
|
+
return {
|
|
233
|
+
raw: packed,
|
|
234
|
+
colorRef,
|
|
235
|
+
lineWidth: packed & 0xff,
|
|
236
|
+
borderType: (packed >>> 8) & 0xff,
|
|
237
|
+
space: (packed >>> 16) & 0x1f,
|
|
238
|
+
shadow: Boolean(packed & (1 << 21)),
|
|
239
|
+
frame: Boolean(packed & (1 << 22)),
|
|
240
|
+
};
|
|
205
241
|
}
|
|
206
242
|
function parseTcgrf(bytes, offset = 0) {
|
|
207
243
|
const value = u16(bytes, offset);
|
|
@@ -231,6 +267,63 @@ function parseTc80(bytes, offset = 0) {
|
|
|
231
267
|
},
|
|
232
268
|
};
|
|
233
269
|
}
|
|
270
|
+
function parseShd80(bytes, offset = 0) {
|
|
271
|
+
if (offset + 2 > bytes.length)
|
|
272
|
+
return null;
|
|
273
|
+
const raw = u16(bytes, offset);
|
|
274
|
+
const foregroundColorIndex = raw & 0x1f;
|
|
275
|
+
const backgroundColorIndex = (raw >>> 5) & 0x1f;
|
|
276
|
+
const pattern = (raw >>> 10) & 0x3f;
|
|
277
|
+
return {
|
|
278
|
+
foregroundColorIndex,
|
|
279
|
+
backgroundColorIndex,
|
|
280
|
+
pattern,
|
|
281
|
+
auto: pattern === 0,
|
|
282
|
+
nil: raw === 0xffff,
|
|
283
|
+
legacy: true,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function parseShd(bytes, offset = 0) {
|
|
287
|
+
if (offset + 10 > bytes.length)
|
|
288
|
+
return null;
|
|
289
|
+
const foregroundColorRef = u32(bytes, offset) >>> 0;
|
|
290
|
+
const backgroundColorRef = u32(bytes, offset + 4) >>> 0;
|
|
291
|
+
const pattern = u16(bytes, offset + 8);
|
|
292
|
+
return {
|
|
293
|
+
foregroundColorRef,
|
|
294
|
+
backgroundColorRef,
|
|
295
|
+
pattern,
|
|
296
|
+
auto: pattern === 0,
|
|
297
|
+
nil: pattern === 0xffff,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function parseTableBorders(bytes, legacy) {
|
|
301
|
+
const borderSize = legacy ? 4 : 8;
|
|
302
|
+
if (!bytes.length || bytes.length < 1 + borderSize * 6)
|
|
303
|
+
return null;
|
|
304
|
+
const parse = legacy ? parseBrc80 : parseBrc;
|
|
305
|
+
const names = [
|
|
306
|
+
'top', 'left', 'bottom', 'right', 'horizontalInside', 'verticalInside',
|
|
307
|
+
];
|
|
308
|
+
const borders = {};
|
|
309
|
+
names.forEach((name, index) => {
|
|
310
|
+
const border = parse(bytes, 1 + index * borderSize);
|
|
311
|
+
if (border)
|
|
312
|
+
borders[name] = border;
|
|
313
|
+
});
|
|
314
|
+
return borders;
|
|
315
|
+
}
|
|
316
|
+
function parseDefaultShading(bytes, start, legacy) {
|
|
317
|
+
const size = legacy ? 2 : 10;
|
|
318
|
+
const count = Math.floor(Math.min(bytes[0] ?? 0, Math.max(0, bytes.length - 1)) / size);
|
|
319
|
+
const values = [];
|
|
320
|
+
for (let index = 0; index < count; index += 1) {
|
|
321
|
+
const shading = legacy ? parseShd80(bytes, 1 + index * size) : parseShd(bytes, 1 + index * size);
|
|
322
|
+
if (shading)
|
|
323
|
+
values.push(shading);
|
|
324
|
+
}
|
|
325
|
+
return { start, values };
|
|
326
|
+
}
|
|
234
327
|
function parseTDefTableOperand(bytes) {
|
|
235
328
|
if (bytes.length < 3)
|
|
236
329
|
return null;
|
|
@@ -275,16 +368,67 @@ function parseRangeWidthOperand(bytes) {
|
|
|
275
368
|
wWidth: width,
|
|
276
369
|
};
|
|
277
370
|
}
|
|
371
|
+
function parseCssaOperand(bytes) {
|
|
372
|
+
if (bytes.length < 7)
|
|
373
|
+
return null;
|
|
374
|
+
const width = u16(bytes, 5);
|
|
375
|
+
return {
|
|
376
|
+
cb: bytes[0] ?? 0,
|
|
377
|
+
range: parseItcFirstLim(bytes, 1),
|
|
378
|
+
sides: bytes[3] ?? 0,
|
|
379
|
+
ftsWidth: bytes[4] ?? 0,
|
|
380
|
+
width,
|
|
381
|
+
wWidth: width,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
278
384
|
function parseRangeBrcOperand(bytes) {
|
|
279
|
-
if (
|
|
385
|
+
if (bytes.length < 8)
|
|
386
|
+
return null;
|
|
387
|
+
return {
|
|
388
|
+
cb: bytes[0] ?? 0,
|
|
389
|
+
range: parseItcFirstLim(bytes, 1),
|
|
390
|
+
bordersToApply: bytes[3] ?? 0,
|
|
391
|
+
border: parseBrc80(bytes, 4) || { nil: true },
|
|
392
|
+
extra: bytes.subarray(8),
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
function parseRangeBrcOperand97(bytes) {
|
|
396
|
+
if (bytes.length < 12)
|
|
280
397
|
return null;
|
|
281
398
|
return {
|
|
282
399
|
cb: bytes[0] ?? 0,
|
|
283
400
|
range: parseItcFirstLim(bytes, 1),
|
|
284
|
-
|
|
285
|
-
|
|
401
|
+
bordersToApply: bytes[3] ?? 0,
|
|
402
|
+
border: parseBrc(bytes, 4) || { nil: true },
|
|
403
|
+
extra: bytes.subarray(12),
|
|
286
404
|
};
|
|
287
405
|
}
|
|
406
|
+
function parseTableShadeOperand(bytes, odd = false) {
|
|
407
|
+
if (bytes.length < 13)
|
|
408
|
+
return null;
|
|
409
|
+
return {
|
|
410
|
+
cb: bytes[0] ?? 0,
|
|
411
|
+
range: parseItcFirstLim(bytes, 1),
|
|
412
|
+
shading: parseShd(bytes, 3),
|
|
413
|
+
odd,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
function parseVertMergeOperand(bytes) {
|
|
417
|
+
if (bytes.length < 3)
|
|
418
|
+
return null;
|
|
419
|
+
return { cb: bytes[0] ?? 0, index: bytes[1] ?? 0, value: bytes[2] ?? 0 };
|
|
420
|
+
}
|
|
421
|
+
function parseFixedRangeValueOperand(bytes) {
|
|
422
|
+
if (bytes.length < 3)
|
|
423
|
+
return null;
|
|
424
|
+
return { range: parseItcFirstLim(bytes, 0), value: bytes[2] ?? 0 };
|
|
425
|
+
}
|
|
426
|
+
function parseFixedRangeWidthOperand(bytes) {
|
|
427
|
+
if (bytes.length < 4)
|
|
428
|
+
return null;
|
|
429
|
+
const width = i16(bytes, 2);
|
|
430
|
+
return { range: parseItcFirstLim(bytes, 0), width, wWidth: width };
|
|
431
|
+
}
|
|
288
432
|
function parseTTableWidth(bytes) {
|
|
289
433
|
if (!bytes.length)
|
|
290
434
|
return null;
|
|
@@ -296,23 +440,13 @@ function parseTTableWidth(bytes) {
|
|
|
296
440
|
};
|
|
297
441
|
}
|
|
298
442
|
function parseTInsertOperand(bytes) {
|
|
299
|
-
if (
|
|
443
|
+
if (bytes.length < 4)
|
|
300
444
|
return null;
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
for (let i = 0; i < ctc && offset + 2 <= bytes.length; i += 1) {
|
|
307
|
-
dxaCol.push(i16(bytes, offset));
|
|
308
|
-
offset += 2;
|
|
309
|
-
}
|
|
310
|
-
const cells = [];
|
|
311
|
-
for (let i = 0; i < ctc && offset + 20 <= bytes.length; i += 1) {
|
|
312
|
-
cells.push(parseTc80(bytes, offset));
|
|
313
|
-
offset += 20;
|
|
314
|
-
}
|
|
315
|
-
return { cb, range: itc, itcFirst: itc.first, ctc, dxaCol, cells };
|
|
445
|
+
return {
|
|
446
|
+
itcFirst: bytes[0] ?? 0,
|
|
447
|
+
ctc: bytes[1] ?? 0,
|
|
448
|
+
dxaCol: u16(bytes, 2),
|
|
449
|
+
};
|
|
316
450
|
}
|
|
317
451
|
export function getSprmOperandLength(buffer, offset, sprm) {
|
|
318
452
|
const spra = (sprm >> 13) & 0x7;
|
|
@@ -415,18 +549,18 @@ export function decodeSprm(sprm, operandBytes) {
|
|
|
415
549
|
case SprmCodes.sprmPDxaWidth: return setMeta('para', 'frameWidth', i16(bytes, 0), raw, bytes);
|
|
416
550
|
case SprmCodes.sprmPPc: return setMeta('para', 'framePosition', bytes[0] ?? 0, raw, bytes);
|
|
417
551
|
case SprmCodes.sprmPWr: return setMeta('para', 'frameWrap', bytes[0] ?? 0, raw, bytes);
|
|
418
|
-
case SprmCodes.sprmPBrcTop80:
|
|
419
|
-
case SprmCodes.sprmPBrcTop: return setMeta('para', 'borderTop',
|
|
420
|
-
case SprmCodes.sprmPBrcLeft80:
|
|
421
|
-
case SprmCodes.sprmPBrcLeft: return setMeta('para', 'borderLeft',
|
|
422
|
-
case SprmCodes.sprmPBrcBottom80:
|
|
423
|
-
case SprmCodes.sprmPBrcBottom: return setMeta('para', 'borderBottom',
|
|
424
|
-
case SprmCodes.sprmPBrcRight80:
|
|
425
|
-
case SprmCodes.sprmPBrcRight: return setMeta('para', 'borderRight',
|
|
426
|
-
case SprmCodes.sprmPBrcBetween80:
|
|
427
|
-
case SprmCodes.sprmPBrcBetween: return setMeta('para', 'borderBetween',
|
|
428
|
-
case SprmCodes.sprmPBrcBar80:
|
|
429
|
-
case SprmCodes.sprmPBrcBar: return setMeta('para', 'borderBar',
|
|
552
|
+
case SprmCodes.sprmPBrcTop80: return setMeta('para', 'borderTop', parseBrc80(bytes, 0), raw, bytes);
|
|
553
|
+
case SprmCodes.sprmPBrcTop: return setMeta('para', 'borderTop', parseBrc(bytes, 1), raw, bytes);
|
|
554
|
+
case SprmCodes.sprmPBrcLeft80: return setMeta('para', 'borderLeft', parseBrc80(bytes, 0), raw, bytes);
|
|
555
|
+
case SprmCodes.sprmPBrcLeft: return setMeta('para', 'borderLeft', parseBrc(bytes, 1), raw, bytes);
|
|
556
|
+
case SprmCodes.sprmPBrcBottom80: return setMeta('para', 'borderBottom', parseBrc80(bytes, 0), raw, bytes);
|
|
557
|
+
case SprmCodes.sprmPBrcBottom: return setMeta('para', 'borderBottom', parseBrc(bytes, 1), raw, bytes);
|
|
558
|
+
case SprmCodes.sprmPBrcRight80: return setMeta('para', 'borderRight', parseBrc80(bytes, 0), raw, bytes);
|
|
559
|
+
case SprmCodes.sprmPBrcRight: return setMeta('para', 'borderRight', parseBrc(bytes, 1), raw, bytes);
|
|
560
|
+
case SprmCodes.sprmPBrcBetween80: return setMeta('para', 'borderBetween', parseBrc80(bytes, 0), raw, bytes);
|
|
561
|
+
case SprmCodes.sprmPBrcBetween: return setMeta('para', 'borderBetween', parseBrc(bytes, 1), raw, bytes);
|
|
562
|
+
case SprmCodes.sprmPBrcBar80: return setMeta('para', 'borderBar', parseBrc80(bytes, 0), raw, bytes);
|
|
563
|
+
case SprmCodes.sprmPBrcBar: return setMeta('para', 'borderBar', parseBrc(bytes, 1), raw, bytes);
|
|
430
564
|
case SprmCodes.sprmPWHeightAbs: return setMeta('para', 'frameHeight', i16(bytes, 0), raw, bytes);
|
|
431
565
|
case SprmCodes.sprmPShd80:
|
|
432
566
|
case SprmCodes.sprmPShd: return setMeta('para', 'shading', bytes.slice(), raw, bytes);
|
|
@@ -449,6 +583,15 @@ export function decodeSprm(sprm, operandBytes) {
|
|
|
449
583
|
case SprmCodes.sprmTTableHeader: return setMeta('table', 'header', boolValue(bytes), raw, bytes);
|
|
450
584
|
case SprmCodes.sprmTDyaRowHeight: return setMeta('table', 'rowHeight', i16(bytes, 0), raw, bytes);
|
|
451
585
|
case SprmCodes.sprmTDefTable: return setMeta('table', 'defTable', parseTDefTableOperand(bytes), raw, bytes);
|
|
586
|
+
case SprmCodes.sprmTTableBorders80: return setMeta('table', 'tableBorders', parseTableBorders(bytes, true), raw, bytes);
|
|
587
|
+
case SprmCodes.sprmTTableBorders: return setMeta('table', 'tableBorders', parseTableBorders(bytes, false), raw, bytes);
|
|
588
|
+
case SprmCodes.sprmTDefTableShd80: return setMeta('table', 'defaultShading', parseDefaultShading(bytes, 0, true), raw, bytes);
|
|
589
|
+
case SprmCodes.sprmTDefTableShd:
|
|
590
|
+
case SprmCodes.sprmTDefTableShdRaw: return setMeta('table', 'defaultShading', parseDefaultShading(bytes, 0, false), raw, bytes);
|
|
591
|
+
case SprmCodes.sprmTDefTableShd2nd:
|
|
592
|
+
case SprmCodes.sprmTDefTableShdRaw2nd: return setMeta('table', 'defaultShading', parseDefaultShading(bytes, 22, false), raw, bytes);
|
|
593
|
+
case SprmCodes.sprmTDefTableShd3rd:
|
|
594
|
+
case SprmCodes.sprmTDefTableShdRaw3rd: return setMeta('table', 'defaultShading', parseDefaultShading(bytes, 44, false), raw, bytes);
|
|
452
595
|
case SprmCodes.sprmTFBiDi: return setMeta('table', 'rtl', boolValue(bytes), raw, bytes);
|
|
453
596
|
case SprmCodes.sprmTPc: return setMeta('table', 'positionCode', bytes[0] ?? 0, raw, bytes);
|
|
454
597
|
case SprmCodes.sprmTDxaAbs: return setMeta('table', 'absLeft', i16(bytes, 0), raw, bytes);
|
|
@@ -461,20 +604,24 @@ export function decodeSprm(sprm, operandBytes) {
|
|
|
461
604
|
case SprmCodes.sprmTWidthAfter: return setMeta('table', 'widthAfter', parseTTableWidth(bytes), raw, bytes);
|
|
462
605
|
case SprmCodes.sprmTInsert: return setMeta('table', 'insertCells', parseTInsertOperand(bytes), raw, bytes);
|
|
463
606
|
case SprmCodes.sprmTDelete: return setMeta('table', 'deleteCells', parseItcFirstLim(bytes, 0), raw, bytes);
|
|
464
|
-
case SprmCodes.sprmTDxaCol: return setMeta('table', 'columnWidth',
|
|
607
|
+
case SprmCodes.sprmTDxaCol: return setMeta('table', 'columnWidth', parseFixedRangeWidthOperand(bytes), raw, bytes);
|
|
465
608
|
case SprmCodes.sprmTMerge: return setMeta('table', 'merge', parseItcFirstLim(bytes, 0), raw, bytes);
|
|
466
609
|
case SprmCodes.sprmTSplit: return setMeta('table', 'split', parseItcFirstLim(bytes, 0), raw, bytes);
|
|
467
|
-
case SprmCodes.sprmTTextFlow: return setMeta('table', 'textFlow',
|
|
468
|
-
|
|
610
|
+
case SprmCodes.sprmTTextFlow: return setMeta('table', 'textFlow', {
|
|
611
|
+
range: parseItcFirstLim(bytes, 0),
|
|
612
|
+
value: u16(bytes, 2),
|
|
613
|
+
}, raw, bytes);
|
|
614
|
+
case SprmCodes.sprmTVertMerge: return setMeta('table', 'vertMerge', parseVertMergeOperand(bytes), raw, bytes);
|
|
469
615
|
case SprmCodes.sprmTVertAlign: return setMeta('table', 'vertAlign', parseRangeValueOperand(bytes), raw, bytes);
|
|
470
|
-
case SprmCodes.sprmTSetShd:
|
|
471
|
-
case SprmCodes.sprmTSetShdOdd: return setMeta('table', 'setShading',
|
|
472
|
-
case SprmCodes.sprmTSetBrc80:
|
|
473
|
-
case SprmCodes.sprmTSetBrc: return setMeta('table', 'setBorder',
|
|
616
|
+
case SprmCodes.sprmTSetShd: return setMeta('table', 'setShading', parseTableShadeOperand(bytes), raw, bytes);
|
|
617
|
+
case SprmCodes.sprmTSetShdOdd: return setMeta('table', 'setShading', parseTableShadeOperand(bytes, true), raw, bytes);
|
|
618
|
+
case SprmCodes.sprmTSetBrc80: return setMeta('table', 'setBorder', parseRangeBrcOperand(bytes), raw, bytes);
|
|
619
|
+
case SprmCodes.sprmTSetBrc: return setMeta('table', 'setBorder', parseRangeBrcOperand97(bytes), raw, bytes);
|
|
474
620
|
case SprmCodes.sprmTCellPadding:
|
|
475
|
-
case SprmCodes.sprmTCellPaddingDefault: return setMeta('table', 'cellPadding',
|
|
621
|
+
case SprmCodes.sprmTCellPaddingDefault: return setMeta('table', 'cellPadding', parseCssaOperand(bytes), raw, bytes);
|
|
622
|
+
case SprmCodes.sprmTCellSpacingDefault: return setMeta('table', 'cellSpacing', parseCssaOperand(bytes), raw, bytes);
|
|
476
623
|
case SprmCodes.sprmTCellWidth: return setMeta('table', 'cellWidth', parseRangeWidthOperand(bytes), raw, bytes);
|
|
477
|
-
case SprmCodes.sprmTFitText: return setMeta('table', 'fitText',
|
|
624
|
+
case SprmCodes.sprmTFitText: return setMeta('table', 'fitText', parseFixedRangeValueOperand(bytes), raw, bytes);
|
|
478
625
|
case SprmCodes.sprmTFCellNoWrap: return setMeta('table', 'cellNoWrap', parseRangeValueOperand(bytes), raw, bytes);
|
|
479
626
|
case SprmCodes.sprmTIstd: return setMeta('table', 'styleId', u16(bytes, 0), raw, bytes);
|
|
480
627
|
default: {
|
package/dist/msdoc/styles.js
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import { BinaryReader } from '../core/binary.js';
|
|
2
2
|
import { alignEven } from '../core/utils.js';
|
|
3
3
|
import { decodeGrpprl } from './sprm.js';
|
|
4
|
+
const REPEATABLE_TABLE_PROPERTIES = new Set([
|
|
5
|
+
'insertCells',
|
|
6
|
+
'deleteCells',
|
|
7
|
+
'columnWidth',
|
|
8
|
+
'merge',
|
|
9
|
+
'split',
|
|
10
|
+
'textFlow',
|
|
11
|
+
'vertMerge',
|
|
12
|
+
'vertAlign',
|
|
13
|
+
'setShading',
|
|
14
|
+
'defaultShading',
|
|
15
|
+
'setBorder',
|
|
16
|
+
'cellPadding',
|
|
17
|
+
'cellSpacing',
|
|
18
|
+
'cellWidth',
|
|
19
|
+
'fitText',
|
|
20
|
+
'cellNoWrap',
|
|
21
|
+
]);
|
|
4
22
|
function mergePropertyArrays(...arrays) {
|
|
5
23
|
const map = new Map();
|
|
24
|
+
let sequence = 0;
|
|
6
25
|
for (const array of arrays) {
|
|
7
26
|
for (const prop of array || []) {
|
|
8
|
-
|
|
27
|
+
const repeatable = prop.kind === 'table' && REPEATABLE_TABLE_PROPERTIES.has(prop.name);
|
|
28
|
+
map.set(repeatable ? `${prop.kind}:${prop.name}:${sequence++}` : `${prop.kind}:${prop.name}`, prop);
|
|
9
29
|
}
|
|
10
30
|
}
|
|
11
31
|
return Array.from(map.values());
|
package/dist/render/html.js
CHANGED
|
@@ -26,15 +26,72 @@ function styleObjectToCss(style) {
|
|
|
26
26
|
.join(';');
|
|
27
27
|
}
|
|
28
28
|
function borderToCss(border) {
|
|
29
|
-
if (!border)
|
|
29
|
+
if (!border || border.nil)
|
|
30
30
|
return null;
|
|
31
|
-
const width = Math.max(1, Math.round(((border.lineWidth ?? 8) / 8) * 1.3333));
|
|
32
31
|
const borderType = border.borderType;
|
|
33
|
-
|
|
32
|
+
if (!borderType)
|
|
33
|
+
return null;
|
|
34
|
+
const lineWidth = Math.max(2, border.lineWidth || 0);
|
|
35
|
+
const widthPoints = borderType >= 0x40 ? lineWidth : lineWidth / 8;
|
|
36
|
+
const width = Math.max(1, Math.round(widthPoints * (96 / 72)));
|
|
37
|
+
const style = borderType === 3
|
|
38
|
+
? 'double'
|
|
39
|
+
: borderType === 6
|
|
40
|
+
? 'dotted'
|
|
41
|
+
: [7, 8, 9].includes(borderType)
|
|
42
|
+
? 'dashed'
|
|
43
|
+
: 'solid';
|
|
34
44
|
const colorIndex = border.color;
|
|
35
|
-
const color =
|
|
45
|
+
const color = border.colorRef != null
|
|
46
|
+
? colorRefToCss(border.colorRef)
|
|
47
|
+
: COLOR_INDEX_MAP[colorIndex || 1] || '#000000';
|
|
36
48
|
return `${width}px ${style} ${color}`;
|
|
37
49
|
}
|
|
50
|
+
function colorRefToCss(colorRef) {
|
|
51
|
+
if (colorRef == null || (colorRef >>> 24) === 0xff)
|
|
52
|
+
return '#000000';
|
|
53
|
+
const red = colorRef & 0xff;
|
|
54
|
+
const green = (colorRef >>> 8) & 0xff;
|
|
55
|
+
const blue = (colorRef >>> 16) & 0xff;
|
|
56
|
+
return `#${[red, green, blue].map(value => value.toString(16).padStart(2, '0')).join('')}`;
|
|
57
|
+
}
|
|
58
|
+
const SHADING_PERCENT = {
|
|
59
|
+
2: 5, 3: 10, 4: 20, 5: 25, 6: 30, 7: 40,
|
|
60
|
+
8: 50, 9: 60, 10: 70, 11: 75, 12: 80, 13: 90,
|
|
61
|
+
};
|
|
62
|
+
function mixHexColors(foreground, background, percent) {
|
|
63
|
+
const channel = (color, offset) => Number.parseInt(color.slice(offset, offset + 2), 16);
|
|
64
|
+
const ratio = Math.max(0, Math.min(100, percent)) / 100;
|
|
65
|
+
const values = [1, 3, 5].map(offset => Math.round(channel(foreground, offset) * ratio + channel(background, offset) * (1 - ratio)));
|
|
66
|
+
return `#${values.map(value => value.toString(16).padStart(2, '0')).join('')}`;
|
|
67
|
+
}
|
|
68
|
+
function shadingToCss(shading) {
|
|
69
|
+
if (!shading || shading.nil || shading.auto || shading.pattern === 0)
|
|
70
|
+
return {};
|
|
71
|
+
const foreground = shading.foregroundColorRef != null
|
|
72
|
+
? colorRefToCss(shading.foregroundColorRef)
|
|
73
|
+
: COLOR_INDEX_MAP[shading.foregroundColorIndex || 1] || '#000000';
|
|
74
|
+
const background = shading.backgroundColorRef != null
|
|
75
|
+
? colorRefToCss(shading.backgroundColorRef)
|
|
76
|
+
: COLOR_INDEX_MAP[shading.backgroundColorIndex || 8] || '#ffffff';
|
|
77
|
+
if (shading.pattern === 1)
|
|
78
|
+
return { 'background-color': foreground };
|
|
79
|
+
if (SHADING_PERCENT[shading.pattern] != null) {
|
|
80
|
+
return { 'background-color': mixHexColors(foreground, background, SHADING_PERCENT[shading.pattern]) };
|
|
81
|
+
}
|
|
82
|
+
const stripe = shading.pattern === 14 || shading.pattern === 20
|
|
83
|
+
? `repeating-linear-gradient(0deg,${foreground} 0 1px,transparent 1px 4px)`
|
|
84
|
+
: shading.pattern === 15 || shading.pattern === 21
|
|
85
|
+
? `repeating-linear-gradient(90deg,${foreground} 0 1px,transparent 1px 4px)`
|
|
86
|
+
: shading.pattern === 16 || shading.pattern === 22
|
|
87
|
+
? `repeating-linear-gradient(45deg,${foreground} 0 1px,transparent 1px 5px)`
|
|
88
|
+
: shading.pattern === 17 || shading.pattern === 23
|
|
89
|
+
? `repeating-linear-gradient(-45deg,${foreground} 0 1px,transparent 1px 5px)`
|
|
90
|
+
: null;
|
|
91
|
+
return stripe
|
|
92
|
+
? { 'background-color': background, 'background-image': stripe }
|
|
93
|
+
: { 'background-color': background };
|
|
94
|
+
}
|
|
38
95
|
function paragraphStyleToCss(paraState) {
|
|
39
96
|
const style = {
|
|
40
97
|
'text-align': cssTextAlign(paraState.alignment),
|
|
@@ -74,11 +131,11 @@ function paragraphStyleToCss(paraState) {
|
|
|
74
131
|
if (top)
|
|
75
132
|
style['border-top'] = top;
|
|
76
133
|
if (right)
|
|
77
|
-
style['border-
|
|
134
|
+
style['border-inline-end'] = right;
|
|
78
135
|
if (bottom)
|
|
79
136
|
style['border-bottom'] = bottom;
|
|
80
137
|
if (left)
|
|
81
|
-
style['border-
|
|
138
|
+
style['border-inline-start'] = left;
|
|
82
139
|
return style;
|
|
83
140
|
}
|
|
84
141
|
function buildUnderlineStyle(underline) {
|
|
@@ -263,25 +320,33 @@ function cellStyle(cell) {
|
|
|
263
320
|
style['text-align'] = 'justify';
|
|
264
321
|
if (cell.meta?.vertAlign != null)
|
|
265
322
|
style['vertical-align'] = cssVerticalAlign(cell.meta.vertAlign);
|
|
266
|
-
const
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
323
|
+
const borders = cell.meta?.borders || {};
|
|
324
|
+
const borderAll = borderToCss(borders.all);
|
|
325
|
+
const borderForSide = (side) => Object.prototype.hasOwnProperty.call(borders, side)
|
|
326
|
+
? borderToCss(borders[side])
|
|
327
|
+
: borderAll;
|
|
328
|
+
const top = borderForSide('top');
|
|
329
|
+
const right = borderForSide('right');
|
|
330
|
+
const bottom = borderForSide('bottom');
|
|
331
|
+
const left = borderForSide('left');
|
|
271
332
|
if (top)
|
|
272
333
|
style['border-top'] = top;
|
|
273
334
|
if (right)
|
|
274
|
-
style['border-
|
|
335
|
+
style['border-inline-end'] = right;
|
|
275
336
|
if (bottom)
|
|
276
337
|
style['border-bottom'] = bottom;
|
|
277
338
|
if (left)
|
|
278
|
-
style['border-
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (
|
|
283
|
-
style['
|
|
284
|
-
|
|
339
|
+
style['border-inline-start'] = left;
|
|
340
|
+
const padding = cell.meta?.paddingTwips;
|
|
341
|
+
if (padding?.top != null)
|
|
342
|
+
style['padding-top'] = `${twipsToPx(padding.top)}px`;
|
|
343
|
+
if (padding?.right != null)
|
|
344
|
+
style['padding-inline-end'] = `${twipsToPx(padding.right)}px`;
|
|
345
|
+
if (padding?.bottom != null)
|
|
346
|
+
style['padding-bottom'] = `${twipsToPx(padding.bottom)}px`;
|
|
347
|
+
if (padding?.left != null)
|
|
348
|
+
style['padding-inline-start'] = `${twipsToPx(padding.left)}px`;
|
|
349
|
+
Object.assign(style, shadingToCss(cell.meta?.shading));
|
|
285
350
|
return style;
|
|
286
351
|
}
|
|
287
352
|
function tableStyle(block) {
|
|
@@ -293,8 +358,25 @@ function tableStyle(block) {
|
|
|
293
358
|
style.width = '100%';
|
|
294
359
|
const marginLeft = twipsToPx(block.state?.leftIndent);
|
|
295
360
|
if (marginLeft)
|
|
296
|
-
style['margin-
|
|
297
|
-
|
|
361
|
+
style['margin-inline-start'] = `${marginLeft}px`;
|
|
362
|
+
if (block.state?.rtl)
|
|
363
|
+
style.direction = 'rtl';
|
|
364
|
+
const spacing = block.rows.flatMap(row => row.cells).reduce((maximum, cell) => {
|
|
365
|
+
const value = cell.meta?.spacingTwips;
|
|
366
|
+
if (!value)
|
|
367
|
+
return maximum;
|
|
368
|
+
return {
|
|
369
|
+
horizontal: Math.max(maximum.horizontal, value.left || 0, value.right || 0),
|
|
370
|
+
vertical: Math.max(maximum.vertical, value.top || 0, value.bottom || 0),
|
|
371
|
+
};
|
|
372
|
+
}, { horizontal: 0, vertical: 0 });
|
|
373
|
+
if (spacing.horizontal || spacing.vertical) {
|
|
374
|
+
style['border-collapse'] = 'separate';
|
|
375
|
+
style['border-spacing'] = `${twipsToPx(spacing.horizontal)}px ${twipsToPx(spacing.vertical)}px`;
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
style['border-collapse'] = 'collapse';
|
|
379
|
+
}
|
|
298
380
|
style['table-layout'] = 'fixed';
|
|
299
381
|
return style;
|
|
300
382
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -211,8 +211,31 @@ export interface BorderSpec {
|
|
|
211
211
|
borderType?: number;
|
|
212
212
|
lineWidth?: number;
|
|
213
213
|
color?: number;
|
|
214
|
+
colorRef?: number;
|
|
215
|
+
frame?: boolean;
|
|
216
|
+
nil?: boolean;
|
|
217
|
+
shadow?: boolean;
|
|
218
|
+
space?: number;
|
|
214
219
|
[key: string]: unknown;
|
|
215
220
|
}
|
|
221
|
+
export interface TableBorders {
|
|
222
|
+
top?: BorderSpec;
|
|
223
|
+
left?: BorderSpec;
|
|
224
|
+
bottom?: BorderSpec;
|
|
225
|
+
right?: BorderSpec;
|
|
226
|
+
horizontalInside?: BorderSpec;
|
|
227
|
+
verticalInside?: BorderSpec;
|
|
228
|
+
}
|
|
229
|
+
export interface ShadingSpec {
|
|
230
|
+
foregroundColorIndex?: number;
|
|
231
|
+
backgroundColorIndex?: number;
|
|
232
|
+
foregroundColorRef?: number;
|
|
233
|
+
backgroundColorRef?: number;
|
|
234
|
+
pattern: number;
|
|
235
|
+
auto?: boolean;
|
|
236
|
+
nil?: boolean;
|
|
237
|
+
legacy?: boolean;
|
|
238
|
+
}
|
|
216
239
|
export interface HighlightInfo {
|
|
217
240
|
index: number;
|
|
218
241
|
color?: string;
|
|
@@ -326,6 +349,7 @@ export interface ItcRange {
|
|
|
326
349
|
export interface RangeWidthOperand {
|
|
327
350
|
cb?: number;
|
|
328
351
|
range: ItcRange;
|
|
352
|
+
sides?: number;
|
|
329
353
|
width?: number;
|
|
330
354
|
wWidth?: number;
|
|
331
355
|
ftsWidth?: number;
|
|
@@ -334,6 +358,7 @@ export interface RangeBorderOperand {
|
|
|
334
358
|
cb?: number;
|
|
335
359
|
range: ItcRange;
|
|
336
360
|
border: BorderSpec;
|
|
361
|
+
bordersToApply: number;
|
|
337
362
|
extra?: Uint8Array;
|
|
338
363
|
}
|
|
339
364
|
export interface RangeValueOperand {
|
|
@@ -348,13 +373,9 @@ export interface TableWidthOperand {
|
|
|
348
373
|
wWidth?: number;
|
|
349
374
|
}
|
|
350
375
|
export interface TInsertOperand {
|
|
351
|
-
cb?: number;
|
|
352
|
-
range?: ItcRange;
|
|
353
376
|
itcFirst?: number;
|
|
354
|
-
dxaCol?: number[] | number;
|
|
355
|
-
dxaGapHalf?: number;
|
|
356
377
|
ctc?: number;
|
|
357
|
-
|
|
378
|
+
dxaCol?: number;
|
|
358
379
|
[key: string]: unknown;
|
|
359
380
|
}
|
|
360
381
|
export interface TableState {
|
|
@@ -375,6 +396,7 @@ export interface TableState {
|
|
|
375
396
|
autoFit?: unknown;
|
|
376
397
|
widthBefore?: unknown;
|
|
377
398
|
widthAfter?: unknown;
|
|
399
|
+
borders: TableBorders;
|
|
378
400
|
defTable?: TDefTableOperand;
|
|
379
401
|
operations: DecodedProperty[];
|
|
380
402
|
[key: string]: unknown;
|
|
@@ -393,7 +415,9 @@ export interface TableCellMeta {
|
|
|
393
415
|
textFlow?: number;
|
|
394
416
|
rightBoundary?: number;
|
|
395
417
|
leftBoundary?: number;
|
|
396
|
-
shading?:
|
|
418
|
+
shading?: ShadingSpec;
|
|
419
|
+
paddingTwips?: Partial<Record<'top' | 'left' | 'bottom' | 'right', number>>;
|
|
420
|
+
spacingTwips?: Partial<Record<'top' | 'left' | 'bottom' | 'right', number>>;
|
|
397
421
|
}
|
|
398
422
|
export interface CharSegment {
|
|
399
423
|
cpStart: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@file-viewer/doc",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.27",
|
|
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.",
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"scripts": {
|
|
69
69
|
"build": "tsc -b tsconfig.json",
|
|
70
70
|
"type-check": "tsc -b tsconfig.json",
|
|
71
|
+
"verify:table-spec": "pnpm build && node scripts/verify-table-spec.mjs",
|
|
71
72
|
"verify:github-34": "pnpm build && node scripts/verify-github-34.mjs",
|
|
72
73
|
"verify:github-87": "pnpm build && node scripts/verify-github-87.mjs"
|
|
73
74
|
}
|