Skip to content

Commit aecf9c4

Browse files
committed
fix: centered image
1 parent a995731 commit aecf9c4

File tree

4 files changed

+182
-1
lines changed

4 files changed

+182
-1
lines changed

packages/layout-engine/layout-engine/src/layout-drawing.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,5 +778,68 @@ describe('layoutDrawingBlock', () => {
778778
expect(fragment.width).toBe(600);
779779
expect(fragment.height).toBeCloseTo(333 * expectedScale, 10); // Allow floating point precision
780780
});
781+
782+
it('should center inline shapeGroup drawings using paragraph alignment metadata', () => {
783+
const context = createMockContext(
784+
{
785+
drawingKind: 'shapeGroup',
786+
attrs: {
787+
pmStart: 10,
788+
pmEnd: 11,
789+
wrap: { type: 'Inline' },
790+
inlineParagraphAlignment: 'center',
791+
},
792+
},
793+
{ width: 200, height: 150 },
794+
);
795+
const state = context.ensurePage();
796+
797+
layoutDrawingBlock(context);
798+
799+
const fragment = state.page.fragments[0] as DrawingFragment;
800+
expect(fragment.x).toBe(200);
801+
});
802+
803+
it('should right-align inline shapeGroup drawings using paragraph alignment metadata', () => {
804+
const context = createMockContext(
805+
{
806+
drawingKind: 'shapeGroup',
807+
attrs: {
808+
pmStart: 10,
809+
pmEnd: 11,
810+
wrap: { type: 'Inline' },
811+
inlineParagraphAlignment: 'right',
812+
},
813+
},
814+
{ width: 200, height: 150 },
815+
);
816+
const state = context.ensurePage();
817+
818+
layoutDrawingBlock(context);
819+
820+
const fragment = state.page.fragments[0] as DrawingFragment;
821+
expect(fragment.x).toBe(400);
822+
});
823+
824+
it('should not apply paragraph alignment metadata when shapeGroup is not inline', () => {
825+
const context = createMockContext(
826+
{
827+
drawingKind: 'shapeGroup',
828+
attrs: {
829+
pmStart: 10,
830+
pmEnd: 11,
831+
wrap: { type: 'Square' },
832+
inlineParagraphAlignment: 'center',
833+
},
834+
},
835+
{ width: 200, height: 150 },
836+
);
837+
const state = context.ensurePage();
838+
839+
layoutDrawingBlock(context);
840+
841+
const fragment = state.page.fragments[0] as DrawingFragment;
842+
expect(fragment.x).toBe(0);
843+
});
781844
});
782845
});

