diff --git a/iosMath/lib/MTMathList.h b/iosMath/lib/MTMathList.h index 8a37c3b..26ec530 100644 --- a/iosMath/lib/MTMathList.h +++ b/iosMath/lib/MTMathList.h @@ -494,4 +494,15 @@ typedef NS_ENUM(NSInteger, MTColumnAlignment) { @end +/** A subclass of MTLargeOperator, it adds no new functionality, + but allows to check if the atom if of said type when building a string + out of a math list + */ +@interface MTStackRel : MTLargeOperator + +/// Convenience init to create the stackrel +- (instancetype) initWithBottom:(NSString*) bottom andTop:(MTMathList*) top NS_DESIGNATED_INITIALIZER; + +@end + NS_ASSUME_NONNULL_END diff --git a/iosMath/lib/MTMathList.m b/iosMath/lib/MTMathList.m index d9ff121..10f1a0b 100644 --- a/iosMath/lib/MTMathList.m +++ b/iosMath/lib/MTMathList.m @@ -1034,3 +1034,17 @@ - (id)copyWithZone:(NSZone *)zone } @end + +#pragma mark - MTStackRel + +@implementation MTStackRel : MTLargeOperator + +/// Convenience init to create the stackrel +- (instancetype) initWithBottom:(NSString*) bottom andTop:(MTMathList*) top +{ + self = [super initWithValue:bottom limits:YES]; + self.superScript = top; + return self; +} + +@end diff --git a/iosMath/lib/MTMathListBuilder.m b/iosMath/lib/MTMathListBuilder.m index 894609f..51cce4c 100644 --- a/iosMath/lib/MTMathListBuilder.m +++ b/iosMath/lib/MTMathListBuilder.m @@ -506,6 +506,11 @@ - (MTMathAtom*) atomForCommand:(NSString*) command mathColorbox.colorString = [self readColor]; mathColorbox.innerList = [self buildInternal:true]; return mathColorbox; + } else if ([command isEqualToString:@"stackrel"]) { + MTMathList* top = [self buildInternal:YES]; + MTMathList* bottom = [self buildInternal:YES]; + MTLargeOperator* stack = [[MTStackRel alloc] initWithBottom:bottom.stringValue andTop:top]; + return stack; } else { NSString* errorMessage = [NSString stringWithFormat:@"Invalid command \\%@", command]; [self setError:MTParseErrorInvalidCommand message:errorMessage]; @@ -842,15 +847,20 @@ + (NSString *)mathListToString:(MTMathList *)ml MTAccent* accent = (MTAccent*) atom; [str appendFormat:@"\\%@{%@}", [MTMathAtomFactory accentName:accent], [self mathListToString:accent.innerList]]; } else if (atom.type == kMTMathAtomLargeOperator) { - MTLargeOperator* op = (MTLargeOperator*) atom; - NSString* command = [MTMathAtomFactory latexSymbolNameForAtom:atom]; - MTLargeOperator* originalOp = (MTLargeOperator*) [MTMathAtomFactory atomForLatexSymbolName:command]; - [str appendFormat:@"\\%@ ", command]; - if (originalOp.limits != op.limits) { - if (op.limits) { - [str appendString:@"\\limits "]; - } else { - [str appendString:@"\\nolimits "]; + if ([atom isKindOfClass:[MTStackRel class]]) { + [str appendFormat:@"\\stackrel{%@}{%@}", [self mathListToString:atom.superScript], atom.nucleus]; + continue; + } else { + MTLargeOperator* op = (MTLargeOperator*) atom; + NSString* command = [MTMathAtomFactory latexSymbolNameForAtom:atom]; + MTLargeOperator* originalOp = (MTLargeOperator*) [MTMathAtomFactory atomForLatexSymbolName:command]; + [str appendFormat:@"\\%@ ", command]; + if (originalOp.limits != op.limits) { + if (op.limits) { + [str appendString:@"\\limits "]; + } else { + [str appendString:@"\\nolimits "]; + } } } } else if (atom.type == kMTMathAtomSpace) { diff --git a/iosMathExample/example/ViewController.m b/iosMathExample/example/ViewController.m index 21a78e4..4535ece 100644 --- a/iosMathExample/example/ViewController.m +++ b/iosMathExample/example/ViewController.m @@ -82,7 +82,7 @@ - (void)viewDidLoad // set the size of the content view // Disable horizontal scrolling. [self setEqualWidths:contentView andView:self.scrollView]; - [self setHeight:4350 forView:contentView]; + [self setHeight:4520 forView:contentView]; // Demo formulae @@ -177,7 +177,7 @@ - (void)viewDidLoad "\\end{cases}" withHeight:60]; self.demoLabels[21] = [self createMathLabel:@"\\color{#ff3333}{c}\\color{#9933ff}{o}\\color{#ff0080}{l}+\\color{#99ff33}{\\frac{\\color{#ff99ff}{o}}{\\color{#990099}{r}}}-\\color{#33ffff}{\\sqrt[\\color{#3399ff}{e}]{\\color{#3333ff}{d}}}" withHeight:60]; - + for (NSUInteger i = 1; i < self.demoLabels.count; i++) { self.demoLabels[i].fontSize = 15; @@ -320,6 +320,10 @@ - (void)viewDidLoad "\\colorbox{#00aaff}{c} & \\colorbox{#f0f0f0}{d}" "\\end{pmatrix}}" withHeight:70]; + + // Test stackrel + self.labels[48] = [self createMathLabel:@"\\lim_{x \\to 0} \\frac{\\ln{x}}{x^{2} - 1} \\stackrel{H}{=} \\lim_{x \\to 0} \\frac{\\frac{1}{x}}{2x}" withHeight:90]; + self.labels[49] = [self createMathLabel:@"\\stackrel{up}{bottom}" withHeight:60]; for (NSUInteger i = 1; i < self.labels.count; i++) { [self addLabelWithIndex:i inArray:self.labels toView:contentView]; diff --git a/iosMathTests/MTMathListBuilderTest.m b/iosMathTests/MTMathListBuilderTest.m index 0b39546..d03a5de 100644 --- a/iosMathTests/MTMathListBuilderTest.m +++ b/iosMathTests/MTMathListBuilderTest.m @@ -1385,4 +1385,23 @@ - (void) testNoLimits latex = [MTMathListBuilder mathListToString:list]; XCTAssertEqualObjects(latex, @"\\sum \\nolimits ", @"%@", desc); } + +- (void) testStackrel +{ + // L'hopital rule + NSString *str = @"\\stackrel{H}{=}"; + MTMathList* list = [MTMathListBuilder buildFromString:str]; + NSString* desc = [NSString stringWithFormat:@"Error for string:%@", str]; + + XCTAssertNotNil(list, @"%@", desc); + XCTAssertEqualObjects(@(list.atoms.count), @1, @"%@", desc); + MTLargeOperator* op = list.atoms[0]; + XCTAssertEqual(op.type, kMTMathAtomLargeOperator, @"%@", desc); + XCTAssertTrue(op.limits); + XCTAssertTrue([op isKindOfClass:[MTStackRel class]]); + + // convert it back to latex + NSString* latex = [MTMathListBuilder mathListToString:list]; + XCTAssertEqualObjects(latex, @"\\stackrel{H}{=}", @"%@", desc); +} @end