New file |
| | |
| | | // |
| | | // NSParagraphStyle+YYText.m |
| | | // YYText <https://github.com/ibireme/YYText> |
| | | // |
| | | // Created by ibireme on 14/10/7. |
| | | // Copyright (c) 2015 ibireme. |
| | | // |
| | | // This source code is licensed under the MIT-style license found in the |
| | | // LICENSE file in the root directory of this source tree. |
| | | // |
| | | |
| | | #import "NSParagraphStyle+YYText.h" |
| | | #import "YYTextAttribute.h" |
| | | #import <CoreText/CoreText.h> |
| | | |
| | | // Dummy class for category |
| | | @interface NSParagraphStyle_YYText : NSObject @end |
| | | @implementation NSParagraphStyle_YYText @end |
| | | |
| | | |
| | | @implementation NSParagraphStyle (YYText) |
| | | |
| | | + (NSParagraphStyle *)yy_styleWithCTStyle:(CTParagraphStyleRef)CTStyle { |
| | | if (CTStyle == NULL) return nil; |
| | | |
| | | NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; |
| | | |
| | | #pragma clang diagnostic push |
| | | #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| | | CGFloat lineSpacing; |
| | | if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineSpacing, sizeof(CGFloat), &lineSpacing)) { |
| | | style.lineSpacing = lineSpacing; |
| | | } |
| | | #pragma clang diagnostic pop |
| | | |
| | | CGFloat paragraphSpacing; |
| | | if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierParagraphSpacing, sizeof(CGFloat), ¶graphSpacing)) { |
| | | style.paragraphSpacing = paragraphSpacing; |
| | | } |
| | | |
| | | CTTextAlignment alignment; |
| | | if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &alignment)) { |
| | | style.alignment = NSTextAlignmentFromCTTextAlignment(alignment); |
| | | } |
| | | |
| | | CGFloat firstLineHeadIndent; |
| | | if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(CGFloat), &firstLineHeadIndent)) { |
| | | style.firstLineHeadIndent = firstLineHeadIndent; |
| | | } |
| | | |
| | | CGFloat headIndent; |
| | | if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierHeadIndent, sizeof(CGFloat), &headIndent)) { |
| | | style.headIndent = headIndent; |
| | | } |
| | | |
| | | CGFloat tailIndent; |
| | | if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierTailIndent, sizeof(CGFloat), &tailIndent)) { |
| | | style.tailIndent = tailIndent; |
| | | } |
| | | |
| | | CTLineBreakMode lineBreakMode; |
| | | if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineBreakMode, sizeof(CTLineBreakMode), &lineBreakMode)) { |
| | | style.lineBreakMode = (NSLineBreakMode)lineBreakMode; |
| | | } |
| | | |
| | | CGFloat minimumLineHeight; |
| | | if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(CGFloat), &minimumLineHeight)) { |
| | | style.minimumLineHeight = minimumLineHeight; |
| | | } |
| | | |
| | | CGFloat maximumLineHeight; |
| | | if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(CGFloat), &maximumLineHeight)) { |
| | | style.maximumLineHeight = maximumLineHeight; |
| | | } |
| | | |
| | | CTWritingDirection baseWritingDirection; |
| | | if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierBaseWritingDirection, sizeof(CTWritingDirection), &baseWritingDirection)) { |
| | | style.baseWritingDirection = (NSWritingDirection)baseWritingDirection; |
| | | } |
| | | |
| | | CGFloat lineHeightMultiple; |
| | | if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(CGFloat), &lineHeightMultiple)) { |
| | | style.lineHeightMultiple = lineHeightMultiple; |
| | | } |
| | | |
| | | CGFloat paragraphSpacingBefore; |
| | | if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(CGFloat), ¶graphSpacingBefore)) { |
| | | style.paragraphSpacingBefore = paragraphSpacingBefore; |
| | | } |
| | | |
| | | if ([style respondsToSelector:@selector(tabStops)]) { |
| | | CFArrayRef tabStops; |
| | | if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierTabStops, sizeof(CFArrayRef), &tabStops)) { |
| | | if ([style respondsToSelector:@selector(setTabStops:)]) { |
| | | NSMutableArray *tabs = [NSMutableArray new]; |
| | | [((__bridge NSArray *)(tabStops))enumerateObjectsUsingBlock : ^(id obj, NSUInteger idx, BOOL *stop) { |
| | | CTTextTabRef ctTab = (__bridge CFTypeRef)obj; |
| | | |
| | | NSTextTab *tab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentFromCTTextAlignment(CTTextTabGetAlignment(ctTab)) location:CTTextTabGetLocation(ctTab) options:(__bridge id)CTTextTabGetOptions(ctTab)]; |
| | | [tabs addObject:tab]; |
| | | }]; |
| | | if (tabs.count) { |
| | | style.tabStops = tabs; |
| | | } |
| | | } |
| | | } |
| | | |
| | | CGFloat defaultTabInterval; |
| | | if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierDefaultTabInterval, sizeof(CGFloat), &defaultTabInterval)) { |
| | | if ([style respondsToSelector:@selector(setDefaultTabInterval:)]) { |
| | | style.defaultTabInterval = defaultTabInterval; |
| | | } |
| | | } |
| | | } |
| | | |
| | | return style; |
| | | } |
| | | |
| | | - (CTParagraphStyleRef)yy_CTStyle CF_RETURNS_RETAINED { |
| | | CTParagraphStyleSetting set[kCTParagraphStyleSpecifierCount] = { 0 }; |
| | | int count = 0; |
| | | |
| | | #pragma clang diagnostic push |
| | | #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| | | CGFloat lineSpacing = self.lineSpacing; |
| | | set[count].spec = kCTParagraphStyleSpecifierLineSpacing; |
| | | set[count].valueSize = sizeof(CGFloat); |
| | | set[count].value = &lineSpacing; |
| | | count++; |
| | | #pragma clang diagnostic pop |
| | | |
| | | CGFloat paragraphSpacing = self.paragraphSpacing; |
| | | set[count].spec = kCTParagraphStyleSpecifierParagraphSpacing; |
| | | set[count].valueSize = sizeof(CGFloat); |
| | | set[count].value = ¶graphSpacing; |
| | | count++; |
| | | |
| | | CTTextAlignment alignment = NSTextAlignmentToCTTextAlignment(self.alignment); |
| | | set[count].spec = kCTParagraphStyleSpecifierAlignment; |
| | | set[count].valueSize = sizeof(CTTextAlignment); |
| | | set[count].value = &alignment; |
| | | count++; |
| | | |
| | | CGFloat firstLineHeadIndent = self.firstLineHeadIndent; |
| | | set[count].spec = kCTParagraphStyleSpecifierFirstLineHeadIndent; |
| | | set[count].valueSize = sizeof(CGFloat); |
| | | set[count].value = &firstLineHeadIndent; |
| | | count++; |
| | | |
| | | CGFloat headIndent = self.headIndent; |
| | | set[count].spec = kCTParagraphStyleSpecifierHeadIndent; |
| | | set[count].valueSize = sizeof(CGFloat); |
| | | set[count].value = &headIndent; |
| | | count++; |
| | | |
| | | CGFloat tailIndent = self.tailIndent; |
| | | set[count].spec = kCTParagraphStyleSpecifierTailIndent; |
| | | set[count].valueSize = sizeof(CGFloat); |
| | | set[count].value = &tailIndent; |
| | | count++; |
| | | |
| | | CTLineBreakMode paraLineBreak = (CTLineBreakMode)self.lineBreakMode; |
| | | set[count].spec = kCTParagraphStyleSpecifierLineBreakMode; |
| | | set[count].valueSize = sizeof(CTLineBreakMode); |
| | | set[count].value = ¶LineBreak; |
| | | count++; |
| | | |
| | | CGFloat minimumLineHeight = self.minimumLineHeight; |
| | | set[count].spec = kCTParagraphStyleSpecifierMinimumLineHeight; |
| | | set[count].valueSize = sizeof(CGFloat); |
| | | set[count].value = &minimumLineHeight; |
| | | count++; |
| | | |
| | | CGFloat maximumLineHeight = self.maximumLineHeight; |
| | | set[count].spec = kCTParagraphStyleSpecifierMaximumLineHeight; |
| | | set[count].valueSize = sizeof(CGFloat); |
| | | set[count].value = &maximumLineHeight; |
| | | count++; |
| | | |
| | | CTWritingDirection paraWritingDirection = (CTWritingDirection)self.baseWritingDirection; |
| | | set[count].spec = kCTParagraphStyleSpecifierBaseWritingDirection; |
| | | set[count].valueSize = sizeof(CTWritingDirection); |
| | | set[count].value = ¶WritingDirection; |
| | | count++; |
| | | |
| | | CGFloat lineHeightMultiple = self.lineHeightMultiple; |
| | | set[count].spec = kCTParagraphStyleSpecifierLineHeightMultiple; |
| | | set[count].valueSize = sizeof(CGFloat); |
| | | set[count].value = &lineHeightMultiple; |
| | | count++; |
| | | |
| | | CGFloat paragraphSpacingBefore = self.paragraphSpacingBefore; |
| | | set[count].spec = kCTParagraphStyleSpecifierParagraphSpacingBefore; |
| | | set[count].valueSize = sizeof(CGFloat); |
| | | set[count].value = ¶graphSpacingBefore; |
| | | count++; |
| | | |
| | | if([self respondsToSelector:@selector(tabStops)]) { |
| | | NSMutableArray *tabs = [NSMutableArray array]; |
| | | if ([self respondsToSelector:@selector(tabStops)]) { |
| | | NSInteger numTabs = self.tabStops.count; |
| | | if (numTabs) { |
| | | [self.tabStops enumerateObjectsUsingBlock: ^(NSTextTab *tab, NSUInteger idx, BOOL *stop) { |
| | | CTTextTabRef ctTab = CTTextTabCreate(NSTextAlignmentToCTTextAlignment(tab.alignment), tab.location, (__bridge CFTypeRef)tab.options); |
| | | [tabs addObject:(__bridge id)ctTab]; |
| | | CFRelease(ctTab); |
| | | }]; |
| | | |
| | | CFArrayRef tabStops = (__bridge CFArrayRef)(tabs); |
| | | set[count].spec = kCTParagraphStyleSpecifierTabStops; |
| | | set[count].valueSize = sizeof(CFArrayRef); |
| | | set[count].value = &tabStops; |
| | | count++; |
| | | } |
| | | } |
| | | |
| | | if ([self respondsToSelector:@selector(defaultTabInterval)]) { |
| | | CGFloat defaultTabInterval = self.defaultTabInterval; |
| | | set[count].spec = kCTParagraphStyleSpecifierDefaultTabInterval; |
| | | set[count].valueSize = sizeof(CGFloat); |
| | | set[count].value = &defaultTabInterval; |
| | | count++; |
| | | } |
| | | } |
| | | |
| | | CTParagraphStyleRef style = CTParagraphStyleCreate(set, count); |
| | | return style; |
| | | } |
| | | |
| | | @end |