@stll/folio-core 0.6.0 → 0.7.0
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/controller/layoutPipeline.js +33 -14
- package/dist/docx/blockContentParser.js +2 -100
- package/dist/docx/groupDrawingParser.d.ts +1 -1
- package/dist/docx/groupDrawingParser.js +49 -8
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +9 -0
- package/dist/docx/paragraphTextBoxEnrichment.js +104 -0
- package/dist/docx/runParser.js +11 -2
- package/dist/docx/server/boundedArchive.d.ts +24 -0
- package/dist/docx/server/boundedArchive.js +106 -0
- package/dist/docx/server/extractDocxText.d.ts +23 -0
- package/dist/docx/server/extractDocxText.js +154 -0
- package/dist/docx/tableParser.js +2 -0
- package/dist/layout-bridge/convert/toFlowBlocks.js +75 -19
- package/dist/layout-bridge/sectionColumns.js +6 -1
- package/dist/layout-engine/index.js +120 -19
- package/dist/layout-engine/keep-together.d.ts +7 -5
- package/dist/layout-engine/keep-together.js +20 -4
- package/dist/layout-engine/measure/cache.js +2 -0
- package/dist/layout-engine/measure/measureBlocks.js +3 -2
- package/dist/layout-engine/measure/measureParagraph.js +30 -12
- package/dist/layout-engine/paginator.d.ts +2 -0
- package/dist/layout-engine/paginator.js +27 -15
- package/dist/layout-engine/tableRowBreak.js +3 -0
- package/dist/layout-engine/types.d.ts +20 -5
- package/dist/layout-painter/index.js +1 -1
- package/dist/layout-painter/renderPage.js +7 -2
- package/dist/layout-painter/renderParagraph.js +93 -9
- package/dist/layout-painter/renderTable.js +88 -10
- package/dist/paged-layout/sectionBlockWidths.js +11 -3
- package/dist/prosemirror/conversion/fromProseDoc.js +11 -2
- package/dist/prosemirror/conversion/toProseDoc.js +28 -20
- package/dist/prosemirror/extensions/nodes/TableExtension.js +3 -2
- package/dist/prosemirror/schema/nodes.d.ts +2 -1
- package/dist/prosemirror/utils/tabCalculator.js +1 -1
- package/dist/server.d.ts +3 -1
- package/dist/server.js +3 -1
- package/dist/utils/formatToStyle.js +3 -3
- package/dist/utils/units.d.ts +6 -6
- package/dist/utils/units.js +8 -8
- package/package.json +1 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { emuToPixels } from "../utils/units.js";
|
|
2
|
+
import { isFloatingImageRun, isFloatingTextBoxBlock, tableColumnsArePinned } from "../layout-engine/types.js";
|
|
2
3
|
import { measureParagraph } from "../layout-engine/measure/measureParagraph.js";
|
|
3
4
|
import { getAutomaticTextColorForBackground } from "./documentColors.js";
|
|
4
5
|
import { renderParagraphFragment } from "./renderParagraph.js";
|
|
5
6
|
import { buildTableCellFloatingZones, getTableCellContentWidth, getTableCellFloatingImages } from "../layout-engine/measure/tableCellFloating.js";
|
|
7
|
+
import { renderTextBoxFragment } from "./renderTextBox.js";
|
|
6
8
|
//#region src/layout-painter/renderTable.ts
|
|
7
9
|
/**
|
|
8
10
|
* Table Renderer
|
|
@@ -26,9 +28,6 @@ const TABLE_CLASS_NAMES = {
|
|
|
26
28
|
tableEdgeHandleBottom: "layout-table-edge-handle-bottom",
|
|
27
29
|
tableEdgeHandleRight: "layout-table-edge-handle-right"
|
|
28
30
|
};
|
|
29
|
-
/**
|
|
30
|
-
* Render cell content (paragraphs and nested tables)
|
|
31
|
-
*/
|
|
32
31
|
function renderCellContent(cell, cellMeasure, context, doc) {
|
|
33
32
|
const contentEl = doc.createElement("div");
|
|
34
33
|
contentEl.className = TABLE_CLASS_NAMES.cellContent;
|
|
@@ -37,6 +36,7 @@ function renderCellContent(cell, cellMeasure, context, doc) {
|
|
|
37
36
|
contentEl.style.width = `${contentWidth}px`;
|
|
38
37
|
const cellFloatingImages = getTableCellFloatingImages(cell, cellMeasure, contentWidth);
|
|
39
38
|
const floatingZones = buildTableCellFloatingZones(cellFloatingImages, contentWidth);
|
|
39
|
+
const floatingLayers = [];
|
|
40
40
|
if (cellFloatingImages.length > 0) {
|
|
41
41
|
const floatingLayer = doc.createElement("div");
|
|
42
42
|
floatingLayer.className = "layout-cell-floating-images-layer";
|
|
@@ -47,7 +47,7 @@ function renderCellContent(cell, cellMeasure, context, doc) {
|
|
|
47
47
|
floatingLayer.style.height = "100%";
|
|
48
48
|
floatingLayer.style.pointerEvents = "none";
|
|
49
49
|
floatingLayer.style.zIndex = "10";
|
|
50
|
-
floatingLayer.style.overflow = "
|
|
50
|
+
floatingLayer.style.overflow = "visible";
|
|
51
51
|
for (const img of cellFloatingImages) {
|
|
52
52
|
const imgContainer = doc.createElement("div");
|
|
53
53
|
imgContainer.className = "layout-cell-floating-image";
|
|
@@ -67,9 +67,11 @@ function renderCellContent(cell, cellMeasure, context, doc) {
|
|
|
67
67
|
imgContainer.append(imgEl);
|
|
68
68
|
floatingLayer.append(imgContainer);
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
floatingLayers.push(floatingLayer);
|
|
71
71
|
}
|
|
72
72
|
let cumulativeY = 0;
|
|
73
|
+
let anchorParagraphY = 0;
|
|
74
|
+
let floatingTextBoxesLayer;
|
|
73
75
|
for (let i = 0; i < cell.blocks.length; i++) {
|
|
74
76
|
const block = cell.blocks[i];
|
|
75
77
|
const measure = cellMeasure.blocks[i];
|
|
@@ -103,15 +105,81 @@ function renderCellContent(cell, cellMeasure, context, doc) {
|
|
|
103
105
|
const spaceBefore = paragraphBlock.attrs?.spacing?.before ?? 0;
|
|
104
106
|
if (spaceBefore > 0) fragEl.style.paddingTop = `${spaceBefore}px`;
|
|
105
107
|
contentEl.append(fragEl);
|
|
108
|
+
anchorParagraphY = cumulativeY;
|
|
106
109
|
cumulativeY += paragraphMeasure.totalHeight;
|
|
107
110
|
} else if (block?.kind === "table" && measure?.kind === "table") {
|
|
108
111
|
const nestedTableEl = renderNestedTable(block, measure, context, doc);
|
|
109
112
|
nestedTableEl.style.position = "relative";
|
|
110
113
|
contentEl.append(nestedTableEl);
|
|
111
114
|
cumulativeY += measure.totalHeight;
|
|
115
|
+
anchorParagraphY = cumulativeY;
|
|
116
|
+
} else if (block?.kind === "textBox" && measure?.kind === "textBox") {
|
|
117
|
+
const textBoxBlock = block;
|
|
118
|
+
const textBoxMeasure = measure;
|
|
119
|
+
const textBoxEl = renderTextBoxFragment({
|
|
120
|
+
kind: "textBox",
|
|
121
|
+
blockId: textBoxBlock.id,
|
|
122
|
+
x: 0,
|
|
123
|
+
y: 0,
|
|
124
|
+
width: textBoxMeasure.width,
|
|
125
|
+
height: textBoxMeasure.height,
|
|
126
|
+
...textBoxBlock.pmStart !== void 0 ? { pmStart: textBoxBlock.pmStart } : {},
|
|
127
|
+
...textBoxBlock.pmEnd !== void 0 ? { pmEnd: textBoxBlock.pmEnd } : {}
|
|
128
|
+
}, textBoxBlock, textBoxMeasure, {
|
|
129
|
+
...context,
|
|
130
|
+
insideTableCell: true
|
|
131
|
+
}, { document: doc });
|
|
132
|
+
if (isFloatingTextBoxBlock(textBoxBlock)) {
|
|
133
|
+
floatingTextBoxesLayer ??= createCellFloatingTextBoxesLayer(doc);
|
|
134
|
+
textBoxEl.style.left = `${resolveCellTextBoxX(textBoxBlock, contentWidth)}px`;
|
|
135
|
+
textBoxEl.style.top = `${anchorParagraphY + resolveCellTextBoxY(textBoxBlock)}px`;
|
|
136
|
+
textBoxEl.style.pointerEvents = "auto";
|
|
137
|
+
floatingTextBoxesLayer.append(textBoxEl);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
textBoxEl.style.position = "relative";
|
|
141
|
+
textBoxEl.style.left = "0";
|
|
142
|
+
textBoxEl.style.top = "0";
|
|
143
|
+
contentEl.append(textBoxEl);
|
|
144
|
+
cumulativeY += textBoxMeasure.height;
|
|
145
|
+
anchorParagraphY = cumulativeY;
|
|
112
146
|
}
|
|
113
147
|
}
|
|
114
|
-
|
|
148
|
+
if (floatingTextBoxesLayer) floatingLayers.push(floatingTextBoxesLayer);
|
|
149
|
+
return {
|
|
150
|
+
content: contentEl,
|
|
151
|
+
floatingLayers
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function createCellFloatingTextBoxesLayer(doc) {
|
|
155
|
+
const layer = doc.createElement("div");
|
|
156
|
+
layer.className = "layout-cell-floating-text-boxes-layer";
|
|
157
|
+
layer.style.position = "absolute";
|
|
158
|
+
layer.style.inset = "0";
|
|
159
|
+
layer.style.pointerEvents = "none";
|
|
160
|
+
layer.style.zIndex = "10";
|
|
161
|
+
layer.style.overflow = "visible";
|
|
162
|
+
return layer;
|
|
163
|
+
}
|
|
164
|
+
function resolveCellTextBoxX(block, contentWidth) {
|
|
165
|
+
const horizontal = block.position?.horizontal;
|
|
166
|
+
if (horizontal?.posOffset !== void 0) return emuToPixels(horizontal.posOffset);
|
|
167
|
+
if (horizontal?.align === "center") return (contentWidth - block.width) / 2;
|
|
168
|
+
if (horizontal?.align === "right" || horizontal?.align === "outside") return contentWidth - block.width;
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
function resolveCellTextBoxY(block) {
|
|
172
|
+
const vertical = block.position?.vertical;
|
|
173
|
+
if (vertical?.posOffset !== void 0) return emuToPixels(vertical.posOffset);
|
|
174
|
+
return 0;
|
|
175
|
+
}
|
|
176
|
+
function tableHasFloatingCellContent(block) {
|
|
177
|
+
for (const row of block.rows) for (const cell of row.cells) for (const cellBlock of cell.blocks) {
|
|
178
|
+
if (cellBlock.kind === "textBox" && isFloatingTextBoxBlock(cellBlock)) return true;
|
|
179
|
+
if (cellBlock.kind === "paragraph" && cellBlock.runs.some((run) => run.kind === "image" && isFloatingImageRun(run))) return true;
|
|
180
|
+
if (cellBlock.kind === "table" && tableHasFloatingCellContent(cellBlock)) return true;
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
115
183
|
}
|
|
116
184
|
/**
|
|
117
185
|
* Render a nested table (within a cell)
|
|
@@ -214,8 +282,18 @@ function renderTableCell(cell, cellMeasure, x, rowHeight, borderFlags, columnsPi
|
|
|
214
282
|
default: break;
|
|
215
283
|
}
|
|
216
284
|
}
|
|
217
|
-
const
|
|
218
|
-
|
|
285
|
+
const renderedContent = renderCellContent(cell, cellMeasure, context, doc);
|
|
286
|
+
if (renderedContent.floatingLayers.length > 0) {
|
|
287
|
+
renderedContent.content.style.height = "100%";
|
|
288
|
+
renderedContent.content.style.overflow = "hidden";
|
|
289
|
+
cellEl.style.overflow = "visible";
|
|
290
|
+
}
|
|
291
|
+
cellEl.append(renderedContent.content);
|
|
292
|
+
for (const floatingLayer of renderedContent.floatingLayers) {
|
|
293
|
+
floatingLayer.style.left = `${padLeft}px`;
|
|
294
|
+
floatingLayer.style.top = `${padTop}px`;
|
|
295
|
+
cellEl.append(floatingLayer);
|
|
296
|
+
}
|
|
219
297
|
if (cell.blocks.length > 0) {
|
|
220
298
|
const firstBlock = cell.blocks.at(0);
|
|
221
299
|
const lastBlock = cell.blocks.at(-1);
|
|
@@ -316,7 +394,7 @@ function renderTableFragment(fragment, block, measure, context, options = {}) {
|
|
|
316
394
|
tableEl.style.position = "absolute";
|
|
317
395
|
tableEl.style.width = `${fragment.width}px`;
|
|
318
396
|
tableEl.style.height = `${fragment.height}px`;
|
|
319
|
-
tableEl.style.overflow = "hidden";
|
|
397
|
+
tableEl.style.overflow = tableHasFloatingCellContent(block) ? "visible" : "hidden";
|
|
320
398
|
tableEl.dataset["blockId"] = String(fragment.blockId);
|
|
321
399
|
tableEl.dataset["fromRow"] = String(fragment.fromRow);
|
|
322
400
|
tableEl.dataset["toRow"] = String(fragment.toRow);
|
|
@@ -20,8 +20,10 @@ function computePerBlockWidths({ blocks, bodyConfig, finalConfig }) {
|
|
|
20
20
|
}).widths;
|
|
21
21
|
}
|
|
22
22
|
function computePerBlockMeasureInputs({ blocks, bodyConfig, finalConfig }) {
|
|
23
|
-
function colWidth(cw, cols) {
|
|
23
|
+
function colWidth(cw, cols, columnIndex) {
|
|
24
24
|
if (cols.count <= 1) return cw;
|
|
25
|
+
const authoredWidth = cols.widths?.[columnIndex];
|
|
26
|
+
if (authoredWidth !== void 0) return authoredWidth;
|
|
25
27
|
return Math.floor((cw - (cols.count - 1) * cols.gap) / cols.count);
|
|
26
28
|
}
|
|
27
29
|
function contentWidth(config) {
|
|
@@ -29,17 +31,23 @@ function computePerBlockMeasureInputs({ blocks, bodyConfig, finalConfig }) {
|
|
|
29
31
|
}
|
|
30
32
|
const { configs: sectionConfigs, breakIndices } = collectSectionConfigs(blocks, bodyConfig, finalConfig);
|
|
31
33
|
let sectionIdx = 0;
|
|
34
|
+
let columnIndex = 0;
|
|
32
35
|
const widths = [];
|
|
33
36
|
const marginTops = [];
|
|
34
37
|
const pageHeights = [];
|
|
35
38
|
const marginBottoms = [];
|
|
36
39
|
for (let i = 0; i < blocks.length; i++) {
|
|
37
40
|
const config = sectionConfigs[sectionIdx] ?? finalConfig;
|
|
38
|
-
|
|
41
|
+
const columns = config.columns ?? SINGLE_COLUMN_LAYOUT;
|
|
42
|
+
widths.push(colWidth(contentWidth(config), columns, columnIndex));
|
|
39
43
|
marginTops.push(config.margins.top);
|
|
40
44
|
pageHeights.push(config.pageSize.h);
|
|
41
45
|
marginBottoms.push(config.margins.bottom);
|
|
42
|
-
if (sectionIdx < breakIndices.length && i === breakIndices[sectionIdx])
|
|
46
|
+
if (sectionIdx < breakIndices.length && i === breakIndices[sectionIdx]) {
|
|
47
|
+
sectionIdx++;
|
|
48
|
+
columnIndex = 0;
|
|
49
|
+
} else if (blocks[i]?.kind === "pageBreak") columnIndex = 0;
|
|
50
|
+
else if (blocks[i]?.kind === "columnBreak") columnIndex = (columnIndex + 1) % columns.count;
|
|
43
51
|
}
|
|
44
52
|
return {
|
|
45
53
|
widths,
|
|
@@ -1430,9 +1430,18 @@ function tableRowAttrsToFormatting(attrs) {
|
|
|
1430
1430
|
function convertPMTableCell(node, documentCounts) {
|
|
1431
1431
|
const attrs = expectTableCellAttrs(node);
|
|
1432
1432
|
const content = [];
|
|
1433
|
+
let previousStandaloneTextBox = null;
|
|
1433
1434
|
node.forEach((contentNode) => {
|
|
1434
|
-
if (contentNode.type.name === "paragraph")
|
|
1435
|
-
|
|
1435
|
+
if (contentNode.type.name === "paragraph") {
|
|
1436
|
+
content.push(convertPMParagraph(contentNode, documentCounts));
|
|
1437
|
+
previousStandaloneTextBox = null;
|
|
1438
|
+
} else if (contentNode.type.name === "table") {
|
|
1439
|
+
content.push(convertPMTable(contentNode, documentCounts));
|
|
1440
|
+
previousStandaloneTextBox = null;
|
|
1441
|
+
} else if (contentNode.type.name === "textBox") previousStandaloneTextBox = appendTextBoxBlock(content, contentNode, {
|
|
1442
|
+
pendingPageBreaks: 0,
|
|
1443
|
+
previousStandaloneTextBox
|
|
1444
|
+
});
|
|
1436
1445
|
});
|
|
1437
1446
|
const cell = {
|
|
1438
1447
|
type: "tableCell",
|
|
@@ -24,15 +24,18 @@ function toProseDoc(document, options) {
|
|
|
24
24
|
const styleResolver = createStyleEngine(options?.styles ?? document.package.styles);
|
|
25
25
|
const theme = options?.theme ?? document.package.theme ?? null;
|
|
26
26
|
let textBoxGroupIndex = 0;
|
|
27
|
+
const nextTextBoxGroupId = () => String(textBoxGroupIndex++);
|
|
27
28
|
const convertBodyBlocks = (blocks) => {
|
|
28
29
|
const out = [];
|
|
29
30
|
for (const block of blocks) if (block.type === "paragraph") {
|
|
30
31
|
const pbPos = paragraphPageBreakPosition(block);
|
|
31
32
|
if (pbPos === "before") out.push(schema.node("pageBreak"));
|
|
32
|
-
out.push(...convertParagraphWithTextBoxes(block, styleResolver,
|
|
33
|
-
textBoxGroupIndex += 1;
|
|
33
|
+
out.push(...convertParagraphWithTextBoxes(block, styleResolver, { textBoxGroupId: nextTextBoxGroupId() }));
|
|
34
34
|
if (pbPos === "after") out.push(schema.node("pageBreak"));
|
|
35
|
-
} else if (block.type === "table") out.push(convertTable(block, styleResolver,
|
|
35
|
+
} else if (block.type === "table") out.push(convertTable(block, styleResolver, {
|
|
36
|
+
theme,
|
|
37
|
+
nextTextBoxGroupId
|
|
38
|
+
}));
|
|
36
39
|
else out.push(convertBlockSdt(block, convertBodyBlocks));
|
|
37
40
|
return out;
|
|
38
41
|
};
|
|
@@ -535,7 +538,7 @@ function paragraphContentHasMeaningfulContent(content) {
|
|
|
535
538
|
if (content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo") return content.content.some(paragraphContentHasMeaningfulContent);
|
|
536
539
|
return true;
|
|
537
540
|
}
|
|
538
|
-
function convertTable(table, styleResolver,
|
|
541
|
+
function convertTable(table, styleResolver, context) {
|
|
539
542
|
const rowSpanMap = calculateRowSpans(table);
|
|
540
543
|
const columnWidths = table.columnWidths;
|
|
541
544
|
const totalWidth = columnWidths?.reduce((sum, w) => sum + w, 0) ?? 0;
|
|
@@ -546,6 +549,7 @@ function convertTable(table, styleResolver, theme) {
|
|
|
546
549
|
const fallbackTableStyle = tableStyleId ? void 0 : defaultTableStyle;
|
|
547
550
|
const conditionalTableStyleId = tableStyle?.styleId ?? fallbackTableStyle?.styleId;
|
|
548
551
|
const resolvedTableBorders = table.formatting?.borders ?? tableStyle?.tblPr?.borders ?? fallbackTableStyle?.tblPr?.borders;
|
|
552
|
+
const resolvedTableIndent = table.formatting?.indent ?? tableStyle?.tblPr?.indent ?? fallbackTableStyle?.tblPr?.indent;
|
|
549
553
|
const tableCellMargins = table.formatting?.cellMargins ?? tableStyle?.tblPr?.cellMargins ?? fallbackTableStyle?.tblPr?.cellMargins;
|
|
550
554
|
let cellMarginsAttr;
|
|
551
555
|
if (tableCellMargins) {
|
|
@@ -566,6 +570,7 @@ function convertTable(table, styleResolver, theme) {
|
|
|
566
570
|
if (cellMarginsAttr) attrs.cellMargins = cellMarginsAttr;
|
|
567
571
|
if (table.formatting?.look) attrs.look = table.formatting.look;
|
|
568
572
|
if (table.formatting?.borders) attrs.borders = table.formatting.borders;
|
|
573
|
+
if (resolvedTableIndent) attrs._resolvedIndent = resolvedTableIndent;
|
|
569
574
|
if (table.formatting) attrs._originalFormatting = table.formatting;
|
|
570
575
|
if (table.propertyChanges && table.propertyChanges.length > 0) attrs.tblPrChange = [...table.propertyChanges];
|
|
571
576
|
const conditionalStyles = {};
|
|
@@ -604,7 +609,7 @@ function convertTable(table, styleResolver, theme) {
|
|
|
604
609
|
})();
|
|
605
610
|
})();
|
|
606
611
|
if (bandingEnabledH && !isFirstRowStyled && !isLastRow) dataRowIndex++;
|
|
607
|
-
return convertTableRow(row, styleResolver, isFirstRowStyled, columnWidths, totalWidth, conditionalStyles, rowBandStyle, bandingEnabledV, look, resolvedTableBorders, rowIndex, totalRows, totalColumns, rowSpanMap, cellMarginsAttr
|
|
612
|
+
return convertTableRow(row, styleResolver, context, isFirstRowStyled, columnWidths, totalWidth, conditionalStyles, rowBandStyle, bandingEnabledV, look, resolvedTableBorders, rowIndex, totalRows, totalColumns, rowSpanMap, cellMarginsAttr);
|
|
608
613
|
});
|
|
609
614
|
return schema.node("table", attrs, rows);
|
|
610
615
|
}
|
|
@@ -620,7 +625,7 @@ function countTableColumns(rows) {
|
|
|
620
625
|
/**
|
|
621
626
|
* Convert a TableRow to a ProseMirror table row node
|
|
622
627
|
*/
|
|
623
|
-
function convertTableRow(row, styleResolver, isHeaderRow, columnWidths, totalWidth, conditionalStyles, rowBandStyle, bandingEnabledV, tableLook, tableBorders, rowIndex, totalRows, totalColumns, rowSpanMap, defaultCellMargins
|
|
628
|
+
function convertTableRow(row, styleResolver, context, isHeaderRow, columnWidths, totalWidth, conditionalStyles, rowBandStyle, bandingEnabledV, tableLook, tableBorders, rowIndex, totalRows, totalColumns, rowSpanMap, defaultCellMargins) {
|
|
624
629
|
const attrs = { isHeader: !!row.formatting?.header };
|
|
625
630
|
if (row.formatting?.height?.value !== void 0) attrs.height = row.formatting.height.value;
|
|
626
631
|
if (row.formatting?.heightRule) attrs.heightRule = row.formatting.heightRule;
|
|
@@ -695,7 +700,7 @@ function convertTableRow(row, styleResolver, isHeaderRow, columnWidths, totalWid
|
|
|
695
700
|
if (cellIsFirstRow && cellIsLastCol && (tableLook?.firstRow || rowCnf?.firstRow || cellCnf?.firstRow) && (tableLook?.lastColumn || rowCnf?.lastColumn || cellCnf?.lastColumn)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.neCell);
|
|
696
701
|
if (cellIsLastRow && cellIsFirstCol && (tableLook?.lastRow || rowCnf?.lastRow || cellCnf?.lastRow) && (tableLook?.firstColumn || rowCnf?.firstColumn || cellCnf?.firstColumn)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.swCell);
|
|
697
702
|
if (cellIsLastRow && cellIsLastCol && (tableLook?.lastRow || rowCnf?.lastRow || cellCnf?.lastRow) && (tableLook?.lastColumn || rowCnf?.lastColumn || cellCnf?.lastColumn)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.seCell);
|
|
698
|
-
cells.push(convertTableCell(cell, styleResolver, isHeaderRow, gridWidth, cellConditionalStyle, tableBorders, isFirstRow, isLastRow, isFirstCol, isLastCol, calculatedRowSpan, preserveVMergeRestart, rowSpanInfo?.continuationCells, defaultCellMargins
|
|
703
|
+
cells.push(convertTableCell(cell, styleResolver, context, isHeaderRow, gridWidth, cellConditionalStyle, tableBorders, isFirstRow, isLastRow, isFirstCol, isLastCol, calculatedRowSpan, preserveVMergeRestart, rowSpanInfo?.continuationCells, defaultCellMargins));
|
|
699
704
|
}
|
|
700
705
|
return schema.node("tableRow", attrs, cells);
|
|
701
706
|
}
|
|
@@ -724,7 +729,8 @@ function resolveThemedBorderColors(borders, theme) {
|
|
|
724
729
|
/**
|
|
725
730
|
* Convert a TableCell to a ProseMirror table cell node
|
|
726
731
|
*/
|
|
727
|
-
function convertTableCell(cell, styleResolver, isHeader, gridWidthPercent, conditionalStyle, tableBorders, isFirstRow, isLastRow, isFirstCol, isLastCol, calculatedRowSpan, preserveVMergeRestart, vMergeContinuationCells, defaultCellMargins
|
|
732
|
+
function convertTableCell(cell, styleResolver, context, isHeader, gridWidthPercent, conditionalStyle, tableBorders, isFirstRow, isLastRow, isFirstCol, isLastCol, calculatedRowSpan, preserveVMergeRestart, vMergeContinuationCells, defaultCellMargins) {
|
|
733
|
+
const { theme } = context;
|
|
728
734
|
const formatting = cell.formatting;
|
|
729
735
|
const rowspan = calculatedRowSpan ?? 1;
|
|
730
736
|
let width = formatting?.width?.value;
|
|
@@ -776,8 +782,12 @@ function convertTableCell(cell, styleResolver, isHeader, gridWidthPercent, condi
|
|
|
776
782
|
if (preserveVMergeRestart) attrs._preserveVMergeRestart = true;
|
|
777
783
|
if (vMergeContinuationCells && vMergeContinuationCells.length > 0) attrs._docxVMergeContinuationCells = vMergeContinuationCells;
|
|
778
784
|
const contentNodes = [];
|
|
779
|
-
for (const content of cell.content) if (content.type === "paragraph") contentNodes.push(
|
|
780
|
-
|
|
785
|
+
for (const content of cell.content) if (content.type === "paragraph") contentNodes.push(...convertParagraphWithTextBoxes(content, styleResolver, {
|
|
786
|
+
textBoxGroupId: context.nextTextBoxGroupId(),
|
|
787
|
+
...conditionalStyle?.rPr !== void 0 ? { extraRunFormatting: conditionalStyle.rPr } : {},
|
|
788
|
+
...conditionalStyle?.pPr !== void 0 ? { tableParagraphOverlay: conditionalStyle.pPr } : {}
|
|
789
|
+
}));
|
|
790
|
+
else contentNodes.push(convertTable(content, styleResolver, context));
|
|
781
791
|
if (contentNodes.length === 0) contentNodes.push(schema.node("paragraph", {}, []));
|
|
782
792
|
const nodeType = isHeader ? "tableHeader" : "tableCell";
|
|
783
793
|
return schema.node(nodeType, attrs, contentNodes);
|
|
@@ -1214,13 +1224,9 @@ function convertShape(shape) {
|
|
|
1214
1224
|
position
|
|
1215
1225
|
});
|
|
1216
1226
|
}
|
|
1217
|
-
|
|
1218
|
-
* Convert a paragraph block to PM nodes, extracting text boxes as sibling nodes.
|
|
1219
|
-
* Skips ghost empty paragraphs that only contained text box drawings.
|
|
1220
|
-
*/
|
|
1221
|
-
function convertParagraphWithTextBoxes(block, styleResolver, textBoxGroupId) {
|
|
1227
|
+
function convertParagraphWithTextBoxes(block, styleResolver, { textBoxGroupId, extraRunFormatting, tableParagraphOverlay }) {
|
|
1222
1228
|
const textBoxes = extractTextBoxesFromParagraph(block);
|
|
1223
|
-
const pmParagraph = convertParagraph(block, styleResolver);
|
|
1229
|
+
const pmParagraph = convertParagraph(block, styleResolver, void 0, extraRunFormatting, tableParagraphOverlay);
|
|
1224
1230
|
const nodes = [];
|
|
1225
1231
|
const isEmptyAfterExtraction = textBoxes.length > 0 && pmParagraph.content.size === 0;
|
|
1226
1232
|
const keepWrapperParagraph = isEmptyAfterExtraction && hasParagraphBoundaryPayload(block, pmParagraph);
|
|
@@ -1361,12 +1367,14 @@ function headerFooterToProseDoc(content, options) {
|
|
|
1361
1367
|
const styleResolver = options?.styles ? createStyleEngine(options.styles) : null;
|
|
1362
1368
|
const theme = options?.theme ?? null;
|
|
1363
1369
|
let textBoxGroupIndex = 0;
|
|
1370
|
+
const nextTextBoxGroupId = () => String(textBoxGroupIndex++);
|
|
1364
1371
|
const convertBlocks = (blocks) => {
|
|
1365
1372
|
const out = [];
|
|
1366
|
-
for (const block of blocks) if (block.type === "paragraph") {
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1373
|
+
for (const block of blocks) if (block.type === "paragraph") out.push(...convertParagraphWithTextBoxes(block, styleResolver, { textBoxGroupId: nextTextBoxGroupId() }));
|
|
1374
|
+
else if (block.type === "table") out.push(convertTable(block, styleResolver, {
|
|
1375
|
+
theme,
|
|
1376
|
+
nextTextBoxGroupId
|
|
1377
|
+
}));
|
|
1370
1378
|
else out.push(convertBlockSdt(block, convertBlocks));
|
|
1371
1379
|
return out;
|
|
1372
1380
|
};
|
|
@@ -152,6 +152,7 @@ const tableSpec = {
|
|
|
152
152
|
cellMargins: { default: null },
|
|
153
153
|
look: { default: null },
|
|
154
154
|
borders: { default: null },
|
|
155
|
+
_resolvedIndent: { default: null },
|
|
155
156
|
_originalFormatting: { default: null },
|
|
156
157
|
tblPrChange: { default: null }
|
|
157
158
|
},
|
|
@@ -298,7 +299,7 @@ function buildCellWidthStyles(attrs) {
|
|
|
298
299
|
return styles;
|
|
299
300
|
}
|
|
300
301
|
const tableCellSpec = {
|
|
301
|
-
content: "(paragraph | table)+",
|
|
302
|
+
content: "(paragraph | table | textBox)+",
|
|
302
303
|
tableRole: "cell",
|
|
303
304
|
isolating: true,
|
|
304
305
|
attrs: {
|
|
@@ -351,7 +352,7 @@ const tableCellSpec = {
|
|
|
351
352
|
}
|
|
352
353
|
};
|
|
353
354
|
const tableHeaderSpec = {
|
|
354
|
-
content: "(paragraph | table)+",
|
|
355
|
+
content: "(paragraph | table | textBox)+",
|
|
355
356
|
tableRole: "header_cell",
|
|
356
357
|
isolating: true,
|
|
357
358
|
attrs: {
|
|
@@ -347,7 +347,8 @@ type TableAttrs = {
|
|
|
347
347
|
right?: number;
|
|
348
348
|
}; /** Table look flags for conditional formatting (w:tblLook) */
|
|
349
349
|
look?: document_d_exports.TableLook; /** Table-level borders (w:tblBorders) — full BorderSpec per side */
|
|
350
|
-
borders?: document_d_exports.TableBorders; /**
|
|
350
|
+
borders?: document_d_exports.TableBorders; /** Effective table indent after style resolution. PM-only; never serialized. */
|
|
351
|
+
_resolvedIndent?: NonNullable<document_d_exports.TableFormatting["indent"]>; /** Original table formatting from DOCX for lossless round-trip serialization */
|
|
351
352
|
_originalFormatting?: document_d_exports.TableFormatting; /** Tracked table property changes (w:tblPrChange) for round-trip + accept/reject */
|
|
352
353
|
tblPrChange?: document_d_exports.TablePropertyChange[];
|
|
353
354
|
};
|
|
@@ -105,7 +105,7 @@ function calculateTabWidth(currentXPx, context, following = {}) {
|
|
|
105
105
|
...nextStop.leader !== void 0 ? { leader: nextStop.leader } : {},
|
|
106
106
|
alignment: "bar"
|
|
107
107
|
};
|
|
108
|
-
if (width
|
|
108
|
+
if (width <= 0) {
|
|
109
109
|
const defaultTabPx = twipsToPixels(defaultTabInterval);
|
|
110
110
|
let fallbackWidth = defaultTabPx - currentXPx % defaultTabPx;
|
|
111
111
|
if (fallbackWidth <= 0) fallbackWidth = defaultTabPx;
|
package/dist/server.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ import { DeriveBlockIdInput, FolioBlockId, deriveBlockId, getFolioParaIdFromBloc
|
|
|
7
7
|
import { CreateEmptyDocumentOptions, createEmptyDocument } from "./utils/createDocument.js";
|
|
8
8
|
import { createDocx } from "./docx/rezip.js";
|
|
9
9
|
import { CreateCommentReplyInput, replyToComment } from "./docx/replyToComment.js";
|
|
10
|
+
import { DocxArchiveError } from "./docx/server/boundedArchive.js";
|
|
11
|
+
import { DocxParagraphSource, ExtractedDocxParagraph, ExtractedDocxText, extractDocxText } from "./docx/server/extractDocxText.js";
|
|
10
12
|
import { GenerateRedlineDocxOptions, GenerateRedlineDocxResult, generateRedlineDocx } from "./redline.js";
|
|
11
13
|
import { FolioBlockDiff, FolioFormatProperty, FolioVersionDiff, FolioVersionDiffSegment, compareDocxVersions } from "./version-comparison.js";
|
|
12
|
-
export { type ApplyFolioAIEditsToBufferOptions, type ApplyFolioAIEditsToBufferResult, type CreateCommentReplyInput, type CreateEmptyDocumentOptions, type DeriveBlockIdInput, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditSnapshot, type FolioAIInlineFormatting, type FolioAITextRangeHandle, type FolioApplyDocumentOperationsOptions, type FolioApplyOperationsOptions, type FolioBlockDiff, type FolioBlockId, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationStatus, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocumentStory, type FolioDocumentStoryHandle, FolioDocxReviewer, type FolioDocxReviewerOptions, type FolioFormatProperty, type FolioReviewChange, type FolioReviewChangeFilter, type FolioReviewChangeKind, type FolioReviewComment, type FolioReviewCommentFilter, type FolioReviewCommentReply, type FolioReviewReplyInput, type FolioVersionDiff, type FolioVersionDiffSegment, type GenerateRedlineDocxOptions, type GenerateRedlineDocxResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioAIEditsToBuffer, assertSupportedFolioDocumentOperationVersion, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, deriveBlockId, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch, replyToComment };
|
|
14
|
+
export { type ApplyFolioAIEditsToBufferOptions, type ApplyFolioAIEditsToBufferResult, type CreateCommentReplyInput, type CreateEmptyDocumentOptions, type DeriveBlockIdInput, DocxArchiveError, type DocxParagraphSource, type ExtractedDocxParagraph, type ExtractedDocxText, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditSnapshot, type FolioAIInlineFormatting, type FolioAITextRangeHandle, type FolioApplyDocumentOperationsOptions, type FolioApplyOperationsOptions, type FolioBlockDiff, type FolioBlockId, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationStatus, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocumentStory, type FolioDocumentStoryHandle, FolioDocxReviewer, type FolioDocxReviewerOptions, type FolioFormatProperty, type FolioReviewChange, type FolioReviewChangeFilter, type FolioReviewChangeKind, type FolioReviewComment, type FolioReviewCommentFilter, type FolioReviewCommentReply, type FolioReviewReplyInput, type FolioVersionDiff, type FolioVersionDiffSegment, type GenerateRedlineDocxOptions, type GenerateRedlineDocxResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioAIEditsToBuffer, assertSupportedFolioDocumentOperationVersion, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, deriveBlockId, extractDocxText, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch, replyToComment };
|
package/dist/server.js
CHANGED
|
@@ -7,4 +7,6 @@ import { replyToComment } from "./docx/replyToComment.js";
|
|
|
7
7
|
import { FolioDocxReviewer, applyFolioAIEditsToBuffer } from "./ai-edits/headless.js";
|
|
8
8
|
import { compareDocxVersions } from "./version-comparison.js";
|
|
9
9
|
import { generateRedlineDocx } from "./redline.js";
|
|
10
|
-
|
|
10
|
+
import { DocxArchiveError } from "./docx/server/boundedArchive.js";
|
|
11
|
+
import { extractDocxText } from "./docx/server/extractDocxText.js";
|
|
12
|
+
export { DocxArchiveError, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FolioDocxReviewer, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioAIEditsToBuffer, assertSupportedFolioDocumentOperationVersion, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, deriveBlockId, extractDocxText, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch, replyToComment };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { eighthsToPixels, formatPx, halfPointsToPixels, halfPointsToPoints, twipsToPixels } from "./units.js";
|
|
1
|
+
import { AUTO_PARAGRAPH_SPACING_PX, eighthsToPixels, formatPx, halfPointsToPixels, halfPointsToPoints, twipsToPixels } from "./units.js";
|
|
2
2
|
import { resolveFontFamily, resolveThemeFont } from "./fontResolver.js";
|
|
3
3
|
import { resolveColor, resolveHighlightToCss, resolveShadingColor } from "./colorResolver.js";
|
|
4
4
|
//#region src/utils/formatToStyle.ts
|
|
@@ -89,9 +89,9 @@ function paragraphToStyle(formatting, theme) {
|
|
|
89
89
|
if (!formatting) return {};
|
|
90
90
|
const style = {};
|
|
91
91
|
if (formatting.alignment) style.textAlign = mapAlignment(formatting.alignment);
|
|
92
|
-
if (formatting.beforeAutospacing) style.marginTop = formatPx(
|
|
92
|
+
if (formatting.beforeAutospacing) style.marginTop = formatPx(AUTO_PARAGRAPH_SPACING_PX);
|
|
93
93
|
else if (formatting.spaceBefore !== void 0) style.marginTop = formatPx(twipsToPixels(formatting.spaceBefore));
|
|
94
|
-
if (formatting.afterAutospacing) style.marginBottom = formatPx(
|
|
94
|
+
if (formatting.afterAutospacing) style.marginBottom = formatPx(AUTO_PARAGRAPH_SPACING_PX);
|
|
95
95
|
else if (formatting.spaceAfter !== void 0) style.marginBottom = formatPx(twipsToPixels(formatting.spaceAfter));
|
|
96
96
|
if (formatting.lineSpacing !== void 0 && formatting.lineSpacing > 0) if (formatting.lineSpacingRule === "exact") {
|
|
97
97
|
const exactPx = twipsToPixels(formatting.lineSpacing);
|
package/dist/utils/units.d.ts
CHANGED
|
@@ -13,16 +13,16 @@
|
|
|
13
13
|
*/
|
|
14
14
|
/** Twips per inch (1 inch = 1440 twips) */
|
|
15
15
|
declare const TWIPS_PER_INCH = 1440;
|
|
16
|
+
/** Pixels per inch at standard DPI */
|
|
17
|
+
declare const PIXELS_PER_INCH = 96;
|
|
16
18
|
/**
|
|
17
19
|
* Word's auto paragraph spacing in px. HTML-origin paragraphs use
|
|
18
20
|
* `w:beforeAutospacing`/`w:afterAutospacing` instead of explicit before/after;
|
|
19
|
-
* Word ignores any explicit value on such paragraphs and renders
|
|
20
|
-
* value is empirical (Word's rendered auto gap), not derivable from a
|
|
21
|
-
* conversion. See eigenpal/docx-editor#823.
|
|
21
|
+
* Word ignores any explicit value on such paragraphs and renders a 14pt gap.
|
|
22
|
+
* The value is empirical (Word's rendered auto gap), not derivable from a
|
|
23
|
+
* twips conversion. See eigenpal/docx-editor#823.
|
|
22
24
|
*/
|
|
23
|
-
declare const AUTO_PARAGRAPH_SPACING_PX
|
|
24
|
-
/** Pixels per inch at standard DPI */
|
|
25
|
-
declare const PIXELS_PER_INCH = 96;
|
|
25
|
+
declare const AUTO_PARAGRAPH_SPACING_PX: number;
|
|
26
26
|
/**
|
|
27
27
|
* Convert twips to pixels (at 96 DPI)
|
|
28
28
|
*
|
package/dist/utils/units.js
CHANGED
|
@@ -15,14 +15,6 @@
|
|
|
15
15
|
const STANDARD_DPI = 96;
|
|
16
16
|
/** Twips per inch (1 inch = 1440 twips) */
|
|
17
17
|
const TWIPS_PER_INCH = 1440;
|
|
18
|
-
/**
|
|
19
|
-
* Word's auto paragraph spacing in px. HTML-origin paragraphs use
|
|
20
|
-
* `w:beforeAutospacing`/`w:afterAutospacing` instead of explicit before/after;
|
|
21
|
-
* Word ignores any explicit value on such paragraphs and renders ~14px. The
|
|
22
|
-
* value is empirical (Word's rendered auto gap), not derivable from a twips
|
|
23
|
-
* conversion. See eigenpal/docx-editor#823.
|
|
24
|
-
*/
|
|
25
|
-
const AUTO_PARAGRAPH_SPACING_PX = 14;
|
|
26
18
|
/** EMUs per inch (1 inch = 914400 EMUs) */
|
|
27
19
|
const EMUS_PER_INCH = 914400;
|
|
28
20
|
/** Points per inch (1 inch = 72 points) */
|
|
@@ -34,6 +26,14 @@ const EIGHTHS_PER_INCH = 576;
|
|
|
34
26
|
/** Pixels per inch at standard DPI */
|
|
35
27
|
const PIXELS_PER_INCH = STANDARD_DPI;
|
|
36
28
|
/**
|
|
29
|
+
* Word's auto paragraph spacing in px. HTML-origin paragraphs use
|
|
30
|
+
* `w:beforeAutospacing`/`w:afterAutospacing` instead of explicit before/after;
|
|
31
|
+
* Word ignores any explicit value on such paragraphs and renders a 14pt gap.
|
|
32
|
+
* The value is empirical (Word's rendered auto gap), not derivable from a
|
|
33
|
+
* twips conversion. See eigenpal/docx-editor#823.
|
|
34
|
+
*/
|
|
35
|
+
const AUTO_PARAGRAPH_SPACING_PX = pointsToPixels(14);
|
|
36
|
+
/**
|
|
37
37
|
* Convert twips to pixels (at 96 DPI)
|
|
38
38
|
*
|
|
39
39
|
* 1 inch = 1440 twips = 96 pixels
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stll/folio-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Headless, framework-neutral core of folio: the OOXML (.docx) parser, document model, ProseMirror integration, and page-layout engine. No React.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"document-model",
|