@stll/folio-core 0.31.0 → 0.31.1
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/layout-bridge/convert/paragraphFrames.js +7 -1
- package/dist/layout-engine/types.d.ts +2 -0
- package/dist/layout-painter/renderPage.d.ts +12 -7
- package/dist/layout-painter/renderPage.js +56 -15
- package/dist/layout-painter/renderTable.js +41 -2
- package/dist/layout-painter/tableRowPaintGeometry.d.ts +15 -0
- package/dist/layout-painter/tableRowPaintGeometry.js +22 -0
- package/package.json +1 -1
|
@@ -61,10 +61,11 @@ function frameWrapType(frame) {
|
|
|
61
61
|
function toFrameTextBox(content, frame, nextBlockId) {
|
|
62
62
|
const first = content.at(0);
|
|
63
63
|
const last = content.at(-1);
|
|
64
|
+
const hasAuthoredWidth = frame.width !== void 0;
|
|
64
65
|
const textBox = {
|
|
65
66
|
kind: "textBox",
|
|
66
67
|
id: nextBlockId(),
|
|
67
|
-
width: frame.width ??
|
|
68
|
+
width: frame.width ?? 0,
|
|
68
69
|
margins: {
|
|
69
70
|
top: 0,
|
|
70
71
|
right: 0,
|
|
@@ -82,6 +83,11 @@ function toFrameTextBox(content, frame, nextBlockId) {
|
|
|
82
83
|
...first?.pmStart !== void 0 ? { pmStart: first.pmStart } : {},
|
|
83
84
|
...last?.pmEnd !== void 0 ? { pmEnd: last.pmEnd } : {}
|
|
84
85
|
};
|
|
86
|
+
if (!hasAuthoredWidth) {
|
|
87
|
+
textBox.widthMode = "intrinsic";
|
|
88
|
+
textBox.autoFit = "shape";
|
|
89
|
+
textBox.textWrap = "none";
|
|
90
|
+
}
|
|
85
91
|
if (frame.height !== void 0) textBox.height = frame.height;
|
|
86
92
|
const position = framePosition(frame);
|
|
87
93
|
if (position !== void 0) textBox.position = position;
|
|
@@ -769,6 +769,8 @@ type TextBoxBlock = {
|
|
|
769
769
|
id: BlockId;
|
|
770
770
|
/** Width in pixels */
|
|
771
771
|
width: number;
|
|
772
|
+
/** Whether width is authored or derived from the live rendered content. */
|
|
773
|
+
widthMode?: "intrinsic";
|
|
772
774
|
/** Height in pixels (may be auto-calculated) */
|
|
773
775
|
height?: number;
|
|
774
776
|
/** Text fitting behavior */
|
|
@@ -177,6 +177,12 @@ type HeaderFooterLayoutInfo = {
|
|
|
177
177
|
left: number;
|
|
178
178
|
};
|
|
179
179
|
};
|
|
180
|
+
type HeaderFooterHorizontalPosition = {
|
|
181
|
+
relativeTo?: string;
|
|
182
|
+
posOffset?: number;
|
|
183
|
+
align?: string;
|
|
184
|
+
alignment?: string;
|
|
185
|
+
};
|
|
180
186
|
/**
|
|
181
187
|
* Resolve the CSS `left` (px string) for an anchored object (image or text box)
|
|
182
188
|
* in a header/footer, honoring `wp:positionH` (relativeTo page/margin, align
|
|
@@ -184,12 +190,11 @@ type HeaderFooterLayoutInfo = {
|
|
|
184
190
|
* a page-centered banner in the header lands centered like Word, not pinned to
|
|
185
191
|
* the left. Ported from eigenpal/docx-editor#700.
|
|
186
192
|
*/
|
|
187
|
-
declare function resolveHeaderFooterFloatLeft(width: number, h:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
} | undefined, layout: HeaderFooterLayoutInfo): string;
|
|
193
|
+
declare function resolveHeaderFooterFloatLeft(width: number, h: HeaderFooterHorizontalPosition | undefined, layout: HeaderFooterLayoutInfo): string;
|
|
194
|
+
declare function resolveHeaderFooterIntrinsicFrameHorizontalPosition(h: HeaderFooterHorizontalPosition | undefined, layout: HeaderFooterLayoutInfo): {
|
|
195
|
+
left: string;
|
|
196
|
+
transform?: string;
|
|
197
|
+
};
|
|
193
198
|
/**
|
|
194
199
|
* Resolve the on-page coordinates of an anchored floating image.
|
|
195
200
|
*
|
|
@@ -297,4 +302,4 @@ declare function findPageShellForPmPos(container: HTMLElement, pmPos: number): {
|
|
|
297
302
|
isExact: boolean;
|
|
298
303
|
} | null;
|
|
299
304
|
//#endregion
|
|
300
|
-
export { type AnchoredImagePosition, FootnoteRenderItem, type HeaderFooterContent, HeaderFooterLayoutInfo, PAGE_CLASS_NAMES, PAINTER_PAINTED_EVENT, PageFloatingImage, type PageGeometry, PainterPaintedDetail, type RenderContext, RenderPageOptions, applySectionHeaderFooterOptions, calculateFootnoteAreaRenderHeight, computePageFingerprint, emuToPixels, findPageShellForPmPos, floatingTableReservesBand, getDefaultPageFontFamily, isFloatingImageRun, renderAllPagesNow, renderFloatingImagesLayer, renderFootnoteArea, renderPage, renderPages, resolveAnchoredImagePosition, resolveHeaderFooterFloatLeft };
|
|
305
|
+
export { type AnchoredImagePosition, FootnoteRenderItem, type HeaderFooterContent, HeaderFooterLayoutInfo, PAGE_CLASS_NAMES, PAINTER_PAINTED_EVENT, PageFloatingImage, type PageGeometry, PainterPaintedDetail, type RenderContext, RenderPageOptions, applySectionHeaderFooterOptions, calculateFootnoteAreaRenderHeight, computePageFingerprint, emuToPixels, findPageShellForPmPos, floatingTableReservesBand, getDefaultPageFontFamily, isFloatingImageRun, renderAllPagesNow, renderFloatingImagesLayer, renderFootnoteArea, renderPage, renderPages, resolveAnchoredImagePosition, resolveHeaderFooterFloatLeft, resolveHeaderFooterIntrinsicFrameHorizontalPosition };
|
|
@@ -207,6 +207,37 @@ function applyContentAreaStyles(element, page) {
|
|
|
207
207
|
function getPositionAlignment(position) {
|
|
208
208
|
return position?.align ?? position?.alignment;
|
|
209
209
|
}
|
|
210
|
+
function resolveHeaderFooterHorizontalAnchorPoint(h, layout) {
|
|
211
|
+
if (!h) return {
|
|
212
|
+
left: 0,
|
|
213
|
+
alignment: "left"
|
|
214
|
+
};
|
|
215
|
+
if (h.posOffset !== void 0) {
|
|
216
|
+
const pageOffset = h.relativeTo === "page" ? -layout.flowLeft : 0;
|
|
217
|
+
return {
|
|
218
|
+
left: emuToPixels(h.posOffset) + pageOffset,
|
|
219
|
+
alignment: "left"
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
let align = getPositionAlignment(h);
|
|
223
|
+
if (align === "inside") align = "left";
|
|
224
|
+
else if (align === "outside") align = "right";
|
|
225
|
+
const alignment = align === "center" || align === "right" ? align : "left";
|
|
226
|
+
const frameWidth = h.relativeTo === "page" ? layout.pageWidth : layout.contentWidth;
|
|
227
|
+
const frameLeft = h.relativeTo === "page" ? -layout.flowLeft : 0;
|
|
228
|
+
if (alignment === "center") return {
|
|
229
|
+
left: frameLeft + frameWidth / 2,
|
|
230
|
+
alignment
|
|
231
|
+
};
|
|
232
|
+
if (alignment === "right") return {
|
|
233
|
+
left: frameLeft + frameWidth,
|
|
234
|
+
alignment
|
|
235
|
+
};
|
|
236
|
+
return {
|
|
237
|
+
left: frameLeft,
|
|
238
|
+
alignment
|
|
239
|
+
};
|
|
240
|
+
}
|
|
210
241
|
function resolveHeaderFooterFloatTop(floatImg, layout) {
|
|
211
242
|
const v = floatImg.position.vertical;
|
|
212
243
|
if (!v) return floatImg.paragraphY;
|
|
@@ -261,20 +292,24 @@ function resolveHeaderFooterFloatingTablePosition(floating, measure, layout, sou
|
|
|
261
292
|
* the left. Ported from eigenpal/docx-editor#700.
|
|
262
293
|
*/
|
|
263
294
|
function resolveHeaderFooterFloatLeft(width, h, layout) {
|
|
264
|
-
|
|
265
|
-
let
|
|
266
|
-
if (
|
|
267
|
-
else if (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
295
|
+
const anchor = resolveHeaderFooterHorizontalAnchorPoint(h, layout);
|
|
296
|
+
let widthFactor = 0;
|
|
297
|
+
if (anchor.alignment === "right") widthFactor = 1;
|
|
298
|
+
else if (anchor.alignment === "center") widthFactor = .5;
|
|
299
|
+
const left = anchor.left - width * widthFactor;
|
|
300
|
+
return left === 0 ? "0" : `${left}px`;
|
|
301
|
+
}
|
|
302
|
+
function resolveHeaderFooterIntrinsicFrameHorizontalPosition(h, layout) {
|
|
303
|
+
const anchor = resolveHeaderFooterHorizontalAnchorPoint(h, layout);
|
|
304
|
+
if (anchor.alignment === "center") return {
|
|
305
|
+
left: `${anchor.left}px`,
|
|
306
|
+
transform: "translateX(-50%)"
|
|
307
|
+
};
|
|
308
|
+
if (anchor.alignment === "right") return {
|
|
309
|
+
left: `${anchor.left}px`,
|
|
310
|
+
transform: "translateX(-100%)"
|
|
311
|
+
};
|
|
312
|
+
return { left: anchor.left === 0 ? "0" : `${anchor.left}px` };
|
|
278
313
|
}
|
|
279
314
|
function applyHeaderFooterFloatHorizontalPosition(img, floatImg, layout) {
|
|
280
315
|
img.style.left = resolveHeaderFooterFloatLeft(floatImg.width, floatImg.position.horizontal, layout);
|
|
@@ -500,6 +535,12 @@ function renderHeaderFooterContent(content, context, options, layout) {
|
|
|
500
535
|
}, layout) : cursorY;
|
|
501
536
|
fragEl.style.top = `${textBoxTop}px`;
|
|
502
537
|
fragEl.style.left = resolveHeaderFooterFloatLeft(measure.width, block.position?.horizontal, layout);
|
|
538
|
+
if (block.widthMode === "intrinsic") {
|
|
539
|
+
const horizontalPosition = resolveHeaderFooterIntrinsicFrameHorizontalPosition(block.position?.horizontal, layout);
|
|
540
|
+
fragEl.style.width = "max-content";
|
|
541
|
+
fragEl.style.left = horizontalPosition.left;
|
|
542
|
+
fragEl.style.transform = horizontalPosition.transform ?? "";
|
|
543
|
+
}
|
|
503
544
|
if (block.wrapType === "behind") fragEl.style.zIndex = "-1";
|
|
504
545
|
containerEl.append(fragEl);
|
|
505
546
|
if (!isPositionedHeaderFooterTextBoxBlock(block)) cursorY += measure.height;
|
|
@@ -1823,4 +1864,4 @@ function depopulatePageShell(shell, pageDataMap) {
|
|
|
1823
1864
|
}
|
|
1824
1865
|
}
|
|
1825
1866
|
//#endregion
|
|
1826
|
-
export { PAGE_CLASS_NAMES, PAINTER_PAINTED_EVENT, applySectionHeaderFooterOptions, calculateFootnoteAreaRenderHeight, computePageFingerprint, emuToPixels, findPageShellForPmPos, floatingTableReservesBand, getDefaultPageFontFamily, isFloatingImageRun, renderAllPagesNow, renderFloatingImagesLayer, renderFootnoteArea, renderPage, renderPages, resolveAnchoredImagePosition, resolveHeaderFooterFloatLeft };
|
|
1867
|
+
export { PAGE_CLASS_NAMES, PAINTER_PAINTED_EVENT, applySectionHeaderFooterOptions, calculateFootnoteAreaRenderHeight, computePageFingerprint, emuToPixels, findPageShellForPmPos, floatingTableReservesBand, getDefaultPageFontFamily, isFloatingImageRun, renderAllPagesNow, renderFloatingImagesLayer, renderFootnoteArea, renderPage, renderPages, resolveAnchoredImagePosition, resolveHeaderFooterFloatLeft, resolveHeaderFooterIntrinsicFrameHorizontalPosition };
|
|
@@ -12,6 +12,7 @@ import { getAutomaticTextColorForBackground } from "./documentColors.js";
|
|
|
12
12
|
import { applyImageVisualAttrs, hasImageCrop, hasImageVisualAttrs } from "./renderImage.js";
|
|
13
13
|
import { renderParagraphFragment } from "./renderParagraph.js";
|
|
14
14
|
import { renderTextBoxFragment } from "./renderTextBox.js";
|
|
15
|
+
import { ownedRowBottomBorderOffsets } from "./tableRowPaintGeometry.js";
|
|
15
16
|
//#region src/layout-painter/renderTable.ts
|
|
16
17
|
/**
|
|
17
18
|
* Table Renderer
|
|
@@ -36,6 +37,7 @@ const TABLE_CLASS_NAMES = {
|
|
|
36
37
|
tableEdgeHandleRight: "layout-table-edge-handle-right"
|
|
37
38
|
};
|
|
38
39
|
const CELL_DIAGONAL_BORDER_CLASS = "layout-table-cell-diagonal-border";
|
|
40
|
+
const CELL_BOTTOM_BORDER_CLASS = "layout-table-cell-bottom-border";
|
|
39
41
|
function renderCellContent({ cell, cellMeasure, context, doc, contentWidthOverride, pageContentPosition }) {
|
|
40
42
|
const contentEl = doc.createElement("div");
|
|
41
43
|
contentEl.className = TABLE_CLASS_NAMES.cellContent;
|
|
@@ -355,7 +357,7 @@ function renderTableCell({ cell, cellMeasure, x, width, rowHeight, borderFlags,
|
|
|
355
357
|
if (cell.borders) {
|
|
356
358
|
if (borderFlags.drawTop) applyBorder(cellEl, "top", cell.borders.top);
|
|
357
359
|
applyBorder(cellEl, "right", cell.borders.right);
|
|
358
|
-
applyBorder(cellEl, "bottom", cell.borders.bottom);
|
|
360
|
+
if (borderFlags.drawBottom) applyBorder(cellEl, "bottom", cell.borders.bottom);
|
|
359
361
|
if (borderFlags.drawLeft) applyBorder(cellEl, "left", cell.borders.left);
|
|
360
362
|
}
|
|
361
363
|
if (cell.background) {
|
|
@@ -444,7 +446,26 @@ function renderTableCell({ cell, cellMeasure, x, width, rowHeight, borderFlags,
|
|
|
444
446
|
return cellEl;
|
|
445
447
|
}
|
|
446
448
|
const hasVisibleBorder = (border) => border !== void 0 && border.style !== "none" && border.style !== "nil";
|
|
447
|
-
|
|
449
|
+
const isMinimumHeightRow = (row) => row?.height !== void 0 && row.heightRule !== "exact";
|
|
450
|
+
const rowHasVerticalMerge = (row) => row?.cells.some((cell) => (cell.rowSpan ?? 1) > 1) === true;
|
|
451
|
+
const continuationRowBottomBorderOffsets = (fragment, block, measure, cellGrid) => {
|
|
452
|
+
const rowHeights = measure.rows.slice(fragment.fromRow, fragment.toRow).map(({ height }) => height);
|
|
453
|
+
const fragmentHasVerticalMerge = block.rows.slice(fragment.fromRow, fragment.toRow).some((row, fragmentRowIndex) => rowHasVerticalMerge(row) || (cellGrid.occupiedColumnsByRow.get(fragment.fromRow + fragmentRowIndex)?.size ?? 0) > 0);
|
|
454
|
+
if (fragment.continuesFromPrev !== true || fragment.headerRowCount || fragment.topClip !== void 0 || fragment.bottomClip !== void 0 || fragmentHasVerticalMerge) return rowHeights.map(() => 0);
|
|
455
|
+
const snapAfterRow = rowHeights.slice(0, -1).map((_, fragmentRowIndex) => {
|
|
456
|
+
const rowIndex = fragment.fromRow + fragmentRowIndex;
|
|
457
|
+
const row = block.rows[rowIndex];
|
|
458
|
+
const nextRow = block.rows[rowIndex + 1];
|
|
459
|
+
return isMinimumHeightRow(row) && isMinimumHeightRow(nextRow) && !rowHasVerticalMerge(row) && !rowHasVerticalMerge(nextRow) && row?.cells.some((cell) => hasVisibleBorder(cell.borders?.bottom)) === true;
|
|
460
|
+
});
|
|
461
|
+
if (!snapAfterRow.some(Boolean)) return rowHeights.map(() => 0);
|
|
462
|
+
return ownedRowBottomBorderOffsets({
|
|
463
|
+
origin: fragment.y,
|
|
464
|
+
rowHeights,
|
|
465
|
+
snapAfterRow
|
|
466
|
+
});
|
|
467
|
+
};
|
|
468
|
+
function renderTableRow({ row, rowMeasure, rowIndex, y, columnWidths, totalRows, context, doc, rowYPositions, isFirstRowInFragment, bidi = false, columnsPinned = false, cellGrid, cellPlacements, contentClip, pageContentPosition, inlineOffset = 0, bottomBorderOffset = 0 }) {
|
|
448
469
|
const rowEl = doc.createElement("div");
|
|
449
470
|
rowEl.className = TABLE_CLASS_NAMES.row;
|
|
450
471
|
rowEl.style.position = "absolute";
|
|
@@ -477,6 +498,7 @@ function renderTableRow({ row, rowMeasure, rowIndex, y, columnWidths, totalRows,
|
|
|
477
498
|
const leftCell = getSourceCellAt(cellGrid, rowIndex, bidi ? columnIndex + colSpan : columnIndex - 1);
|
|
478
499
|
const drawTop = isFirstRow || !hasVisibleBorder(aboveCell?.borders?.bottom);
|
|
479
500
|
const drawLeft = isFirstCol || !hasVisibleBorder(leftCell?.borders?.right);
|
|
501
|
+
const paintBottomBorderSeparately = bottomBorderOffset > 0 && hasVisibleBorder(cell.borders?.bottom);
|
|
480
502
|
const cellEl = renderTableCell({
|
|
481
503
|
cell,
|
|
482
504
|
cellMeasure,
|
|
@@ -485,6 +507,7 @@ function renderTableRow({ row, rowMeasure, rowIndex, y, columnWidths, totalRows,
|
|
|
485
507
|
rowHeight: cellHeight,
|
|
486
508
|
borderFlags: {
|
|
487
509
|
drawTop,
|
|
510
|
+
drawBottom: !paintBottomBorderSeparately,
|
|
488
511
|
isLastRow,
|
|
489
512
|
drawLeft,
|
|
490
513
|
isLastCol
|
|
@@ -503,6 +526,20 @@ function renderTableRow({ row, rowMeasure, rowIndex, y, columnWidths, totalRows,
|
|
|
503
526
|
cellEl.dataset["columnIndex"] = String(columnIndex);
|
|
504
527
|
if (rowSpan > 1) cellEl.dataset["rowSpan"] = String(rowSpan);
|
|
505
528
|
rowEl.append(cellEl);
|
|
529
|
+
if (paintBottomBorderSeparately) {
|
|
530
|
+
const bottomBorderEl = doc.createElement("div");
|
|
531
|
+
bottomBorderEl.className = CELL_BOTTOM_BORDER_CLASS;
|
|
532
|
+
bottomBorderEl.style.position = "absolute";
|
|
533
|
+
bottomBorderEl.style.left = `${cellLeft}px`;
|
|
534
|
+
bottomBorderEl.style.top = "0";
|
|
535
|
+
bottomBorderEl.style.width = `${width}px`;
|
|
536
|
+
bottomBorderEl.style.height = `${cellHeight + bottomBorderOffset}px`;
|
|
537
|
+
bottomBorderEl.style.boxSizing = "border-box";
|
|
538
|
+
bottomBorderEl.style.pointerEvents = "none";
|
|
539
|
+
bottomBorderEl.style.zIndex = "1";
|
|
540
|
+
applyBorder(bottomBorderEl, "bottom", cell.borders?.bottom);
|
|
541
|
+
rowEl.append(bottomBorderEl);
|
|
542
|
+
}
|
|
506
543
|
}
|
|
507
544
|
return rowEl;
|
|
508
545
|
}
|
|
@@ -568,6 +605,7 @@ function renderTableFragment(fragment, block, measure, context, options = {}) {
|
|
|
568
605
|
columnWidths: measure.columnWidths,
|
|
569
606
|
bidi: block.bidi === true
|
|
570
607
|
});
|
|
608
|
+
const contentRowBottomBorderOffsets = continuationRowBottomBorderOffsets(fragment, block, measure, cellGrid);
|
|
571
609
|
const headerRowCount = fragment.headerRowCount ?? 0;
|
|
572
610
|
let y = 0;
|
|
573
611
|
if (headerRowCount > 0 && fragment.continuesFromPrev) for (let hdrIdx = 0; hdrIdx < headerRowCount; hdrIdx++) {
|
|
@@ -646,6 +684,7 @@ function renderTableFragment(fragment, block, measure, context, options = {}) {
|
|
|
646
684
|
columnsPinned,
|
|
647
685
|
cellGrid,
|
|
648
686
|
cellPlacements,
|
|
687
|
+
bottomBorderOffset: contentRowBottomBorderOffsets[rowIndex - fragment.fromRow] ?? 0,
|
|
649
688
|
...contentClip ? { contentClip } : {},
|
|
650
689
|
...tablePageContentPosition ? { pageContentPosition: {
|
|
651
690
|
...tablePageContentPosition,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/layout-painter/tableRowPaintGeometry.d.ts
|
|
2
|
+
type OwnedRowBottomBorderOffsetsOptions = {
|
|
3
|
+
origin: number;
|
|
4
|
+
rowHeights: readonly number[];
|
|
5
|
+
/** Whether the row ending at this index owns a visible shared bottom edge. */
|
|
6
|
+
snapAfterRow: readonly boolean[];
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Offset bottom-owned shared edges onto CSS pixel boundaries. Source row
|
|
10
|
+
* geometry remains unchanged, so content floors and the final band edge stay
|
|
11
|
+
* exact; only the independently painted border moves.
|
|
12
|
+
*/
|
|
13
|
+
declare const ownedRowBottomBorderOffsets: ({ origin, rowHeights, snapAfterRow }: OwnedRowBottomBorderOffsetsOptions) => number[];
|
|
14
|
+
//#endregion
|
|
15
|
+
export { ownedRowBottomBorderOffsets };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/layout-painter/tableRowPaintGeometry.ts
|
|
2
|
+
const CSS_PIXEL_ROUNDING_EPSILON = 1e-6;
|
|
3
|
+
/**
|
|
4
|
+
* Offset bottom-owned shared edges onto CSS pixel boundaries. Source row
|
|
5
|
+
* geometry remains unchanged, so content floors and the final band edge stay
|
|
6
|
+
* exact; only the independently painted border moves.
|
|
7
|
+
*/
|
|
8
|
+
const ownedRowBottomBorderOffsets = ({ origin, rowHeights, snapAfterRow }) => {
|
|
9
|
+
const offsets = [];
|
|
10
|
+
let boundary = origin;
|
|
11
|
+
for (let rowIndex = 0; rowIndex < rowHeights.length; rowIndex++) {
|
|
12
|
+
boundary += rowHeights[rowIndex] ?? 0;
|
|
13
|
+
if (rowIndex === rowHeights.length - 1 || snapAfterRow[rowIndex] !== true) {
|
|
14
|
+
offsets.push(0);
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
offsets.push(Math.ceil(boundary - CSS_PIXEL_ROUNDING_EPSILON) - boundary);
|
|
18
|
+
}
|
|
19
|
+
return offsets;
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { ownedRowBottomBorderOffsets };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stll/folio-core",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.1",
|
|
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",
|