Skip to content

Commit cdae784

Browse files
authored
fix(slides): fix slide import errs (#7142)
1 parent 0dc521b commit cdae784

6 files changed

Lines changed: 195 additions & 8 deletions

File tree

packages/engine-render/src/__tests__/drawing-group.spec.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,50 @@ describe('drawing group', () => {
135135
expect(childRenderSpy).toHaveBeenCalled();
136136
});
137137

138+
it('maps children of nested drawing groups through the parent rendered bound', () => {
139+
const outerGroup = new DrawingGroupObject('outer-group');
140+
outerGroup.transformByState({
141+
left: 0,
142+
top: 0,
143+
width: 500,
144+
height: 500,
145+
});
146+
outerGroup.setBaseBound({
147+
left: 0,
148+
top: 0,
149+
width: 10,
150+
height: 10,
151+
});
152+
153+
const innerGroup = new DrawingGroupObject('inner-group');
154+
innerGroup.transformByState({
155+
left: 1,
156+
top: 1,
157+
width: 2,
158+
height: 2,
159+
});
160+
innerGroup.setBaseBound({
161+
left: 100,
162+
top: 100,
163+
width: 50,
164+
height: 50,
165+
});
166+
167+
const child = new Rect('nested-child', {
168+
left: 100,
169+
top: 100,
170+
width: 50,
171+
height: 50,
172+
fill: '#333333',
173+
});
174+
175+
innerGroup.addObject(child);
176+
outerGroup.addObject(innerGroup);
177+
178+
expect(innerGroup.getRealBound().width).toBe(100);
179+
expect(child.getRealBound().width).toBe(100);
180+
});
181+
138182
it('covers group object management, transform recalculation and dispose flow', () => {
139183
const sceneChild = new Rect('scene-child', {
140184
left: 5,

packages/engine-render/src/base-object.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -613,11 +613,14 @@ export abstract class BaseObject extends Disposable {
613613
}
614614
if (baseBound) {
615615
const parentState = this.getParent();
616+
const parentRealBound = typeof parentState.getRealBound === 'function'
617+
? parentState.getRealBound()
618+
: parentState;
616619
const parentBound = {
617-
top: parentState.top || 0,
618-
left: parentState.left,
619-
width: parentState.width || 0,
620-
height: parentState.height || 0,
620+
top: parentRealBound.top || 0,
621+
left: parentRealBound.left,
622+
width: parentRealBound.width || 0,
623+
height: parentRealBound.height || 0,
621624
};
622625
const realBound = getRenderTransformBaseOnParentBound(baseBound, parentBound, { width: realWidth, height: realHeight, left: realLeft, top: realTop });
623626

packages/engine-render/src/components/docs/layout/block/paragraph/__tests__/layout-ruler.spec.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
*/
1616

1717
import type { IParagraphConfig } from '../../../../../../basics/interfaces';
18+
import type { IDocumentSkeletonGlyph } from '../../../../../../basics/i-document-skeleton-cached';
1819
import {
1920
BooleanNumber,
2021
DataStreamTreeTokenType,
22+
DocumentFlavor,
2123
GridType,
2224
ObjectRelativeFromV,
2325
PositionedObjectLayoutType,
@@ -50,6 +52,44 @@ describe('layout-ruler', () => {
5052
});
5153
});
5254

55+
function createGlyph(content: string, width: number): IDocumentSkeletonGlyph {
56+
return {
57+
content,
58+
raw: content,
59+
ts: {},
60+
fontStyle: {
61+
fontString: 'bold 40.5pt "Microsoft YaHei"',
62+
fontSize: 40.5,
63+
originFontSize: 40.5,
64+
fontFamily: 'Microsoft YaHei',
65+
fontCache: 'Microsoft YaHei-40.5-bold',
66+
},
67+
width,
68+
bBox: {
69+
width,
70+
ba: 40,
71+
bd: 10,
72+
aba: 40,
73+
abd: 10,
74+
sp: 0,
75+
sbr: 0,
76+
sbo: 0,
77+
spr: 0,
78+
spo: 0,
79+
},
80+
xOffset: 0,
81+
left: 0,
82+
glyphType: GlyphType.LETTER,
83+
streamType: DataStreamTreeTokenType.LETTER,
84+
isJustifiable: true,
85+
adjustability: {
86+
stretchability: [0, 0],
87+
shrinkability: [0, 0],
88+
},
89+
count: content.length,
90+
};
91+
}
92+
5393
function getLineBoxHeight(metrics: ReturnType<typeof getLineHeightMetrics>) {
5494
return metrics.paddingTop + metrics.contentHeight + metrics.paddingBottom;
5595
}
@@ -379,6 +419,38 @@ describe('layout-ruler', () => {
379419
expect(lastPage.sections.length).toBeGreaterThan(0);
380420
});
381421

422+
it('keeps imported shape text on one line when browser glyph bboxes slightly exceed the box', () => {
423+
const text = '\u4F01\u4E1A\u6587\u5316\u5EFA\u8BBE';
424+
const { ctx, paragraphNode, sectionBreakConfig, curPage } = createParagraphLayoutTestBed(text, {
425+
documentStyle: {
426+
documentFlavor: DocumentFlavor.TRADITIONAL,
427+
pageSize: { width: 365.3740157480315, height: 120 },
428+
marginTop: 0,
429+
marginBottom: 0,
430+
marginLeft: 20,
431+
marginRight: 20,
432+
paragraphLineGapDefault: 0,
433+
},
434+
});
435+
const glyphs = text.split('').map((char) => createGlyph(char, 54.65998840332031));
436+
const paragraphConfig = {
437+
paragraphIndex: paragraphNode.endIndex,
438+
paragraphStyle: {
439+
lineSpacing: 1,
440+
snapToGrid: BooleanNumber.FALSE,
441+
spaceAbove: { v: 0 },
442+
spaceBelow: { v: 0 },
443+
},
444+
useWordStyleLineHeight: false,
445+
} as unknown as IParagraphConfig;
446+
447+
const result = layoutParagraph(ctx, glyphs, [curPage], sectionBreakConfig, paragraphConfig, true);
448+
const lines = result[0].sections[0].columns[0].lines;
449+
450+
expect(lines).toHaveLength(1);
451+
expect(lines[0].divides[0].glyphGroup.map((glyph) => glyph.content).join('')).toBe(text);
452+
});
453+
382454
it('uses glyph height as the base for auto line spacing when grid snapping is not explicitly enabled', () => {
383455
const metrics = getLineHeightMetrics(16, 0, 15.6, GridType.LINES, 1.5, SpacingRule.AUTO, BooleanNumber.FALSE, true);
384456

packages/engine-render/src/components/docs/layout/block/paragraph/__tests__/line-adjustment.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,35 @@ describe('line-adjustment', () => {
152152
expect(() => lineAdjustment(pages, viewModel, paragraphNode, sectionBreakConfig)).not.toThrow();
153153
});
154154

155+
it('spreads glyphs across the line for horizontal align DISTRIBUTED', () => {
156+
const text = '2038';
157+
const { viewModel, ctx, paragraphNode, sectionBreakConfig, curPage } = createParagraphLayoutTestBed(text, {
158+
body: {
159+
dataStream: `${text}\r\n`,
160+
textRuns: [{ st: 0, ed: 6, ts: {} }],
161+
paragraphs: [{
162+
startIndex: text.length,
163+
paragraphStyle: {
164+
horizontalAlign: HorizontalAlign.DISTRIBUTED,
165+
},
166+
}],
167+
sectionBreaks: [{ startIndex: text.length + 1 }],
168+
},
169+
});
170+
const shapedTextList = shaping(ctx, paragraphNode.content!, viewModel, paragraphNode, sectionBreakConfig);
171+
const pages = lineBreaking(ctx, viewModel, shapedTextList, curPage, paragraphNode, sectionBreakConfig, null);
172+
const divide = pages[0].sections[0].columns[0].lines[0].divides[0];
173+
const visibleGlyphs = divide.glyphGroup.filter((glyph) => glyph.content !== '');
174+
const lastVisibleGlyph = visibleGlyphs[visibleGlyphs.length - 1];
175+
const initialLastGlyphLeft = lastVisibleGlyph.left;
176+
177+
lineAdjustment(pages, viewModel, paragraphNode, sectionBreakConfig);
178+
179+
expect(lastVisibleGlyph.left).toBeGreaterThan(initialLastGlyphLeft);
180+
expect(lastVisibleGlyph.left + lastVisibleGlyph.width).toBeCloseTo(divide.width, 1);
181+
expect(divide.paddingLeft).toBe(0);
182+
});
183+
155184
it('handles line with only paragraph break', () => {
156185
const { viewModel, ctx, paragraphNode, sectionBreakConfig, curPage } = createParagraphLayoutTestBed('');
157186
const shapedTextList = shaping(ctx, paragraphNode.content!, viewModel, paragraphNode, sectionBreakConfig);

packages/engine-render/src/components/docs/layout/block/paragraph/layout-ruler.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ import { createTableSkeletons, rollbackListCache } from '../table';
7575

7676
const LINE_LAYOUT_OVERFLOW_TOLERANCE = 2;
7777
const FLOAT_OBJECT_RELAYOUT_LIMIT = 5;
78+
const MIN_LINE_WIDTH_TOLERANCE = 1;
79+
const MAX_LINE_WIDTH_TOLERANCE = 3;
80+
const RELATIVE_LINE_WIDTH_TOLERANCE = 0.01;
81+
82+
function isBeyondDivideWidth(width: number, divideWidth: number) {
83+
const tolerance = Math.min(
84+
MAX_LINE_WIDTH_TOLERANCE,
85+
Math.max(MIN_LINE_WIDTH_TOLERANCE, divideWidth * RELATIVE_LINE_WIDTH_TOLERANCE)
86+
);
87+
88+
return width - divideWidth > tolerance;
89+
}
7890

7991
export function layoutParagraph(
8092
ctx: ILayoutContext,
@@ -237,7 +249,7 @@ function _divideOperator(
237249
const lastLeft = lastGlyph?.left || 0;
238250
const preOffsetLeft = lastWidth + lastLeft;
239251
const { hyphenationZone } = sectionBreakConfig;
240-
if (preOffsetLeft + width > divide.width) {
252+
if (isBeyondDivideWidth(preOffsetLeft + width, divide.width)) {
241253
if (shouldKeepOverflowingTextOnLine(sectionBreakConfig)) {
242254
addGlyphToDivide(divide, glyphGroup, preOffsetLeft);
243255
updateDivideInfo(divide, { breakType: breakPointType });
@@ -295,7 +307,7 @@ function _divideOperator(
295307
sliceGlyphGroup.push(glyphGroup.shift()!);
296308

297309
const sliceGlyphGroupWidth = __getGlyphGroupWidth(sliceGlyphGroup);
298-
if (sliceGlyphGroupWidth > divide.width) {
310+
if (isBeyondDivideWidth(sliceGlyphGroupWidth, divide.width)) {
299311
// To avoid infinity loop when width is less than one char's width.
300312
if (sliceGlyphGroup.length > 1) { // || (sliceGlyphGroup.length > 0 && sliceGlyphGroup[sliceGlyphGroup.length - 1].drawingId)) {
301313
glyphGroup.unshift(sliceGlyphGroup.pop()!);

packages/engine-render/src/components/docs/layout/block/paragraph/line-adjustment.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,27 @@ function adjustGlyphsInDivide(divide: IDocumentSkeletonDivide, justificationRati
127127
setGlyphGroupLeft(divide.glyphGroup);
128128
}
129129

130+
function distributeGlyphsInDivide(divide: IDocumentSkeletonDivide, remaining: number): boolean {
131+
if (remaining <= 0) {
132+
return false;
133+
}
134+
135+
const visibleGlyphs = divide.glyphGroup.filter((glyph) => glyph.content !== '' && glyph.width > 0);
136+
137+
if (visibleGlyphs.length < 2) {
138+
return false;
139+
}
140+
141+
const extraGap = remaining / (visibleGlyphs.length - 1);
142+
143+
for (let i = 0; i < visibleGlyphs.length - 1; i++) {
144+
visibleGlyphs[i].width += extraGap;
145+
}
146+
147+
setGlyphGroupLeft(divide.glyphGroup);
148+
return true;
149+
}
150+
130151
/**
131152
* When aligning text horizontally within a document,
132153
* it may be ineffective if the total line width is not initially calculated.
@@ -226,7 +247,13 @@ function horizontalAlignHandler(
226247

227248
const inkBounds = allowOverflowHorizontalOffset ? getGlyphGroupInkBounds(divide) : null;
228249

229-
if (horizontalAlign === HorizontalAlign.CENTER && inkBounds) {
250+
if (horizontalAlign === HorizontalAlign.DISTRIBUTED) {
251+
if (distributeGlyphsInDivide(divide, width - glyphGroupWidth)) {
252+
glyphGroupWidth = getGlyphGroupWidth(divide);
253+
divide.glyphGroupWidth = glyphGroupWidth;
254+
}
255+
divide.paddingLeft = 0;
256+
} else if (horizontalAlign === HorizontalAlign.CENTER && inkBounds) {
230257
divide.paddingLeft = width / 2 - (inkBounds.left + inkBounds.right) / 2;
231258
} else if (horizontalAlign === HorizontalAlign.RIGHT && inkBounds) {
232259
divide.paddingLeft = width - inkBounds.right;
@@ -343,7 +370,7 @@ export function lineAdjustment(
343370
restoreLastCJKGlyphWidth(line);
344371
// Add dash to the end of divide when divide is break by Hyphen.
345372
addHyphenDash(line, viewModel, paragraphNode, sectionBreakConfig, paragraphStyle);
346-
// Handle horizontal align: left\center\right\justified.
373+
// Handle horizontal align: left\center\right\justified\distributed.
347374
horizontalAlignHandler(line, horizontalAlign, shouldAllowOverflowHorizontalOffset(sectionBreakConfig));
348375
});
349376
}

0 commit comments

Comments
 (0)