packages/layout-engine/layout-engine/src/layout-drawing.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ export function layoutDrawingBlock({
8585
const indentRight = typeof attrs?.hrIndentRight === 'number' ? attrs.hrIndentRight : 0;
8686
const maxWidthForBlock =
8787
attrs?.isFullWidth === true && maxWidth > 0 ? Math.max(1, maxWidth - indentLeft - indentRight) : maxWidth;
88+
const rawWrap = attrs?.wrap as { type?: unknown } | undefined;
89+
const isInlineShapeGroup = block.drawingKind === 'shapeGroup' && rawWrap?.type === 'Inline';
90+
const inlineParagraphAlignment =
91+
attrs?.inlineParagraphAlignment === 'center' || attrs?.inlineParagraphAlignment === 'right'
92+
? attrs.inlineParagraphAlignment
93+
: undefined;
8894

8995
if (width > maxWidthForBlock && maxWidthForBlock > 0) {
9096
const scale = maxWidthForBlock / width;
@@ -107,12 +113,18 @@ export function layoutDrawingBlock({
107113
}
108114

109115
const pmRange = extractBlockPmRange(block);
116+
let x = columnX(state.columnIndex) + marginLeft + indentLeft;
117+
if (isInlineShapeGroup && inlineParagraphAlignment === 'center') {
118+
x += Math.max(0, maxWidthForBlock - width) / 2;
119+
} else if (isInlineShapeGroup && inlineParagraphAlignment === 'right') {
120+
x += Math.max(0, maxWidthForBlock - width);
121+
}
110122

111123
const fragment: DrawingFragment = {
112124
kind: 'drawing',
113125
blockId: block.id,
114126
drawingKind: block.drawingKind,
115-
x: columnX(state.columnIndex) + marginLeft + indentLeft,
127+
x,
116128
y: state.cursorY + marginTop,
117129
width,
118130
height,

packages/layout-engine/pm-adapter/src/converters/paragraph.test.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2144,6 +2144,89 @@ describe('paragraph converters', () => {
21442144
expect(shapeGroupNodeToDrawingBlock).toHaveBeenCalledWith(shapeNode, nextBlockId, positions);
21452145
});
21462146

2147+
it('should attach inline paragraph alignment to inline shapeGroup drawings', () => {
2148+
const shapeNode: PMNode = { type: 'shapeGroup' };
2149+
2150+
vi.mocked(computeParagraphAttrs).mockReturnValue({
2151+
paragraphAttrs: {
2152+
alignment: 'center',
2153+
},
2154+
resolvedParagraphProperties: {},
2155+
} as never);
2156+
2157+
vi.mocked(shapeGroupNodeToDrawingBlock).mockReturnValue({
2158+
kind: 'drawing',
2159+
id: 'drawing-0',
2160+
drawingKind: 'shapeGroup',
2161+
wrap: { type: 'Inline' },
2162+
attrs: {
2163+
wrap: { type: 'Inline' },
2164+
},
2165+
shapes: [],
2166+
} as never);
2167+
2168+
const blocks = paragraphToFlowBlocks(
2169+
{
2170+
type: 'paragraph',
2171+
content: [shapeNode],
2172+
},
2173+
nextBlockId,
2174+
positions,
2175+
'Arial',
2176+
16,
2177+
undefined,
2178+
undefined,
2179+
undefined,
2180+
undefined,
2181+
);
2182+
2183+
const drawingBlock = blocks.find((block) => block.kind === 'drawing') as FlowBlock & {
2184+
attrs?: Record<string, unknown>;
2185+
};
2186+
expect(drawingBlock?.attrs?.inlineParagraphAlignment).toBe('center');
2187+
});
2188+
2189+
it('should not attach inline paragraph alignment to non-inline shapeGroup drawings', () => {
2190+
const shapeNode: PMNode = { type: 'shapeGroup' };
2191+
2192+
vi.mocked(computeParagraphAttrs).mockReturnValue({
2193+
paragraphAttrs: {
2194+
alignment: 'center',
2195+
},
2196+
resolvedParagraphProperties: {},
2197+
} as never);
2198+
2199+
vi.mocked(shapeGroupNodeToDrawingBlock).mockReturnValue({
2200+
kind: 'drawing',
2201+
id: 'drawing-0',
2202+
drawingKind: 'shapeGroup',
2203+
attrs: {
2204+
wrap: { type: 'Square' },
2205+
},
2206+
shapes: [],
2207+
} as never);
2208+
2209+
const blocks = paragraphToFlowBlocks(
2210+
{
2211+
type: 'paragraph',
2212+
content: [shapeNode],
2213+
},
2214+
nextBlockId,
2215+
positions,
2216+
'Arial',
2217+
16,
2218+
undefined,
2219+
undefined,
2220+
undefined,
2221+
undefined,
2222+
);
2223+
2224+
const drawingBlock = blocks.find((block) => block.kind === 'drawing') as FlowBlock & {
2225+
attrs?: Record<string, unknown>;
2226+
};
2227+
expect(drawingBlock?.attrs?.inlineParagraphAlignment).toBeUndefined();
2228+
});
2229+
21472230
it('should handle shapeContainer node', () => {
21482231
const shapeNode: PMNode = { type: 'shapeContainer' };
21492232

packages/layout-engine/pm-adapter/src/converters/paragraph.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,26 @@ export function paragraphToFlowBlocks({
665665
blockWithAttrs.attrs.anchorParagraphId = anchorParagraphId;
666666
return blockWithAttrs;
667667
};
668+
const attachInlineShapeGroupAlignment = <T extends FlowBlock>(block: T): T => {
669+
if (block.kind !== 'drawing') {
670+
return block;
671+
}
672+
const drawingBlock = block as T & {
673+
drawingKind?: string;
674+
attrs?: Record<string, unknown>;
675+
};
676+
const rawWrap = drawingBlock.attrs?.wrap as { type?: unknown } | undefined;
677+
if (drawingBlock.drawingKind !== 'shapeGroup' || rawWrap?.type !== 'Inline') {
678+
return block;
679+
}
680+
if (!drawingBlock.attrs) {
681+
drawingBlock.attrs = {};
682+
}
683+
if (paragraphAttrs.alignment === 'center' || paragraphAttrs.alignment === 'right') {
684+
drawingBlock.attrs.inlineParagraphAlignment = paragraphAttrs.alignment;
685+
}
686+
return block;
687+
};
668688

669689
const flushParagraph = () => {
670690
if (currentRuns.length === 0) {
@@ -757,11 +777,13 @@ export function paragraphToFlowBlocks({
757777
const block = blockConverter(node, { ...blockOptions, blocks: newBlocks });
758778
if (block) {
759779
attachAnchorParagraphId(block, anchorParagraphId);
780+
attachInlineShapeGroupAlignment(block);
760781
blocks.push(block);
761782
} else if (newBlocks.length > 0) {
762783
// Some block converters may push multiple blocks to the provided array
763784
newBlocks.forEach((b) => {
764785
attachAnchorParagraphId(b, anchorParagraphId);
786+
attachInlineShapeGroupAlignment(b);
765787
blocks.push(b);
766788
});
767789
}
@@ -779,6 +801,7 @@ export function paragraphToFlowBlocks({
779801
const converter = SHAPE_CONVERTERS_REGISTRY[node.type];
780802
const drawingBlock = converter(node, stableNextBlockId, positions);
781803
if (drawingBlock) {
804+
attachInlineShapeGroupAlignment(drawingBlock);
782805
blocks.push(attachAnchorParagraphId(drawingBlock, anchorParagraphId));
783806
}
784807
return;

0 commit comments

Comments
 (0)