From 9e7e8003cca56c69143a717bd708d09f8fb37120 Mon Sep 17 00:00:00 2001 From: dzenbot Date: Wed, 4 Dec 2013 13:59:34 -0300 Subject: [PATCH 1/5] Added optional bool for avoiding default black color rendering of attributed string --- src/NSAttributedStringMarkdownParser.h | 1 + src/NSAttributedStringMarkdownParser.m | 1 + 2 files changed, 2 insertions(+) diff --git a/src/NSAttributedStringMarkdownParser.h b/src/NSAttributedStringMarkdownParser.h index 535e1e7..5c6464b 100644 --- a/src/NSAttributedStringMarkdownParser.h +++ b/src/NSAttributedStringMarkdownParser.h @@ -44,6 +44,7 @@ typedef enum { @property (nonatomic, copy) NSString* boldFontName; // Default: boldSystemFont @property (nonatomic, copy) NSString* italicFontName; // Default: Helvetica-Oblique @property (nonatomic, copy) NSString* boldItalicFontName; // Default: Helvetica-BoldOblique +@property (nonatomic) BOOL allowDefaultColor; - (void)setFont:(UIFont *)font forHeader:(NSAttributedStringMarkdownParserHeader)header; - (UIFont *)fontForHeader:(NSAttributedStringMarkdownParserHeader)header; diff --git a/src/NSAttributedStringMarkdownParser.m b/src/NSAttributedStringMarkdownParser.m index 9fb5a89..1e3433c 100644 --- a/src/NSAttributedStringMarkdownParser.m +++ b/src/NSAttributedStringMarkdownParser.m @@ -238,6 +238,7 @@ - (void)consumeToken:(int)token text:(char*)text { NSMutableDictionary* attributes = [NSMutableDictionary dictionary]; [attributes addEntriesFromDictionary:[self attributesForFont:self.topFont]]; + [attributes setValue:[NSNumber numberWithBool:_allowDefaultColor] forKey:(NSString *)kCTForegroundColorFromContextAttributeName]; switch (token) { case MARKDOWNEM: { // * * From c33c2081931ca36793e7682b7c1eb174b4011c54 Mon Sep 17 00:00:00 2001 From: dzenbot Date: Wed, 4 Dec 2013 14:16:52 -0300 Subject: [PATCH 2/5] Update the git submodules to public urls --- .gitignore | 1 + fmemopen | 2 +- nimbus | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index fca9853..464c369 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Xcode +.DS_Store build/* *.pbxuser !default.pbxuser diff --git a/fmemopen b/fmemopen index 1d696d3..44f91de 160000 --- a/fmemopen +++ b/fmemopen @@ -1 +1 @@ -Subproject commit 1d696d3793b1ab6a0fc1b7bc07ff2770534aa2a2 +Subproject commit 44f91decc1ff211c2f810dba91f381b7f325ddbb diff --git a/nimbus b/nimbus index 98e18b8..d2f5fd2 160000 --- a/nimbus +++ b/nimbus @@ -1 +1 @@ -Subproject commit 98e18b88af9dc3a3ffe5ebaa8f8d7faf62df1fff +Subproject commit d2f5fd2ab1209c5af37efe83b73405cb4612766a From 87a34a32a195e574499257989ff05e39cd00ce9c Mon Sep 17 00:00:00 2001 From: dzenbot Date: Wed, 4 Dec 2013 14:17:22 -0300 Subject: [PATCH 3/5] Updated the sample project automatic settings --- .DS_Store | Bin 0 -> 6148 bytes .gitmodules | 4 +- .../project.pbxproj | 444 +++++++++--------- 3 files changed, 224 insertions(+), 224 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..8a59af9ef3898dc8c3b2fee7e79077a2530b93cf GIT binary patch literal 6148 zcmeHKO-sW-5Pe&FsCX%!JTCqKp=U3##G`n~A5ar%{YVoeL67_Ay!wv>-|Q@q; zWCmv5W_D(ieG5Ao0B+t_Ghhl}LKPex(tIN_FFKG>@{EZtu2G@ED^|EbH;6XJJ~AL@ zcZw_A;ssCGJim^f2i&3OS>AJ|Ac|*&1l4o6Urlq&RX7z2IPM5 zRKYA^P>fdxO>6;(@*5e2W9cO%$0y7Z21U+LoTd^r)uk50X*&Hr#bpVDqNc;8=EJ2o zyVRk$_;#-EV>n! Date: Wed, 4 Dec 2013 14:32:03 -0300 Subject: [PATCH 4/5] Removed 'allowDefaultColor' attribute. Used 'foregroundColor' instead for setting generic text color for replacing NSAttributedString default color. --- .DS_Store | Bin 6148 -> 6148 bytes src/NSAttributedStringMarkdownParser.h | 16 +- src/NSAttributedStringMarkdownParser.m | 534 +++++++++++++------------ 3 files changed, 277 insertions(+), 273 deletions(-) diff --git a/.DS_Store b/.DS_Store index 8a59af9ef3898dc8c3b2fee7e79077a2530b93cf..1e66f017559865759a936fd6acda4d7dfa1dd89e 100644 GIT binary patch delta 18 ZcmZoMXffE3%*6O~@*XCy&Fh%LL;*u72F(Bf delta 18 ZcmZoMXffE3%*5zFc@LA<=5 typedef enum { - NSAttributedStringMarkdownParserHeader1, - NSAttributedStringMarkdownParserHeader2, - NSAttributedStringMarkdownParserHeader3, - NSAttributedStringMarkdownParserHeader4, - NSAttributedStringMarkdownParserHeader5, - NSAttributedStringMarkdownParserHeader6, - + NSAttributedStringMarkdownParserHeader1, + NSAttributedStringMarkdownParserHeader2, + NSAttributedStringMarkdownParserHeader3, + NSAttributedStringMarkdownParserHeader4, + NSAttributedStringMarkdownParserHeader5, + NSAttributedStringMarkdownParserHeader6, + } NSAttributedStringMarkdownParserHeader; @protocol NSAttributedStringMarkdownStylesheet; @@ -44,7 +44,7 @@ typedef enum { @property (nonatomic, copy) NSString* boldFontName; // Default: boldSystemFont @property (nonatomic, copy) NSString* italicFontName; // Default: Helvetica-Oblique @property (nonatomic, copy) NSString* boldItalicFontName; // Default: Helvetica-BoldOblique -@property (nonatomic) BOOL allowDefaultColor; +@property (nonatomic, strong) UIColor* foregroundColor; // Default: blackColor - (void)setFont:(UIFont *)font forHeader:(NSAttributedStringMarkdownParserHeader)header; - (UIFont *)fontForHeader:(NSAttributedStringMarkdownParserHeader)header; diff --git a/src/NSAttributedStringMarkdownParser.m b/src/NSAttributedStringMarkdownParser.m index 1e3433c..d5d1867 100644 --- a/src/NSAttributedStringMarkdownParser.m +++ b/src/NSAttributedStringMarkdownParser.m @@ -31,7 +31,7 @@ options:NSRegularExpressionCaseInsensitive error:nil]; }); - + return _hrefRegex; } @@ -47,169 +47,170 @@ @implementation NSAttributedStringMarkdownLink @end @implementation NSAttributedStringMarkdownParser { - NSMutableDictionary* _headerFonts; - - NSMutableArray* _bulletStarts; - - NSMutableAttributedString* _accum; - NSMutableArray* _links; - - UIFont* _topFont; - NSMutableDictionary* _fontCache; + NSMutableDictionary* _headerFonts; + + NSMutableArray* _bulletStarts; + + NSMutableAttributedString* _accum; + NSMutableArray* _links; + + UIFont* _topFont; + NSMutableDictionary* _fontCache; } - (id)init { - if ((self = [super init])) { - _headerFonts = [NSMutableDictionary dictionary]; - - self.paragraphFont = [UIFont systemFontOfSize:12]; - self.boldFontName = [UIFont boldSystemFontOfSize:12].fontName; - self.italicFontName = @"Helvetica-Oblique"; - self.boldItalicFontName = @"Helvetica-BoldOblique"; - - NSAttributedStringMarkdownParserHeader header = NSAttributedStringMarkdownParserHeader1; - for (CGFloat headerFontSize = 24; headerFontSize >= 14; headerFontSize -= 2, header++) { - [self setFont:[UIFont systemFontOfSize:headerFontSize] forHeader:header]; + if ((self = [super init])) { + _headerFonts = [NSMutableDictionary dictionary]; + + self.paragraphFont = [UIFont systemFontOfSize:12]; + self.boldFontName = [UIFont boldSystemFontOfSize:12].fontName; + self.italicFontName = @"Helvetica-Oblique"; + self.boldItalicFontName = @"Helvetica-BoldOblique"; + self.foregroundColor = [UIColor blackColor]; + + NSAttributedStringMarkdownParserHeader header = NSAttributedStringMarkdownParserHeader1; + for (CGFloat headerFontSize = 24; headerFontSize >= 14; headerFontSize -= 2, header++) { + [self setFont:[UIFont systemFontOfSize:headerFontSize] forHeader:header]; + } } - } - return self; + return self; } - (id)copyWithZone:(NSZone *)zone { - NSAttributedStringMarkdownParser* parser = [[self.class allocWithZone:zone] init]; - parser.paragraphFont = self.paragraphFont; - parser.boldFontName = self.boldFontName; - parser.italicFontName = self.italicFontName; - parser.boldItalicFontName = self.boldItalicFontName; - for (NSAttributedStringMarkdownParserHeader header = NSAttributedStringMarkdownParserHeader1; header <= NSAttributedStringMarkdownParserHeader6; ++header) { - [parser setFont:[self fontForHeader:header] forHeader:header]; - } - return parser; + NSAttributedStringMarkdownParser* parser = [[self.class allocWithZone:zone] init]; + parser.paragraphFont = self.paragraphFont; + parser.boldFontName = self.boldFontName; + parser.italicFontName = self.italicFontName; + parser.boldItalicFontName = self.boldItalicFontName; + for (NSAttributedStringMarkdownParserHeader header = NSAttributedStringMarkdownParserHeader1; header <= NSAttributedStringMarkdownParserHeader6; ++header) { + [parser setFont:[self fontForHeader:header] forHeader:header]; + } + return parser; } - (id)keyForHeader:(NSAttributedStringMarkdownParserHeader)header { - return @(header); + return @(header); } - (void)setFont:(UIFont *)font forHeader:(NSAttributedStringMarkdownParserHeader)header { - _headerFonts[[self keyForHeader:header]] = font; + _headerFonts[[self keyForHeader:header]] = font; } - (UIFont *)fontForHeader:(NSAttributedStringMarkdownParserHeader)header { - return _headerFonts[[self keyForHeader:header]]; + return _headerFonts[[self keyForHeader:header]]; } - (NSAttributedString *)attributedStringFromMarkdownString:(NSString *)string { - _links = [NSMutableArray array]; - _bulletStarts = [NSMutableArray array]; - _accum = [[NSMutableAttributedString alloc] init]; - - const char* cstr = [string UTF8String]; - FILE* markdownin = fmemopen((void *)cstr, sizeof(char) * (string.length + 1), "r"); - - yyscan_t scanner; - - markdownlex_init(&scanner); - markdownset_extra((__bridge void *)(self), scanner); - markdownset_in(markdownin, scanner); - markdownlex(scanner); - markdownlex_destroy(scanner); - - fclose(markdownin); - - if (_bulletStarts.count > 0) { - // Treat nested bullet points as flat ones... - - // Finish off the previous dash and start a new one. - NSInteger lastBulletStart = [[_bulletStarts lastObject] intValue]; - [_bulletStarts removeLastObject]; + _links = [NSMutableArray array]; + _bulletStarts = [NSMutableArray array]; + _accum = [[NSMutableAttributedString alloc] init]; - [_accum addAttributes:[self paragraphStyle] + const char* cstr = [string UTF8String]; + FILE* markdownin = fmemopen((void *)cstr, sizeof(char) * (string.length + 1), "r"); + + yyscan_t scanner; + + markdownlex_init(&scanner); + markdownset_extra((__bridge void *)(self), scanner); + markdownset_in(markdownin, scanner); + markdownlex(scanner); + markdownlex_destroy(scanner); + + fclose(markdownin); + + if (_bulletStarts.count > 0) { + // Treat nested bullet points as flat ones... + + // Finish off the previous dash and start a new one. + NSInteger lastBulletStart = [[_bulletStarts lastObject] intValue]; + [_bulletStarts removeLastObject]; + + [_accum addAttributes:[self paragraphStyle] range:NSMakeRange(lastBulletStart, _accum.length - lastBulletStart)]; - } - - return [_accum copy]; + } + + return [_accum copy]; } - (NSArray *)links { - return [_links copy]; + return [_links copy]; } - (NSDictionary *)paragraphStyle { - CGFloat paragraphSpacing = 0.0; - CGFloat paragraphSpacingBefore = 0.0; - CGFloat firstLineHeadIndent = 15.0; - CGFloat headIndent = 30.0; - - CGFloat firstTabStop = 35.0; // width of your indent - CGFloat lineSpacing = 0.45; - + CGFloat paragraphSpacing = 0.0; + CGFloat paragraphSpacingBefore = 0.0; + CGFloat firstLineHeadIndent = 15.0; + CGFloat headIndent = 30.0; + + CGFloat firstTabStop = 35.0; // width of your indent + CGFloat lineSpacing = 0.45; + #ifdef TARGET_OS_IPHONE - NSTextAlignment alignment = NSTextAlignmentLeft; - - NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; - style.paragraphSpacing = paragraphSpacing; - style.paragraphSpacingBefore = paragraphSpacingBefore; - style.firstLineHeadIndent = firstLineHeadIndent; - style.headIndent = headIndent; - style.lineSpacing = lineSpacing; - style.alignment = alignment; - style.tabStops = @[[[NSTextTab alloc] initWithTextAlignment:alignment location:firstTabStop options:nil]]; - - return @{ NSParagraphStyleAttributeName: style }; + NSTextAlignment alignment = NSTextAlignmentLeft; + + NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; + style.paragraphSpacing = paragraphSpacing; + style.paragraphSpacingBefore = paragraphSpacingBefore; + style.firstLineHeadIndent = firstLineHeadIndent; + style.headIndent = headIndent; + style.lineSpacing = lineSpacing; + style.alignment = alignment; + style.tabStops = @[[[NSTextTab alloc] initWithTextAlignment:alignment location:firstTabStop options:nil]]; + + return @{ NSParagraphStyleAttributeName: style }; #else - CTTextAlignment alignment = kCTLeftTextAlignment; - - CTTextTabRef tabArray[] = { CTTextTabCreate(0, firstTabStop, NULL) }; - - CFArrayRef tabStops = CFArrayCreate( kCFAllocatorDefault, (const void**) tabArray, 1, &kCFTypeArrayCallBacks ); - CFRelease(tabArray[0]); - - CTParagraphStyleSetting altSettings[] = - { - { kCTParagraphStyleSpecifierLineSpacing, sizeof(CGFloat), &lineSpacing}, - { kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &alignment}, - { kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(CGFloat), &firstLineHeadIndent}, - { kCTParagraphStyleSpecifierHeadIndent, sizeof(CGFloat), &headIndent}, - { kCTParagraphStyleSpecifierTabStops, sizeof(CFArrayRef), &tabStops}, - { kCTParagraphStyleSpecifierParagraphSpacing, sizeof(CGFloat), ¶graphSpacing}, - { kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(CGFloat), ¶graphSpacingBefore} - }; - - CTParagraphStyleRef style; - style = CTParagraphStyleCreate( altSettings, sizeof(altSettings) / sizeof(CTParagraphStyleSetting) ); - - if ( style == NULL ) - { - NSLog(@"*** Unable To Create CTParagraphStyle in apply paragraph formatting" ); - return nil; - } - - return [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)style,(NSString*) kCTParagraphStyleAttributeName, nil]; + CTTextAlignment alignment = kCTLeftTextAlignment; + + CTTextTabRef tabArray[] = { CTTextTabCreate(0, firstTabStop, NULL) }; + + CFArrayRef tabStops = CFArrayCreate( kCFAllocatorDefault, (const void**) tabArray, 1, &kCFTypeArrayCallBacks ); + CFRelease(tabArray[0]); + + CTParagraphStyleSetting altSettings[] = + { + { kCTParagraphStyleSpecifierLineSpacing, sizeof(CGFloat), &lineSpacing}, + { kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &alignment}, + { kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(CGFloat), &firstLineHeadIndent}, + { kCTParagraphStyleSpecifierHeadIndent, sizeof(CGFloat), &headIndent}, + { kCTParagraphStyleSpecifierTabStops, sizeof(CFArrayRef), &tabStops}, + { kCTParagraphStyleSpecifierParagraphSpacing, sizeof(CGFloat), ¶graphSpacing}, + { kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(CGFloat), ¶graphSpacingBefore} + }; + + CTParagraphStyleRef style; + style = CTParagraphStyleCreate( altSettings, sizeof(altSettings) / sizeof(CTParagraphStyleSetting) ); + + if ( style == NULL ) + { + NSLog(@"*** Unable To Create CTParagraphStyle in apply paragraph formatting" ); + return nil; + } + + return [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)style,(NSString*) kCTParagraphStyleAttributeName, nil]; #endif } - (UIFont *)topFont { - if (nil == _topFont) { - return self.paragraphFont; - } else { - return _topFont; - } + if (nil == _topFont) { + return self.paragraphFont; + } else { + return _topFont; + } } - (id)keyForFontWithName:(NSString *)fontName pointSize:(CGFloat)pointSize { - return [fontName stringByAppendingFormat:@"%f", pointSize]; + return [fontName stringByAppendingFormat:@"%f", pointSize]; } - (CTFontRef)fontRefForFontWithName:(NSString *)fontName pointSize:(CGFloat)pointSize { - id key = [self keyForFontWithName:fontName pointSize:pointSize]; - NSValue* value = _fontCache[key]; - if (nil == value) { - CTFontRef fontRef = CTFontCreateWithName((__bridge CFStringRef)fontName, pointSize, nil); - value = [NSValue valueWithPointer:fontRef]; - _fontCache[key] = value; - } - return [value pointerValue]; + id key = [self keyForFontWithName:fontName pointSize:pointSize]; + NSValue* value = _fontCache[key]; + if (nil == value) { + CTFontRef fontRef = CTFontCreateWithName((__bridge CFStringRef)fontName, pointSize, nil); + value = [NSValue valueWithPointer:fontRef]; + _fontCache[key] = value; + } + return [value pointerValue]; } - (NSDictionary *)attributesForFontWithName:(NSString *)fontName { @@ -221,155 +222,158 @@ - (NSDictionary *)attributesForFont:(UIFont *)font { } - (void)recurseOnString:(NSString *)string withFont:(UIFont *)font { - NSAttributedStringMarkdownParser* recursiveParser = [self copy]; - recursiveParser->_topFont = font; - [_accum appendAttributedString:[recursiveParser attributedStringFromMarkdownString:string]]; - - // Adjust the recursive parser's links so that they are offset correctly. - for (NSValue* rangeValue in recursiveParser.links) { - NSRange range = [rangeValue rangeValue]; - range.location += _accum.length; - [_links addObject:[NSValue valueWithRange:range]]; - } + NSAttributedStringMarkdownParser* recursiveParser = [self copy]; + recursiveParser->_topFont = font; + [_accum appendAttributedString:[recursiveParser attributedStringFromMarkdownString:string]]; + + // Adjust the recursive parser's links so that they are offset correctly. + for (NSValue* rangeValue in recursiveParser.links) { + NSRange range = [rangeValue rangeValue]; + range.location += _accum.length; + [_links addObject:[NSValue valueWithRange:range]]; + } } - (void)consumeToken:(int)token text:(char*)text { - NSString* textAsString = [[NSString alloc] initWithCString:text encoding:NSUTF8StringEncoding]; - - NSMutableDictionary* attributes = [NSMutableDictionary dictionary]; - [attributes addEntriesFromDictionary:[self attributesForFont:self.topFont]]; - [attributes setValue:[NSNumber numberWithBool:_allowDefaultColor] forKey:(NSString *)kCTForegroundColorFromContextAttributeName]; - - switch (token) { - case MARKDOWNEM: { // * * - textAsString = [textAsString substringWithRange:NSMakeRange(1, textAsString.length - 2)]; - [attributes addEntriesFromDictionary:[self attributesForFontWithName:self.italicFontName]]; - break; - } - case MARKDOWNSTRONG: { // ** ** - textAsString = [textAsString substringWithRange:NSMakeRange(2, textAsString.length - 4)]; - [attributes addEntriesFromDictionary:[self attributesForFontWithName:self.boldFontName]]; - break; - } - case MARKDOWNSTRONGEM: { // *** *** - textAsString = [textAsString substringWithRange:NSMakeRange(3, textAsString.length - 6)]; - [attributes addEntriesFromDictionary:[self attributesForFontWithName:self.boldItalicFontName]]; - break; - } - case MARKDOWNSTRIKETHROUGH: { // ~~ ~~ - textAsString = [textAsString substringWithRange:NSMakeRange(2, textAsString.length - 4)]; - [attributes addEntriesFromDictionary:@{NSStrikethroughStyleAttributeName : @(NSUnderlineStyleSingle)}]; - break; - } - case MARKDOWNHEADER: { // #### - NSRange rangeOfNonHash = [textAsString rangeOfCharacterFromSet:[[NSCharacterSet characterSetWithCharactersInString:@"#"] invertedSet]]; - if (rangeOfNonHash.length > 0) { - textAsString = [[textAsString substringFromIndex:rangeOfNonHash.location] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - - NSAttributedStringMarkdownParserHeader header = rangeOfNonHash.location - 1; - [self recurseOnString:textAsString withFont:[self fontForHeader:header]]; - - // We already appended the recursive parser's results in recurseOnString. - textAsString = nil; - } - break; - } - case MARKDOWNMULTILINEHEADER: { - NSArray* components = [textAsString componentsSeparatedByString:@"\n"]; - textAsString = [components objectAtIndex:0]; - UIFont* font = nil; - if ([[components objectAtIndex:1] rangeOfString:@"="].length > 0) { - font = [self fontForHeader:NSAttributedStringMarkdownParserHeader1]; - } else if ([[components objectAtIndex:1] rangeOfString:@"-"].length > 0) { - font = [self fontForHeader:NSAttributedStringMarkdownParserHeader2]; - } - - [self recurseOnString:textAsString withFont:font]; - - // We already appended the recursive parser's results in recurseOnString. - textAsString = nil; - break; - } - case MARKDOWNPARAGRAPH: { - textAsString = @"\n\n"; - - if (_bulletStarts.count > 0) { - // Treat nested bullet points as flat ones... - - // Finish off the previous dash and start a new one. - NSInteger lastBulletStart = [[_bulletStarts lastObject] intValue]; - [_bulletStarts removeLastObject]; - - [_accum addAttributes:[self paragraphStyle] - range:NSMakeRange(lastBulletStart, _accum.length - lastBulletStart)]; - } - break; - } - case MARKDOWNBULLETSTART: { - NSInteger numberOfDashes = [textAsString rangeOfString:@" "].location; - if (_bulletStarts.count > 0 && _bulletStarts.count <= numberOfDashes) { - // Treat nested bullet points as flat ones... - - // Finish off the previous dash and start a new one. - NSInteger lastBulletStart = [[_bulletStarts lastObject] intValue]; - [_bulletStarts removeLastObject]; - - [_accum addAttributes:[self paragraphStyle] - range:NSMakeRange(lastBulletStart, _accum.length - lastBulletStart)]; - } - - [_bulletStarts addObject:[NSNumber numberWithInt:_accum.length]]; - textAsString = @"•\t"; - break; - } - case MARKDOWNNEWLINE: { - textAsString = @""; - break; + NSString* textAsString = [[NSString alloc] initWithCString:text encoding:NSUTF8StringEncoding]; + + NSMutableDictionary* attributes = [NSMutableDictionary dictionary]; + [attributes addEntriesFromDictionary:[self attributesForFont:self.topFont]]; + + if (_foregroundColor != nil) { + [attributes setValue:_foregroundColor forKey:(NSString *)kCTForegroundColorAttributeName]; } - case MARKDOWNURL: { - NSAttributedStringMarkdownLink* link = [[NSAttributedStringMarkdownLink alloc] init]; - link.url = [NSURL URLWithString:textAsString]; - link.range = NSMakeRange(_accum.length, textAsString.length); - [_links addObject:link]; - break; + + switch (token) { + case MARKDOWNEM: { // * * + textAsString = [textAsString substringWithRange:NSMakeRange(1, textAsString.length - 2)]; + [attributes addEntriesFromDictionary:[self attributesForFontWithName:self.italicFontName]]; + break; + } + case MARKDOWNSTRONG: { // ** ** + textAsString = [textAsString substringWithRange:NSMakeRange(2, textAsString.length - 4)]; + [attributes addEntriesFromDictionary:[self attributesForFontWithName:self.boldFontName]]; + break; + } + case MARKDOWNSTRONGEM: { // *** *** + textAsString = [textAsString substringWithRange:NSMakeRange(3, textAsString.length - 6)]; + [attributes addEntriesFromDictionary:[self attributesForFontWithName:self.boldItalicFontName]]; + break; + } + case MARKDOWNSTRIKETHROUGH: { // ~~ ~~ + textAsString = [textAsString substringWithRange:NSMakeRange(2, textAsString.length - 4)]; + [attributes addEntriesFromDictionary:@{NSStrikethroughStyleAttributeName : @(NSUnderlineStyleSingle)}]; + break; + } + case MARKDOWNHEADER: { // #### + NSRange rangeOfNonHash = [textAsString rangeOfCharacterFromSet:[[NSCharacterSet characterSetWithCharactersInString:@"#"] invertedSet]]; + if (rangeOfNonHash.length > 0) { + textAsString = [[textAsString substringFromIndex:rangeOfNonHash.location] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + NSAttributedStringMarkdownParserHeader header = rangeOfNonHash.location - 1; + [self recurseOnString:textAsString withFont:[self fontForHeader:header]]; + + // We already appended the recursive parser's results in recurseOnString. + textAsString = nil; + } + break; + } + case MARKDOWNMULTILINEHEADER: { + NSArray* components = [textAsString componentsSeparatedByString:@"\n"]; + textAsString = [components objectAtIndex:0]; + UIFont* font = nil; + if ([[components objectAtIndex:1] rangeOfString:@"="].length > 0) { + font = [self fontForHeader:NSAttributedStringMarkdownParserHeader1]; + } else if ([[components objectAtIndex:1] rangeOfString:@"-"].length > 0) { + font = [self fontForHeader:NSAttributedStringMarkdownParserHeader2]; + } + + [self recurseOnString:textAsString withFont:font]; + + // We already appended the recursive parser's results in recurseOnString. + textAsString = nil; + break; + } + case MARKDOWNPARAGRAPH: { + textAsString = @"\n\n"; + + if (_bulletStarts.count > 0) { + // Treat nested bullet points as flat ones... + + // Finish off the previous dash and start a new one. + NSInteger lastBulletStart = [[_bulletStarts lastObject] intValue]; + [_bulletStarts removeLastObject]; + + [_accum addAttributes:[self paragraphStyle] + range:NSMakeRange(lastBulletStart, _accum.length - lastBulletStart)]; + } + break; + } + case MARKDOWNBULLETSTART: { + NSInteger numberOfDashes = [textAsString rangeOfString:@" "].location; + if (_bulletStarts.count > 0 && _bulletStarts.count <= numberOfDashes) { + // Treat nested bullet points as flat ones... + + // Finish off the previous dash and start a new one. + NSInteger lastBulletStart = [[_bulletStarts lastObject] intValue]; + [_bulletStarts removeLastObject]; + + [_accum addAttributes:[self paragraphStyle] + range:NSMakeRange(lastBulletStart, _accum.length - lastBulletStart)]; + } + + [_bulletStarts addObject:[NSNumber numberWithInt:_accum.length]]; + textAsString = @"•\t"; + break; + } + case MARKDOWNNEWLINE: { + textAsString = @""; + break; + } + case MARKDOWNURL: { + NSAttributedStringMarkdownLink* link = [[NSAttributedStringMarkdownLink alloc] init]; + link.url = [NSURL URLWithString:textAsString]; + link.range = NSMakeRange(_accum.length, textAsString.length); + [_links addObject:link]; + break; + } + case MARKDOWNHREF: { // [Title] (url "tooltip") + NSTextCheckingResult *result = [hrefRegex() firstMatchInString:textAsString options:0 range:NSMakeRange(0, textAsString.length)]; + + NSRange linkTitleRange = [result rangeAtIndex:1]; + NSRange linkURLRange = [result rangeAtIndex:2]; + NSRange tooltipRange = [result rangeAtIndex:5]; + + if (linkTitleRange.location != NSNotFound && linkURLRange.location != NSNotFound) { + NSAttributedStringMarkdownLink *link = [[NSAttributedStringMarkdownLink alloc] init]; + + link.url = [NSURL URLWithString:[textAsString substringWithRange:linkURLRange]]; + link.range = NSMakeRange(_accum.length, linkTitleRange.length); + + if (tooltipRange.location != NSNotFound) { + link.tooltip = [textAsString substringWithRange:tooltipRange]; + } + + [_links addObject:link]; + textAsString = [textAsString substringWithRange:linkTitleRange]; + } + break; + } + default: { + break; + } } - case MARKDOWNHREF: { // [Title] (url "tooltip") - NSTextCheckingResult *result = [hrefRegex() firstMatchInString:textAsString options:0 range:NSMakeRange(0, textAsString.length)]; - - NSRange linkTitleRange = [result rangeAtIndex:1]; - NSRange linkURLRange = [result rangeAtIndex:2]; - NSRange tooltipRange = [result rangeAtIndex:5]; - - if (linkTitleRange.location != NSNotFound && linkURLRange.location != NSNotFound) { - NSAttributedStringMarkdownLink *link = [[NSAttributedStringMarkdownLink alloc] init]; - - link.url = [NSURL URLWithString:[textAsString substringWithRange:linkURLRange]]; - link.range = NSMakeRange(_accum.length, linkTitleRange.length); - - if (tooltipRange.location != NSNotFound) { - link.tooltip = [textAsString substringWithRange:tooltipRange]; - } - - [_links addObject:link]; - textAsString = [textAsString substringWithRange:linkTitleRange]; - } - break; - } - default: { - break; + + if (textAsString.length > 0) { + NSAttributedString* attributedString = [[NSAttributedString alloc] initWithString:textAsString attributes:attributes]; + [_accum appendAttributedString:attributedString]; } - } - - if (textAsString.length > 0) { - NSAttributedString* attributedString = [[NSAttributedString alloc] initWithString:textAsString attributes:attributes]; - [_accum appendAttributedString:attributedString]; - } } @end int markdownConsume(char* text, int token, yyscan_t scanner) { - NSAttributedStringMarkdownParser* string = (__bridge NSAttributedStringMarkdownParser *)(markdownget_extra(scanner)); - [string consumeToken:token text:text]; - return 0; + NSAttributedStringMarkdownParser* string = (__bridge NSAttributedStringMarkdownParser *)(markdownget_extra(scanner)); + [string consumeToken:token text:text]; + return 0; } From f6eb300c1bcfeb0895a978902177909a2b85f238 Mon Sep 17 00:00:00 2001 From: dzenbot Date: Thu, 5 Dec 2013 23:47:08 -0300 Subject: [PATCH 5/5] Added multiple MARKDOWNHREF links support. Fixes #14. --- src/NSAttributedStringMarkdownParser.m | 48 +++++++++++++++++++------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/NSAttributedStringMarkdownParser.m b/src/NSAttributedStringMarkdownParser.m index d5d1867..edd2fe3 100644 --- a/src/NSAttributedStringMarkdownParser.m +++ b/src/NSAttributedStringMarkdownParser.m @@ -338,25 +338,47 @@ - (void)consumeToken:(int)token text:(char*)text { break; } case MARKDOWNHREF: { // [Title] (url "tooltip") - NSTextCheckingResult *result = [hrefRegex() firstMatchInString:textAsString options:0 range:NSMakeRange(0, textAsString.length)]; - NSRange linkTitleRange = [result rangeAtIndex:1]; - NSRange linkURLRange = [result rangeAtIndex:2]; - NSRange tooltipRange = [result rangeAtIndex:5]; + NSMutableString *mutableString = [NSMutableString new]; - if (linkTitleRange.location != NSNotFound && linkURLRange.location != NSNotFound) { - NSAttributedStringMarkdownLink *link = [[NSAttributedStringMarkdownLink alloc] init]; + NSArray *results = [hrefRegex() matchesInString:textAsString options:0 range:NSMakeRange(0, textAsString.length)]; + + for (int i = 0; i < results.count; i++) + { + NSTextCheckingResult* result = [results objectAtIndex:i]; + NSTextCheckingResult* prevResult = (i-1 >= 0) ? [results objectAtIndex:i-1] : nil; - link.url = [NSURL URLWithString:[textAsString substringWithRange:linkURLRange]]; - link.range = NSMakeRange(_accum.length, linkTitleRange.length); + NSRange linkTitleRange = [result rangeAtIndex:1]; + NSRange linkURLRange = [result rangeAtIndex:2]; + __unused NSRange tooltipRange = [result rangeAtIndex:5]; - if (tooltipRange.location != NSNotFound) { - link.tooltip = [textAsString substringWithRange:tooltipRange]; - } + NSString *linkTitle = [textAsString substringWithRange:linkTitleRange]; - [_links addObject:link]; - textAsString = [textAsString substringWithRange:linkTitleRange]; + if (linkTitleRange.location != NSNotFound && linkURLRange.location != NSNotFound) { + + if (prevResult != nil) { + __unused NSRange prevTitleRange = [prevResult rangeAtIndex:1]; + NSRange prevURLRange = [prevResult rangeAtIndex:2]; + + NSUInteger location = prevURLRange.location+prevURLRange.length+1; + NSUInteger lenght = (linkTitleRange.location-1) - location; + + NSString *text = [textAsString substringWithRange:NSMakeRange(location, lenght)]; + [mutableString appendString:text]; + } + + [mutableString appendString:linkTitle]; + + NSAttributedStringMarkdownLink *link = [[NSAttributedStringMarkdownLink alloc] init]; + link.url = [NSURL URLWithString:[textAsString substringWithRange:linkURLRange]]; + link.range = [mutableString rangeOfString:linkTitle]; + + [_links addObject:link]; + } } + + textAsString = [NSString stringWithString:mutableString]; + break; } default: {