Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions packages/layout-engine/painters/dom/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3810,6 +3810,66 @@ describe('DomPainter', () => {
expect(borderLayer.style.borderLeftColor).toBe('rgb(0, 255, 0)');
});

it('applies padding for border spacing to prevent overlap', () => {
const blockWithBorderSpacing: FlowBlock = {
kind: 'paragraph',
id: 'border-space-block',
attrs: {
borders: {
top: { style: 'solid', width: 2, color: '#ff0000', space: 5 },
bottom: { style: 'solid', width: 3, color: '#00ff00', space: 10 },
left: { style: 'dashed', width: 1, color: '#0000ff', space: 4 },
right: { style: 'dotted', width: 2, color: '#ffff00', space: 6 },
},
},
runs: [{ text: 'Border spacing test', fontFamily: 'Arial', fontSize: 16 }],
};

const painter = createDomPainter({
blocks: [blockWithBorderSpacing],
measures: [measure],
});

const borderSpaceLayout: Layout = {
pageSize: layout.pageSize,
pages: [
{
number: 1,
fragments: [
{
kind: 'para',
blockId: 'border-space-block',
fromLine: 0,
toLine: 1,
x: 50,
y: 60,
width: 260,
},
],
},
],
};

painter.paint(borderSpaceLayout, mount);

const fragment = mount.querySelector('[data-block-id="border-space-block"]') as HTMLElement;
const borderLayer = fragment.querySelector('.superdoc-paragraph-border') as HTMLElement;

// Verify padding is applied to create space for borders
// Top: space(5) + width(2) = 7px
expect(fragment.style.paddingTop).toBe('7px');
// Bottom: space(10) + width(3) = 13px
expect(fragment.style.paddingBottom).toBe('13px');
// Left: space(4) + width(1) = 5px
expect(fragment.style.paddingLeft).toBe('5px');
// Right: space(6) + width(2) = 8px
expect(fragment.style.paddingRight).toBe('8px');

// Verify border layer is positioned with space offset
expect(borderLayer.style.top).toBe('5px');
expect(borderLayer.style.bottom).toBe('10px');
});

it('applies paragraph shading fill to fragment backgrounds', () => {
const shadedBlock: FlowBlock = {
kind: 'paragraph',
Expand Down
61 changes: 61 additions & 0 deletions packages/layout-engine/painters/dom/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2036,6 +2036,32 @@ export class DomPainter {
if (fragmentEl.style.marginRight) fragmentEl.style.removeProperty('margin-right');
if (fragmentEl.style.textIndent) fragmentEl.style.removeProperty('text-indent');

// Apply border padding to fragment after removing indent padding
// This padding creates space for borders and prevents them from overlapping content
const borders = block.attrs?.borders;
if (borders) {
if (borders.top) {
const topSpace = Math.max(0, borders.top.space ?? 0);
const topWidth = Math.max(0, borders.top.width ?? 1);
fragmentEl.style.paddingTop = `${topSpace + topWidth}px`;
}
if (borders.bottom) {
const bottomSpace = Math.max(0, borders.bottom.space ?? 0);
const bottomWidth = Math.max(0, borders.bottom.width ?? 1);
fragmentEl.style.paddingBottom = `${bottomSpace + bottomWidth}px`;
}
if (borders.left) {
const leftSpace = Math.max(0, borders.left.space ?? 0);
const leftWidth = Math.max(0, borders.left.width ?? 1);
fragmentEl.style.paddingLeft = `${leftSpace + leftWidth}px`;
}
if (borders.right) {
const rightSpace = Math.max(0, borders.right.space ?? 0);
const rightWidth = Math.max(0, borders.right.width ?? 1);
fragmentEl.style.paddingRight = `${rightSpace + rightWidth}px`;
}
}

const paraIndent = block.attrs?.indent;
const paraIndentLeft = paraIndent?.left ?? 0;
const paraIndentRight = paraIndent?.right ?? 0;
Expand Down Expand Up @@ -5912,6 +5938,34 @@ const createParagraphDecorationLayers = (
): { shadingLayer?: HTMLElement; borderLayer?: HTMLElement } => {
if (!attrs?.borders && !attrs?.shading) return {};
const borderBox = getParagraphBorderBox(fragmentWidth, attrs.indent);

// Calculate border spacing to position the border layer correctly
// The border should be drawn at the inner edge of the space (between space and content)
const borders = attrs.borders;
let topOffset = 0;
let bottomOffset = 0;
let leftOffset = 0;
let rightOffset = 0;

if (borders) {
if (borders.top) {
const space = Math.max(0, borders.top.space ?? 0);
topOffset = space; // Border at the inner edge of the space
}
if (borders.bottom) {
const space = Math.max(0, borders.bottom.space ?? 0);
bottomOffset = space;
}
if (borders.left) {
const space = Math.max(0, borders.left.space ?? 0);
leftOffset = space;
}
if (borders.right) {
const space = Math.max(0, borders.right.space ?? 0);
rightOffset = space;
}
}

const baseStyles = {
position: 'absolute',
top: '0px',
Expand All @@ -5936,6 +5990,13 @@ const createParagraphDecorationLayers = (
borderLayer.classList.add('superdoc-paragraph-border');
Object.assign(borderLayer.style, baseStyles);
borderLayer.style.zIndex = '1';

// Adjust positioning to account for border space
borderLayer.style.top = `${topOffset}px`;
borderLayer.style.bottom = `${bottomOffset}px`;
borderLayer.style.left = `${borderBox.leftInset + leftOffset}px`;
borderLayer.style.width = `${Math.max(0, borderBox.width - leftOffset - rightOffset)}px`;

applyParagraphBorderStyles(borderLayer, attrs.borders);
}

Expand Down
Loading