Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 4 additions & 0 deletions iosMath/render/internal/MTTypesetter.m
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,8 @@ - (void) createDisplayAtoms:(NSArray*) preprocessed
if (_currentLine.length > 0) {
[self addDisplayLine];
}
// Color is spaced as Ord (see getInterElementSpaceArrayIndexForType).
[self addInterElementSpace:prevNode currentType:atom.type];
MTMathColor* colorAtom = (MTMathColor*) atom;
MTDisplay* display = [MTTypesetter createLineForMathList:colorAtom.innerList font:_font style:_style];
display.localTextColor = [MTColor colorFromHexString:colorAtom.colorString];
Expand All @@ -649,6 +651,8 @@ - (void) createDisplayAtoms:(NSArray*) preprocessed
if (_currentLine.length > 0) {
[self addDisplayLine];
}
// Colorbox is spaced as Ord (see getInterElementSpaceArrayIndexForType).
[self addInterElementSpace:prevNode currentType:atom.type];
MTMathColorbox* colorboxAtom = (MTMathColorbox*) atom;
MTDisplay* display = [MTTypesetter createLineForMathList:colorboxAtom.innerList font:_font style:_style];

Expand Down
91 changes: 91 additions & 0 deletions iosMathTests/MTTypesetterTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -2725,4 +2725,95 @@ - (void)testOversetRowRendersAtScriptScriptWhenNestedInSuperscript
XCTAssertLessThan(nestedStack.over.ascent, baselineStack.over.ascent);
}

// REN-3: \color atoms must receive inter-element spacing before the colored sub-display.
// Before the fix, the colored group abutted the preceding binary operator with no gap.
// After the fix, the medium binary-operator→ordinary gap (4 mu) separates them.

- (void)testColorReceivesInterElementSpacingBeforeIt {
// x + \color{red}y — the colored sub-display follows the binary operator +.
// The gap between the end of "x+" and the start of the colored group must equal
// the medium space (4mu = 4 * font.mathTable.muUnit) at display style.
MTMathListDisplay* display = [self displayForLaTeX:@"x+\\color{#ff0000}{y}"];
XCTAssertNotNil(display);
XCTAssertEqual(display.subDisplays.count, 2u,
@"Expected CTLine for 'x+' and a colored sub-display");

MTDisplay* sub0 = display.subDisplays[0];
XCTAssertTrue([sub0 isKindOfClass:[MTCTLineDisplay class]],
@"First sub-display should be a CTLine for 'x+'");
MTCTLineDisplay* xPlusLine = (MTCTLineDisplay*)sub0;

MTDisplay* sub1 = display.subDisplays[1];
XCTAssertTrue([sub1 isKindOfClass:[MTMathListDisplay class]],
@"Second sub-display should be the colored MTMathListDisplay");
MTMathListDisplay* colorSub = (MTMathListDisplay*)sub1;
XCTAssertNotNil(colorSub.localTextColor, @"Colored display must carry a localTextColor");

// The medium binary-operator gap is 4 mu = 4 * muUnit (display style, non-script).
CGFloat expectedGap = 4.0 * self.font.mathTable.muUnit;
CGFloat actualGap = colorSub.position.x - (xPlusLine.position.x + xPlusLine.width);
XCTAssertEqualWithAccuracy(actualGap, expectedGap, 0.01,
@"Expected medium binary-op gap of %.4f pt before \\color, got %.4f pt",
expectedGap, actualGap);
}

- (void)testColorboxReceivesInterElementSpacingBeforeIt {
// x + \colorbox{red}y — same as testColorReceivesInterElementSpacingBeforeIt
// but for \colorbox (kMTMathAtomColorbox).
MTMathListDisplay* display = [self displayForLaTeX:@"x+\\colorbox{#ff0000}{y}"];
XCTAssertNotNil(display);
XCTAssertEqual(display.subDisplays.count, 2u,
@"Expected CTLine for 'x+' and a colorbox sub-display");

MTDisplay* sub0 = display.subDisplays[0];
XCTAssertTrue([sub0 isKindOfClass:[MTCTLineDisplay class]],
@"First sub-display should be a CTLine for 'x+'");
MTCTLineDisplay* xPlusLine = (MTCTLineDisplay*)sub0;

MTDisplay* sub1 = display.subDisplays[1];
XCTAssertTrue([sub1 isKindOfClass:[MTMathListDisplay class]],
@"Second sub-display should be the colorbox MTMathListDisplay");
MTMathListDisplay* colorboxSub = (MTMathListDisplay*)sub1;
XCTAssertNotNil(colorboxSub.localBackgroundColor,
@"Colorbox display must carry a localBackgroundColor");

CGFloat expectedGap = 4.0 * self.font.mathTable.muUnit;
CGFloat actualGap = colorboxSub.position.x - (xPlusLine.position.x + xPlusLine.width);
XCTAssertEqualWithAccuracy(actualGap, expectedGap, 0.01,
@"Expected medium binary-op gap of %.4f pt before \\colorbox, got %.4f pt",
expectedGap, actualGap);
}

- (void)testSpacingAfterColorGroupIsPreserved {
// Regression guard: spacing AFTER a \color group already worked via the spacing table.
// \color{red}{x} + z — the binary-operator gap after the colored group must still be present.
// The display structure is: [colored MTMathListDisplay, CTLine for "+z"].
MTMathListDisplay* display = [self displayForLaTeX:@"\\color{#ff0000}{x}+z"];
XCTAssertNotNil(display);
XCTAssertEqual(display.subDisplays.count, 2u,
@"Expected colored sub-display and a CTLine for '+z'");

MTDisplay* sub0 = display.subDisplays[0];
XCTAssertTrue([sub0 isKindOfClass:[MTMathListDisplay class]],
@"First sub-display should be the colored MTMathListDisplay");
MTMathListDisplay* colorSub = (MTMathListDisplay*)sub0;
XCTAssertNotNil(colorSub.localTextColor);

MTDisplay* sub1 = display.subDisplays[1];
XCTAssertTrue([sub1 isKindOfClass:[MTCTLineDisplay class]],
@"Second sub-display should be CTLine for '+z'");
MTCTLineDisplay* plusZLine = (MTCTLineDisplay*)sub1;

// The gap between end of the color group and the start of "+z" is the
// binary-operator gap (4 mu) because the typesetter sees color (Ord) then BinOp.
// But because + is a BinaryOperator and z is Ordinary, the actual inter-element
// space between color (Ord) and + (BinOp, left of the line) is medium (4mu).
// The "+z" CTLine starts at the position where the typesetter placed it.
CGFloat expectedGap = 4.0 * self.font.mathTable.muUnit;
CGFloat actualGap = plusZLine.position.x - (colorSub.position.x + colorSub.width);
XCTAssertEqualWithAccuracy(actualGap, expectedGap, 0.01,
@"Spacing after \\color group must be preserved (%.4f pt), got %.4f pt",
expectedGap, actualGap);
}

@end
Loading