From c3573780d90eb63214ecea00cd28d7a26b4ad8b3 Mon Sep 17 00:00:00 2001 From: czarny Date: Sun, 27 Oct 2013 14:40:29 +0100 Subject: [PATCH 01/11] Fixed extracting text style from html text with nested entities. --- RTLabelProject/Classes/RTLabel.m | 94 +++++++++++++++----------------- 1 file changed, 43 insertions(+), 51 deletions(-) diff --git a/RTLabelProject/Classes/RTLabel.m b/RTLabelProject/Classes/RTLabel.m index 4cf8624..833f4f6 100755 --- a/RTLabelProject/Classes/RTLabel.m +++ b/RTLabelProject/Classes/RTLabel.m @@ -858,61 +858,54 @@ - (NSArray *)components } + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraphReplacement:(NSString*)paragraphReplacement -{ - NSScanner *scanner = nil; - NSString *text = nil; - NSString *tag = nil; - +{ NSMutableArray *components = [NSMutableArray array]; - - int last_position = 0; - scanner = [NSScanner scannerWithString:data]; + NSMutableString *plainText = [NSMutableString new]; + NSScanner *scanner = [NSScanner scannerWithString:data]; + scanner.charactersToBeSkipped = nil; + while (![scanner isAtEnd]) { - [scanner scanUpToString:@"<" intoString:NULL]; - [scanner scanUpToString:@">" intoString:&text]; - - NSString *delimiter = [NSString stringWithFormat:@"%@>", text]; - int position = [data rangeOfString:delimiter].location; - if (position!=NSNotFound) - { - if ([delimiter rangeOfString:@""]; - } - - if ([text rangeOfString:@""]; + text = [text stringByReplacingOccurrencesOfString:@"&" withString:@"&"]; + [plainText appendString:text]; + } + + // Scan html tag + [scanner scanUpToString:@">" intoString:&text]; + [scanner scanString:@">" intoString:nil]; // Skip closing '>' + NSString *tag = [text stringByAppendingString:@">"]; + + if ([tag rangeOfString:@"=0; i--) - { - RTLabelComponent *component = [components objectAtIndex:i]; - if (component.text==nil && [component.tagLabel isEqualToString:tag]) - { - NSString *text2 = [data substringWithRange:NSMakeRange(component.position, position-component.position)]; - component.text = text2; - break; - } + // End of tag + NSString *tag_name = [tag substringWithRange:NSMakeRange(2, tag.length - 3)]; + for (int i=[components count]-1; i>=0; i--) + { + RTLabelComponent *component = [components objectAtIndex:i]; + if (component.text==nil && [component.tagLabel isEqualToString:tag_name]) + { + NSString *text2 = [plainText substringWithRange:NSMakeRange(component.position, plainText.length - component.position)]; + component.text = text2; + break; } } } - else + else if([tag rangeOfString:@"<"].location == 0) { - // start of tag - NSArray *textComponents = [[text substringFromIndex:1] componentsSeparatedByString:@" "]; - tag = [textComponents objectAtIndex:0]; - //NSLog(@"start of tag: %@", tag); + // Start of tag + NSArray *textComponents = [[tag substringWithRange:NSMakeRange(1, tag.length - 2)] componentsSeparatedByString:@" "]; + NSString *tag_name = [textComponents objectAtIndex:0]; NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; for (NSUInteger i=1; i<[textComponents count]; i++) { @@ -932,14 +925,13 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph } } } - RTLabelComponent *component = [RTLabelComponent componentWithString:nil tag:tag attributes:attributes]; - component.position = position; + RTLabelComponent *component = [RTLabelComponent componentWithString:nil tag:tag_name attributes:attributes]; + component.position = plainText.length; [components addObject:component]; } - last_position = position; } - return [RTLabelExtractedComponent rtLabelExtractComponentsWithTextComponent:components plainText:data]; + return [RTLabelExtractedComponent rtLabelExtractComponentsWithTextComponent:components plainText:plainText]; } From 7733cb32e96c3045fcc9c7f761b86420479edf0b Mon Sep 17 00:00:00 2001 From: czarny Date: Sun, 27 Oct 2013 15:23:20 +0100 Subject: [PATCH 02/11] Added eitites of all html reserved characters. --- RTLabelProject/Classes/RTLabel.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RTLabelProject/Classes/RTLabel.m b/RTLabelProject/Classes/RTLabel.m index 833f4f6..5311f20 100755 --- a/RTLabelProject/Classes/RTLabel.m +++ b/RTLabelProject/Classes/RTLabel.m @@ -875,6 +875,8 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph text = [text stringByReplacingOccurrencesOfString:@"<" withString:@"<"]; text = [text stringByReplacingOccurrencesOfString:@">" withString:@">"]; text = [text stringByReplacingOccurrencesOfString:@"&" withString:@"&"]; + text = [text stringByReplacingOccurrencesOfString:@"'" withString:@"'"]; + text = [text stringByReplacingOccurrencesOfString:@""" withString:@"\""]; [plainText appendString:text]; } From 57d85d8cc4b42c2028d49c237cdf7854bb6e1a11 Mon Sep 17 00:00:00 2001 From: czarny Date: Thu, 5 Dec 2013 15:19:38 +0100 Subject: [PATCH 03/11] Added support for 'sup' and 'sub' HTML tags. --- README.md | 2 ++ .../Classes/DemoTableViewController.m | 4 +++ RTLabelProject/Classes/RTLabel.m | 35 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/README.md b/README.md index 66655d9..642fbc9 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Features * kerning * line spacing * clickable links +* superscript and subscript Usage ----- @@ -47,6 +48,7 @@ Usage custom font with kerning

alignment

indentation

+ superscript and subscript Minimum Requirements -------------------- diff --git a/RTLabelProject/Classes/DemoTableViewController.m b/RTLabelProject/Classes/DemoTableViewController.m index ea1ad2d..274c69d 100644 --- a/RTLabelProject/Classes/DemoTableViewController.m +++ b/RTLabelProject/Classes/DemoTableViewController.m @@ -91,6 +91,10 @@ - (id)initWithStyle:(UITableViewStyle)style NSMutableDictionary *row20 = [NSMutableDictionary dictionary]; [row20 setObject:@"

Indented bla bla bla bla bla bla bla bla bla bla bla bla bla

" forKey:@"text"]; [self.dataArray addObject:row20]; + + NSMutableDictionary *row9 = [NSMutableDictionary dictionary]; + [row9 setObject:@"Superscriptsup and subscriptsub" forKey:@"text"]; + [self.dataArray addObject:row9]; } return self; } diff --git a/RTLabelProject/Classes/RTLabel.m b/RTLabelProject/Classes/RTLabel.m index 5311f20..79d8925 100755 --- a/RTLabelProject/Classes/RTLabel.m +++ b/RTLabelProject/Classes/RTLabel.m @@ -312,6 +312,14 @@ - (void)render { [self applyCenterStyleToText:attrString attributes:component.attributes atPosition:component.position withLength:[component.text length]]; } + else if ([component.tagLabel caseInsensitiveCompare:@"sup"] == NSOrderedSame) + { + [self applySuperscriptStyle:1 toText:attrString atPosition:component.position withLength:[component.text length]]; + } + else if ([component.tagLabel caseInsensitiveCompare:@"sub"] == NSOrderedSame) + { + [self applySuperscriptStyle:-1 toText:attrString atPosition:component.position withLength:[component.text length]]; + } } // Create the framesetter with the attributed string. @@ -714,6 +722,33 @@ - (void)applyUnderlineColor:(NSString*)value toText:(CFMutableAttributedStringRe } + +- (void)applySuperscriptStyle:(int)value toText:(CFMutableAttributedStringRef)text atPosition:(int)position withLength:(int)length +{ + // Get current font + CFTypeRef actualFontRef = CFAttributedStringGetAttribute(text, position, kCTFontAttributeName, NULL); + if(!actualFontRef) + actualFontRef = (__bridge CTFontRef)[UIFont systemFontOfSize:[UIFont systemFontSize]]; + + // Make font smaller + CFNumberRef sizeRef = CTFontCopyAttribute(actualFontRef, kCTFontSizeAttribute); + float size = 0; + CFNumberGetValue(sizeRef, kCFNumberFloat32Type, &size); + CTFontRef customFont = CTFontCreateCopyWithAttributes(actualFontRef, size * 0.7f, 0, 0); + CFRelease(sizeRef); + CFAttributedStringSetAttribute(text, CFRangeMake(position, length), kCTFontAttributeName, customFont); + + // Move base line + CFMutableDictionaryRef styleDict = CFDictionaryCreateMutable( 0, 0, NULL, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(styleDict, kCTBaselineReferenceFont, customFont); + CFDictionaryAddValue(styleDict, kCTBaselineClassIdeographicLow, (__bridge CFNumberRef)@(value * size/3.5)); + CFAttributedStringSetAttribute(text, CFRangeMake(position, length), kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow); + CFAttributedStringSetAttribute(text, CFRangeMake(position, length), kCTBaselineReferenceInfoAttributeName, styleDict); + + CFRelease(customFont); + CFRelease(styleDict); +} + #pragma mark - #pragma mark button From b061831d11e8d8533e55ed7eecc4f38c321657ee Mon Sep 17 00:00:00 2001 From: czarny Date: Wed, 5 Mar 2014 13:47:15 +0100 Subject: [PATCH 04/11] Added stripping of white characters between tags. --- RTLabelProject/Classes/RTLabel.m | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/RTLabelProject/Classes/RTLabel.m b/RTLabelProject/Classes/RTLabel.m index 79d8925..d63d743 100755 --- a/RTLabelProject/Classes/RTLabel.m +++ b/RTLabelProject/Classes/RTLabel.m @@ -893,12 +893,15 @@ - (NSArray *)components } + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraphReplacement:(NSString*)paragraphReplacement -{ +{ NSMutableArray *components = [NSMutableArray array]; NSMutableString *plainText = [NSMutableString new]; + + NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:@">\\s*<" options:NSRegularExpressionCaseInsensitive error:nil]; + data = [regex stringByReplacingMatchesInString:data options:0 range:NSMakeRange(0, data.length) withTemplate:@"><"]; NSScanner *scanner = [NSScanner scannerWithString:data]; scanner.charactersToBeSkipped = nil; - + while (![scanner isAtEnd]) { // Scan plain text From 73b709f9ea2ad8a809841ba6b6345a512f864c14 Mon Sep 17 00:00:00 2001 From: czarny Date: Wed, 5 Mar 2014 15:59:19 +0100 Subject: [PATCH 05/11] Added handling of html lists (ol, ul, li). --- RTLabelProject/Classes/RTLabel.m | 86 ++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/RTLabelProject/Classes/RTLabel.m b/RTLabelProject/Classes/RTLabel.m index d63d743..69a845d 100755 --- a/RTLabelProject/Classes/RTLabel.m +++ b/RTLabelProject/Classes/RTLabel.m @@ -127,6 +127,30 @@ - (void)applyParagraphStyleToText:(CFMutableAttributedStringRef)text attributes: @implementation RTLabel +static NSString *ListPointString(NSString *type, NSInteger index) { + static NSString *RomanMap[] = {@"I", @"II", @"III", @"IV", @"V", @"VI", @"VII", @"VIII", @"IX", @"X", @"XI", @"XII", @"XIII", @"XIV", @"XIV"}; // Who needs more ? + + NSString *point = @""; + if([type isEqualToString:@"1"]) + point = [NSString stringWithFormat:@"%d. ", index]; + else if([type isEqualToString:@"A"]) + point = [NSString stringWithFormat:@"%c. ", 'A' + index - 1]; + else if([type isEqualToString:@"a"]) + point = [NSString stringWithFormat:@"%c. ", 'a' + index - 1]; + else if([type isEqualToString:@"I"]) + point = [NSString stringWithFormat:@"%@. ", RomanMap[index - 1]]; + else if([type isEqualToString:@"i"]) + point = [NSString stringWithFormat:@"%@. ", [RomanMap[index - 1] lowercaseString]]; + else if([type isEqualToString:@"circle"]) + point = @"\u25CB "; + else if([type isEqualToString:@"disc"]) + point = @"\u25CF "; + else if([type isEqualToString:@"square"]) + point = @"\u25A0 "; + + return point; +} + - (id)initWithFrame:(CGRect)_frame { self = [super initWithFrame:_frame]; @@ -275,11 +299,7 @@ - (void)render [self applySingleUnderlineText:attrString atPosition:component.position withLength:[component.text length]]; } } - - NSString *value = [component.attributes objectForKey:@"href"]; - value = [value stringByReplacingOccurrencesOfString:@"'" withString:@""]; - [component.attributes setObject:value forKey:@"href"]; - + [links addObject:component]; } else if ([component.tagLabel caseInsensitiveCompare:@"u"] == NSOrderedSame || [component.tagLabel caseInsensitiveCompare:@"uu"] == NSOrderedSame) @@ -575,7 +595,6 @@ - (void)applyFontAttributes:(NSDictionary*)attributes toText:(CFMutableAttribute for (NSString *key in attributes) { NSString *value = [attributes objectForKey:key]; - value = [value stringByReplacingOccurrencesOfString:@"'" withString:@""]; if ([key caseInsensitiveCompare:@"color"] == NSOrderedSame) { @@ -618,13 +637,11 @@ - (void)applyFontAttributes:(NSDictionary*)attributes toText:(CFMutableAttribute if ([attributes objectForKey:@"face"] && [attributes objectForKey:@"size"]) { NSString *fontName = [attributes objectForKey:@"face"]; - fontName = [fontName stringByReplacingOccurrencesOfString:@"'" withString:@""]; font = [UIFont fontWithName:fontName size:[[attributes objectForKey:@"size"] intValue]]; } else if ([attributes objectForKey:@"face"] && ![attributes objectForKey:@"size"]) { NSString *fontName = [attributes objectForKey:@"face"]; - fontName = [fontName stringByReplacingOccurrencesOfString:@"'" withString:@""]; font = [UIFont fontWithName:fontName size:self.font.pointSize]; } else if (![attributes objectForKey:@"face"] && [attributes objectForKey:@"size"]) @@ -696,8 +713,6 @@ - (void)applyColor:(NSString*)value toText:(CFMutableAttributedStringRef)text at - (void)applyUnderlineColor:(NSString*)value toText:(CFMutableAttributedStringRef)text atPosition:(int)position withLength:(int)length { - - value = [value stringByReplacingOccurrencesOfString:@"'" withString:@""]; if ([value rangeOfString:@"#"].location==0) { CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); value = [value stringByReplacingOccurrencesOfString:@"#" withString:@"0x"]; @@ -896,6 +911,10 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph { NSMutableArray *components = [NSMutableArray array]; NSMutableString *plainText = [NSMutableString new]; + int listIndent = 0; + int listPointCounter[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + NSString *listPointType[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + BOOL listPoint = NO; NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:@">\\s*<" options:NSRegularExpressionCaseInsensitive error:nil]; data = [regex stringByReplacingMatchesInString:data options:0 range:NSMakeRange(0, data.length) withTemplate:@"><"]; @@ -915,6 +934,16 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph text = [text stringByReplacingOccurrencesOfString:@"&" withString:@"&"]; text = [text stringByReplacingOccurrencesOfString:@"'" withString:@"'"]; text = [text stringByReplacingOccurrencesOfString:@""" withString:@"\""]; + + if(listPoint && listIndent) { + listPoint = NO; + + NSString *point = ListPointString(listPointType[listIndent], listPointCounter[listIndent]); + text = [NSString stringWithFormat:@"%@%@", point, text]; + for(int i = 0; i < listIndent - 1; i++) + text = [NSString stringWithFormat:@"\t%@", text]; + } + [plainText appendString:text]; } @@ -923,13 +952,19 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph [scanner scanString:@">" intoString:nil]; // Skip closing '>' NSString *tag = [text stringByAppendingString:@">"]; - if ([tag rangeOfString:@"=0; i--) { RTLabelComponent *component = [components objectAtIndex:i]; @@ -946,6 +981,7 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph // Start of tag NSArray *textComponents = [[tag substringWithRange:NSMakeRange(1, tag.length - 2)] componentsSeparatedByString:@" "]; NSString *tag_name = [textComponents objectAtIndex:0]; + NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; for (NSUInteger i=1; i<[textComponents count]; i++) { @@ -958,6 +994,8 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph NSString *value = [[pair subarrayWithRange:NSMakeRange(1, [pair count] - 1)] componentsJoinedByString:@"="]; value = [value stringByReplacingOccurrencesOfString:@"\"" withString:@"" options:NSLiteralSearch range:NSMakeRange(0, 1)]; value = [value stringByReplacingOccurrencesOfString:@"\"" withString:@"" options:NSLiteralSearch range:NSMakeRange([value length]-1, 1)]; + value = [value stringByReplacingOccurrencesOfString:@"'" withString:@"" options:NSLiteralSearch range:NSMakeRange(0, 1)]; + value = [value stringByReplacingOccurrencesOfString:@"'" withString:@"" options:NSLiteralSearch range:NSMakeRange([value length]-1, 1)]; [attributes setObject:value forKey:key]; } else if ([pair count]==1) { @@ -965,6 +1003,26 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph } } } + + if ([tag_name caseInsensitiveCompare:@"p"] == NSOrderedSame) + [plainText appendString:paragraphReplacement]; + else if ([tag_name caseInsensitiveCompare:@"ul"] == NSOrderedSame || [tag_name caseInsensitiveCompare:@"ol"] == NSOrderedSame) { + // Start of html list + listIndent++; + listPointType[listIndent] = [tag_name caseInsensitiveCompare:@"ol"] == NSOrderedSame ? @"1" : @"circle"; // Default types + if(attributes[@"type"]) + listPointType[listIndent] = attributes[@"type"]; + + if(plainText.length && ![plainText hasSuffix:@"\n"]) + [plainText appendString:@"\n"]; + } + else if([tag_name caseInsensitiveCompare:@"li"] == NSOrderedSame) { + // New list point + listPoint = YES; + listPointCounter[listIndent]++; + } + + RTLabelComponent *component = [RTLabelComponent componentWithString:nil tag:tag_name attributes:attributes]; component.position = plainText.length; [components addObject:component]; @@ -981,7 +1039,7 @@ - (void)parse:(NSString *)data valid_tags:(NSArray *)valid_tags NSScanner *scanner = nil; NSString *text = nil; NSString *tag = nil; - + NSMutableArray *components = [NSMutableArray array]; //set up the scanner From 05d7a9337a1e1fe3b6ed58f7fa65a32a8b3708c7 Mon Sep 17 00:00:00 2001 From: czarny Date: Thu, 6 Mar 2014 14:28:40 +0100 Subject: [PATCH 06/11] Improved indentation in html lists. --- RTLabelProject/Classes/RTLabel.m | 49 +++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/RTLabelProject/Classes/RTLabel.m b/RTLabelProject/Classes/RTLabel.m index 69a845d..9277aeb 100755 --- a/RTLabelProject/Classes/RTLabel.m +++ b/RTLabelProject/Classes/RTLabel.m @@ -340,6 +340,10 @@ - (void)render { [self applySuperscriptStyle:-1 toText:attrString atPosition:component.position withLength:[component.text length]]; } + else if ([component.tagLabel caseInsensitiveCompare:@"li"] == NSOrderedSame) + { + [self applyLiAttributes:component.attributes toText:attrString atPosition:component.position withLength:component.text.length]; + } } // Create the framesetter with the attributed string. @@ -669,6 +673,29 @@ - (void)applyBoldStyleToText:(CFMutableAttributedStringRef)text atPosition:(int) CFRelease(boldFontRef); } +- (void)applyLiAttributes:(NSDictionary*)attributes toText:(CFMutableAttributedStringRef)text atPosition:(int)position withLength:(int)length +{ + CFMutableDictionaryRef styleDict = ( CFDictionaryCreateMutable( (0), 0, (0), (0) ) ); + CGFloat fistLineIndent = 15.0f * [attributes[@"indent"] intValue]; + CGFloat headIndent = 15.0f + fistLineIndent; + + CTParagraphStyleSetting theSettings[] = + { + + { kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(CGFloat), &fistLineIndent }, + { kCTParagraphStyleSpecifierHeadIndent, sizeof(CGFloat), &headIndent }, + { kCTParagraphStyleSpecifierMinimumLineSpacing, sizeof(CGFloat), &_lineSpacing }, // leading + { kCTParagraphStyleSpecifierMaximumLineSpacing, sizeof(CGFloat), &_lineSpacing }, // leading + }; + + CTParagraphStyleRef theParagraphRef = CTParagraphStyleCreate(theSettings, sizeof(theSettings) / sizeof(CTParagraphStyleSetting)); + CFDictionaryAddValue( styleDict, kCTParagraphStyleAttributeName, theParagraphRef ); + + CFAttributedStringSetAttributes( text, CFRangeMake(position, length), styleDict, 0 ); + CFRelease(theParagraphRef); + CFRelease(styleDict); +} + - (void)applyBoldItalicStyleToText:(CFMutableAttributedStringRef)text atPosition:(int)position withLength:(int)length { CFTypeRef actualFontRef = CFAttributedStringGetAttribute(text, position, kCTFontAttributeName, NULL); @@ -940,8 +967,6 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph NSString *point = ListPointString(listPointType[listIndent], listPointCounter[listIndent]); text = [NSString stringWithFormat:@"%@%@", point, text]; - for(int i = 0; i < listIndent - 1; i++) - text = [NSString stringWithFormat:@"\t%@", text]; } [plainText appendString:text]; @@ -957,14 +982,6 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph // End of tag NSString *tag_name = [tag substringWithRange:NSMakeRange(2, tag.length - 3)]; - if ([tag_name caseInsensitiveCompare:@"ul"] == NSOrderedSame || [tag_name caseInsensitiveCompare:@"ol"] == NSOrderedSame) { - [plainText deleteCharactersInRange:NSMakeRange(plainText.length - 1, 1)]; - listPointCounter[listIndent] = 0; - listIndent--; - } - else if([tag_name caseInsensitiveCompare:@"li"] == NSOrderedSame) - [plainText appendString:@"\n"]; - for (int i=[components count]-1; i>=0; i--) { RTLabelComponent *component = [components objectAtIndex:i]; @@ -975,6 +992,11 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph break; } } + + if ([tag_name caseInsensitiveCompare:@"ul"] == NSOrderedSame || [tag_name caseInsensitiveCompare:@"ol"] == NSOrderedSame) { + listPointCounter[listIndent] = 0; + listIndent--; + } } else if([tag rangeOfString:@"<"].location == 0) { @@ -1012,14 +1034,15 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph listPointType[listIndent] = [tag_name caseInsensitiveCompare:@"ol"] == NSOrderedSame ? @"1" : @"circle"; // Default types if(attributes[@"type"]) listPointType[listIndent] = attributes[@"type"]; - - if(plainText.length && ![plainText hasSuffix:@"\n"]) - [plainText appendString:@"\n"]; } else if([tag_name caseInsensitiveCompare:@"li"] == NSOrderedSame) { // New list point + attributes[@"indent"] = @(listIndent); listPoint = YES; listPointCounter[listIndent]++; + + if(plainText.length && [tag_name caseInsensitiveCompare:@"li"] == NSOrderedSame) + [plainText appendString:@"\n"]; } From b781f52e265cd3071ca32bda7953cf982a1af2aa Mon Sep 17 00:00:00 2001 From: czarny Date: Mon, 10 Mar 2014 10:25:21 +0100 Subject: [PATCH 07/11] Added support for long lists marked by letters. --- RTLabelProject/Classes/RTLabel.m | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/RTLabelProject/Classes/RTLabel.m b/RTLabelProject/Classes/RTLabel.m index 9277aeb..b2db1df 100755 --- a/RTLabelProject/Classes/RTLabel.m +++ b/RTLabelProject/Classes/RTLabel.m @@ -127,6 +127,20 @@ - (void)applyParagraphStyleToText:(CFMutableAttributedStringRef)text attributes: @implementation RTLabel + +static NSString *LettersForIndex(NSInteger index) { + NSString *result = @""; + index--; + do { + if(result.length) + index--; + int rest = index % ('Z' - 'A' + 1); + index /= 'Z' - 'A' + 1; + result = [[NSString stringWithFormat:@"%c", rest + 'A'] stringByAppendingString:result]; + } while (index > 0); + return result; +} + static NSString *ListPointString(NSString *type, NSInteger index) { static NSString *RomanMap[] = {@"I", @"II", @"III", @"IV", @"V", @"VI", @"VII", @"VIII", @"IX", @"X", @"XI", @"XII", @"XIII", @"XIV", @"XIV"}; // Who needs more ? @@ -134,9 +148,9 @@ @implementation RTLabel if([type isEqualToString:@"1"]) point = [NSString stringWithFormat:@"%d. ", index]; else if([type isEqualToString:@"A"]) - point = [NSString stringWithFormat:@"%c. ", 'A' + index - 1]; + point = [LettersForIndex(index) stringByAppendingString:@". "]; else if([type isEqualToString:@"a"]) - point = [NSString stringWithFormat:@"%c. ", 'a' + index - 1]; + point = [[LettersForIndex(index) lowercaseString] stringByAppendingString:@". "]; else if([type isEqualToString:@"I"]) point = [NSString stringWithFormat:@"%@. ", RomanMap[index - 1]]; else if([type isEqualToString:@"i"]) From 37366812c65b27bf07dbe332c74a938f78dc0c17 Mon Sep 17 00:00:00 2001 From: czarny Date: Mon, 10 Mar 2014 10:39:14 +0100 Subject: [PATCH 08/11] Added support for long lists marked by roman numbers. --- RTLabelProject/Classes/RTLabel.m | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/RTLabelProject/Classes/RTLabel.m b/RTLabelProject/Classes/RTLabel.m index b2db1df..d9bed5c 100755 --- a/RTLabelProject/Classes/RTLabel.m +++ b/RTLabelProject/Classes/RTLabel.m @@ -141,9 +141,26 @@ @implementation RTLabel return result; } -static NSString *ListPointString(NSString *type, NSInteger index) { - static NSString *RomanMap[] = {@"I", @"II", @"III", @"IV", @"V", @"VI", @"VII", @"VIII", @"IX", @"X", @"XI", @"XII", @"XIII", @"XIV", @"XIV"}; // Who needs more ? +static NSString *RomanForIndex(int index) { + static NSString *huns[] = {@"", @"C", @"CC", @"CCC", @"CD", @"D", @"DC", @"DCC", @"DCCC", @"CM"}; + static NSString *tens[] = {@"", @"X", @"XX", @"XXX", @"XL", @"L", @"LX", @"LXX", @"LXXX", @"XC"}; + static NSString *ones[] = {@"", @"I", @"II", @"III", @"IV", @"V", @"VI", @"VII", @"VIII", @"IX"}; + + NSMutableString *result = [NSMutableString new]; + while (index >= 1000) { + [result appendString:@"M"]; + index -= 1000; + } + + [result appendString:huns[index / 100]]; + index %= 100; + [result appendString:tens[index / 10]]; + index %= 10; + [result appendString:ones[index]]; + return result; +} +static NSString *ListPointString(NSString *type, NSInteger index) { NSString *point = @""; if([type isEqualToString:@"1"]) point = [NSString stringWithFormat:@"%d. ", index]; @@ -152,9 +169,9 @@ @implementation RTLabel else if([type isEqualToString:@"a"]) point = [[LettersForIndex(index) lowercaseString] stringByAppendingString:@". "]; else if([type isEqualToString:@"I"]) - point = [NSString stringWithFormat:@"%@. ", RomanMap[index - 1]]; + point = [NSString stringWithFormat:@"%@. ", RomanForIndex(index)]; else if([type isEqualToString:@"i"]) - point = [NSString stringWithFormat:@"%@. ", [RomanMap[index - 1] lowercaseString]]; + point = [NSString stringWithFormat:@"%@. ", [RomanForIndex(index) lowercaseString]]; else if([type isEqualToString:@"circle"]) point = @"\u25CB "; else if([type isEqualToString:@"disc"]) From e13dd0d7670b531a073c2699163677fa267723cd Mon Sep 17 00:00:00 2001 From: czarny Date: Mon, 10 Mar 2014 10:52:48 +0100 Subject: [PATCH 09/11] Updated description and demo. --- README.md | 2 ++ RTLabelProject/Classes/DemoTableViewController.m | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index 642fbc9..fae594d 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Features * line spacing * clickable links * superscript and subscript +* html lists of all types Usage ----- @@ -49,6 +50,7 @@ Usage

alignment

indentation

superscript and subscript +
  • one
  • two
Minimum Requirements -------------------- diff --git a/RTLabelProject/Classes/DemoTableViewController.m b/RTLabelProject/Classes/DemoTableViewController.m index 274c69d..53e2e8a 100644 --- a/RTLabelProject/Classes/DemoTableViewController.m +++ b/RTLabelProject/Classes/DemoTableViewController.m @@ -95,6 +95,10 @@ - (id)initWithStyle:(UITableViewStyle)style NSMutableDictionary *row9 = [NSMutableDictionary dictionary]; [row9 setObject:@"Superscriptsup and subscriptsub" forKey:@"text"]; [self.dataArray addObject:row9]; + + NSMutableDictionary *row10 = [NSMutableDictionary dictionary]; + row10[@"text"] = @"
  1. one
  2. two
    • one
    • two
"; + [self.dataArray insertObject:row10 atIndex:0]; } return self; } From c32bcc59f589d4dceca0420748e8e82c167b56d4 Mon Sep 17 00:00:00 2001 From: czarny Date: Tue, 24 Feb 2015 13:01:52 +0100 Subject: [PATCH 10/11] Improved trimming of whitespaces. --- RTLabelProject/Classes/RTLabel.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RTLabelProject/Classes/RTLabel.m b/RTLabelProject/Classes/RTLabel.m index d9bed5c..b60c0e4 100755 --- a/RTLabelProject/Classes/RTLabel.m +++ b/RTLabelProject/Classes/RTLabel.m @@ -974,8 +974,7 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph NSString *listPointType[8] = {0, 0, 0, 0, 0, 0, 0, 0}; BOOL listPoint = NO; - NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:@">\\s*<" options:NSRegularExpressionCaseInsensitive error:nil]; - data = [regex stringByReplacingMatchesInString:data options:0 range:NSMakeRange(0, data.length) withTemplate:@"><"]; + NSRegularExpression *white_trimmer = [[NSRegularExpression alloc] initWithPattern:@"\\s+" options:NSRegularExpressionCaseInsensitive error:nil]; NSScanner *scanner = [NSScanner scannerWithString:data]; scanner.charactersToBeSkipped = nil; @@ -992,6 +991,7 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph text = [text stringByReplacingOccurrencesOfString:@"&" withString:@"&"]; text = [text stringByReplacingOccurrencesOfString:@"'" withString:@"'"]; text = [text stringByReplacingOccurrencesOfString:@""" withString:@"\""]; + text = [white_trimmer stringByReplacingMatchesInString:text options:0 range:NSMakeRange(0, text.length) withTemplate:@" "]; if(listPoint && listIndent) { listPoint = NO; From c4eeee684f37d60bb0a9b4dc1d42115f6087451f Mon Sep 17 00:00:00 2001 From: czarny Date: Tue, 24 Feb 2015 13:06:27 +0100 Subject: [PATCH 11/11] Fixed handling of br tags. --- RTLabelProject/Classes/RTLabel.m | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/RTLabelProject/Classes/RTLabel.m b/RTLabelProject/Classes/RTLabel.m index b60c0e4..8d243f4 100755 --- a/RTLabelProject/Classes/RTLabel.m +++ b/RTLabelProject/Classes/RTLabel.m @@ -873,14 +873,14 @@ - (void)setHighlighted:(BOOL)highlighted - (void)setHighlightedText:(NSString *)text { - _highlightedText = [text stringByReplacingOccurrencesOfString:@"
" withString:@"\n"]; + _highlightedText = text; RTLabelExtractedComponent *component = [RTLabel extractTextStyleFromText:_highlightedText paragraphReplacement:self.paragraphReplacement]; [self setHighlightedTextComponents:component.textComponents]; } - (void)setText:(NSString *)text { - _text = [text stringByReplacingOccurrencesOfString:@"
" withString:@"\n"]; + _text = text; RTLabelExtractedComponent *component = [RTLabel extractTextStyleFromText:_text paragraphReplacement:self.paragraphReplacement]; [self setTextComponents:component.textComponents]; [self setPlainText:component.plainText]; @@ -889,7 +889,7 @@ - (void)setText:(NSString *)text - (void)setText:(NSString *)text extractedTextComponent:(RTLabelExtractedComponent*)extractedComponent { - _text = [text stringByReplacingOccurrencesOfString:@"
" withString:@"\n"]; + _text = text; [self setTextComponents:extractedComponent.textComponents]; [self setPlainText:extractedComponent.plainText]; [self setNeedsDisplay]; @@ -897,7 +897,7 @@ - (void)setText:(NSString *)text extractedTextComponent:(RTLabelExtractedCompone - (void)setHighlightedText:(NSString *)text extractedTextComponent:(RTLabelExtractedComponent*)extractedComponent { - _highlightedText = [text stringByReplacingOccurrencesOfString:@"
" withString:@"\n"]; + _highlightedText = text; [self setHighlightedTextComponents:extractedComponent.textComponents]; } @@ -1075,7 +1075,10 @@ + (RTLabelExtractedComponent*)extractTextStyleFromText:(NSString*)data paragraph if(plainText.length && [tag_name caseInsensitiveCompare:@"li"] == NSOrderedSame) [plainText appendString:@"\n"]; } - + else if ([tag_name caseInsensitiveCompare:@"br"] == NSOrderedSame) { + [plainText appendString:@"\n"]; + continue; + } RTLabelComponent *component = [RTLabelComponent componentWithString:nil tag:tag_name attributes:attributes]; component.position = plainText.length; @@ -1211,7 +1214,7 @@ - (NSString*)visibleText - (void)setText:(NSString *)text extractedTextStyle:(NSDictionary*)extractTextStyle { - _text = [text stringByReplacingOccurrencesOfString:@"
" withString:@"\n"]; + _text = text; [self setTextComponents:[extractTextStyle objectForKey:@"textComponents"]]; [self setPlainText:[extractTextStyle objectForKey:@"plainText"]]; [self setNeedsDisplay];