New file |
| | |
| | | // |
| | | // YYTextUtilities.m |
| | | // YYText <https://github.com/ibireme/YYText> |
| | | // |
| | | // Created by ibireme on 15/4/6. |
| | | // 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 "YYTextUtilities.h" |
| | | #import <Accelerate/Accelerate.h> |
| | | #import "UIView+YYText.h" |
| | | |
| | | NSCharacterSet *YYTextVerticalFormRotateCharacterSet() { |
| | | static NSMutableCharacterSet *set; |
| | | static dispatch_once_t onceToken; |
| | | dispatch_once(&onceToken, ^{ |
| | | set = [NSMutableCharacterSet new]; |
| | | [set addCharactersInRange:NSMakeRange(0x1100, 256)]; // Hangul Jamo |
| | | [set addCharactersInRange:NSMakeRange(0x2460, 160)]; // Enclosed Alphanumerics |
| | | [set addCharactersInRange:NSMakeRange(0x2600, 256)]; // Miscellaneous Symbols |
| | | [set addCharactersInRange:NSMakeRange(0x2700, 192)]; // Dingbats |
| | | [set addCharactersInRange:NSMakeRange(0x2E80, 128)]; // CJK Radicals Supplement |
| | | [set addCharactersInRange:NSMakeRange(0x2F00, 224)]; // Kangxi Radicals |
| | | [set addCharactersInRange:NSMakeRange(0x2FF0, 16)]; // Ideographic Description Characters |
| | | [set addCharactersInRange:NSMakeRange(0x3000, 64)]; // CJK Symbols and Punctuation |
| | | [set removeCharactersInRange:NSMakeRange(0x3008, 10)]; |
| | | [set removeCharactersInRange:NSMakeRange(0x3014, 12)]; |
| | | [set addCharactersInRange:NSMakeRange(0x3040, 96)]; // Hiragana |
| | | [set addCharactersInRange:NSMakeRange(0x30A0, 96)]; // Katakana |
| | | [set addCharactersInRange:NSMakeRange(0x3100, 48)]; // Bopomofo |
| | | [set addCharactersInRange:NSMakeRange(0x3130, 96)]; // Hangul Compatibility Jamo |
| | | [set addCharactersInRange:NSMakeRange(0x3190, 16)]; // Kanbun |
| | | [set addCharactersInRange:NSMakeRange(0x31A0, 32)]; // Bopomofo Extended |
| | | [set addCharactersInRange:NSMakeRange(0x31C0, 48)]; // CJK Strokes |
| | | [set addCharactersInRange:NSMakeRange(0x31F0, 16)]; // Katakana Phonetic Extensions |
| | | [set addCharactersInRange:NSMakeRange(0x3200, 256)]; // Enclosed CJK Letters and Months |
| | | [set addCharactersInRange:NSMakeRange(0x3300, 256)]; // CJK Compatibility |
| | | [set addCharactersInRange:NSMakeRange(0x3400, 2582)]; // CJK Unified Ideographs Extension A |
| | | [set addCharactersInRange:NSMakeRange(0x4E00, 20941)]; // CJK Unified Ideographs |
| | | [set addCharactersInRange:NSMakeRange(0xAC00, 11172)]; // Hangul Syllables |
| | | [set addCharactersInRange:NSMakeRange(0xD7B0, 80)]; // Hangul Jamo Extended-B |
| | | [set addCharactersInString:@""]; // U+F8FF (Private Use Area) |
| | | [set addCharactersInRange:NSMakeRange(0xF900, 512)]; // CJK Compatibility Ideographs |
| | | [set addCharactersInRange:NSMakeRange(0xFE10, 16)]; // Vertical Forms |
| | | [set addCharactersInRange:NSMakeRange(0xFF00, 240)]; // Halfwidth and Fullwidth Forms |
| | | [set addCharactersInRange:NSMakeRange(0x1F200, 256)]; // Enclosed Ideographic Supplement |
| | | [set addCharactersInRange:NSMakeRange(0x1F300, 768)]; // Enclosed Ideographic Supplement |
| | | [set addCharactersInRange:NSMakeRange(0x1F600, 80)]; // Emoticons (Emoji) |
| | | [set addCharactersInRange:NSMakeRange(0x1F680, 128)]; // Transport and Map Symbols |
| | | |
| | | // See http://unicode-table.com/ for more information. |
| | | }); |
| | | return set; |
| | | } |
| | | |
| | | NSCharacterSet *YYTextVerticalFormRotateAndMoveCharacterSet() { |
| | | static NSMutableCharacterSet *set; |
| | | static dispatch_once_t onceToken; |
| | | dispatch_once(&onceToken, ^{ |
| | | set = [NSMutableCharacterSet new]; |
| | | [set addCharactersInString:@",。、."]; |
| | | }); |
| | | return set; |
| | | } |
| | | |
| | | // return 0 when succeed |
| | | static int matrix_invert(__CLPK_integer N, double *matrix) { |
| | | __CLPK_integer error = 0; |
| | | __CLPK_integer pivot_tmp[6 * 6]; |
| | | __CLPK_integer *pivot = pivot_tmp; |
| | | double workspace_tmp[6 * 6]; |
| | | double *workspace = workspace_tmp; |
| | | bool need_free = false; |
| | | |
| | | if (N > 6) { |
| | | need_free = true; |
| | | pivot = malloc(N * N * sizeof(__CLPK_integer)); |
| | | if (!pivot) return -1; |
| | | workspace = malloc(N * sizeof(double)); |
| | | if (!workspace) { |
| | | free(pivot); |
| | | return -1; |
| | | } |
| | | } |
| | | |
| | | dgetrf_(&N, &N, matrix, &N, pivot, &error); |
| | | |
| | | if (error == 0) { |
| | | dgetri_(&N, matrix, &N, pivot, workspace, &N, &error); |
| | | } |
| | | |
| | | if (need_free) { |
| | | free(pivot); |
| | | free(workspace); |
| | | } |
| | | return error; |
| | | } |
| | | |
| | | CGAffineTransform YYTextCGAffineTransformGetFromPoints(CGPoint before[3], CGPoint after[3]) { |
| | | if (before == NULL || after == NULL) return CGAffineTransformIdentity; |
| | | |
| | | CGPoint p1, p2, p3, q1, q2, q3; |
| | | p1 = before[0]; p2 = before[1]; p3 = before[2]; |
| | | q1 = after[0]; q2 = after[1]; q3 = after[2]; |
| | | |
| | | double A[36]; |
| | | A[ 0] = p1.x; A[ 1] = p1.y; A[ 2] = 0; A[ 3] = 0; A[ 4] = 1; A[ 5] = 0; |
| | | A[ 6] = 0; A[ 7] = 0; A[ 8] = p1.x; A[ 9] = p1.y; A[10] = 0; A[11] = 1; |
| | | A[12] = p2.x; A[13] = p2.y; A[14] = 0; A[15] = 0; A[16] = 1; A[17] = 0; |
| | | A[18] = 0; A[19] = 0; A[20] = p2.x; A[21] = p2.y; A[22] = 0; A[23] = 1; |
| | | A[24] = p3.x; A[25] = p3.y; A[26] = 0; A[27] = 0; A[28] = 1; A[29] = 0; |
| | | A[30] = 0; A[31] = 0; A[32] = p3.x; A[33] = p3.y; A[34] = 0; A[35] = 1; |
| | | |
| | | int error = matrix_invert(6, A); |
| | | if (error) return CGAffineTransformIdentity; |
| | | |
| | | double B[6]; |
| | | B[0] = q1.x; B[1] = q1.y; B[2] = q2.x; B[3] = q2.y; B[4] = q3.x; B[5] = q3.y; |
| | | |
| | | double M[6]; |
| | | M[0] = A[ 0] * B[0] + A[ 1] * B[1] + A[ 2] * B[2] + A[ 3] * B[3] + A[ 4] * B[4] + A[ 5] * B[5]; |
| | | M[1] = A[ 6] * B[0] + A[ 7] * B[1] + A[ 8] * B[2] + A[ 9] * B[3] + A[10] * B[4] + A[11] * B[5]; |
| | | M[2] = A[12] * B[0] + A[13] * B[1] + A[14] * B[2] + A[15] * B[3] + A[16] * B[4] + A[17] * B[5]; |
| | | M[3] = A[18] * B[0] + A[19] * B[1] + A[20] * B[2] + A[21] * B[3] + A[22] * B[4] + A[23] * B[5]; |
| | | M[4] = A[24] * B[0] + A[25] * B[1] + A[26] * B[2] + A[27] * B[3] + A[28] * B[4] + A[29] * B[5]; |
| | | M[5] = A[30] * B[0] + A[31] * B[1] + A[32] * B[2] + A[33] * B[3] + A[34] * B[4] + A[35] * B[5]; |
| | | |
| | | CGAffineTransform transform = CGAffineTransformMake(M[0], M[2], M[1], M[3], M[4], M[5]); |
| | | return transform; |
| | | } |
| | | |
| | | CGAffineTransform YYTextCGAffineTransformGetFromViews(UIView *from, UIView *to) { |
| | | if (!from || !to) return CGAffineTransformIdentity; |
| | | |
| | | CGPoint before[3], after[3]; |
| | | before[0] = CGPointMake(0, 0); |
| | | before[1] = CGPointMake(0, 1); |
| | | before[2] = CGPointMake(1, 0); |
| | | after[0] = [from yy_convertPoint:before[0] toViewOrWindow:to]; |
| | | after[1] = [from yy_convertPoint:before[1] toViewOrWindow:to]; |
| | | after[2] = [from yy_convertPoint:before[2] toViewOrWindow:to]; |
| | | |
| | | return YYTextCGAffineTransformGetFromPoints(before, after); |
| | | } |
| | | |
| | | UIViewContentMode YYTextCAGravityToUIViewContentMode(NSString *gravity) { |
| | | static NSDictionary *dic; |
| | | static dispatch_once_t onceToken; |
| | | dispatch_once(&onceToken, ^{ |
| | | dic = @{ kCAGravityCenter:@(UIViewContentModeCenter), |
| | | kCAGravityTop:@(UIViewContentModeTop), |
| | | kCAGravityBottom:@(UIViewContentModeBottom), |
| | | kCAGravityLeft:@(UIViewContentModeLeft), |
| | | kCAGravityRight:@(UIViewContentModeRight), |
| | | kCAGravityTopLeft:@(UIViewContentModeTopLeft), |
| | | kCAGravityTopRight:@(UIViewContentModeTopRight), |
| | | kCAGravityBottomLeft:@(UIViewContentModeBottomLeft), |
| | | kCAGravityBottomRight:@(UIViewContentModeBottomRight), |
| | | kCAGravityResize:@(UIViewContentModeScaleToFill), |
| | | kCAGravityResizeAspect:@(UIViewContentModeScaleAspectFit), |
| | | kCAGravityResizeAspectFill:@(UIViewContentModeScaleAspectFill) }; |
| | | }); |
| | | if (!gravity) return UIViewContentModeScaleToFill; |
| | | return (UIViewContentMode)((NSNumber *)dic[gravity]).integerValue; |
| | | } |
| | | |
| | | NSString *YYTextUIViewContentModeToCAGravity(UIViewContentMode contentMode) { |
| | | switch (contentMode) { |
| | | case UIViewContentModeScaleToFill: return kCAGravityResize; |
| | | case UIViewContentModeScaleAspectFit: return kCAGravityResizeAspect; |
| | | case UIViewContentModeScaleAspectFill: return kCAGravityResizeAspectFill; |
| | | case UIViewContentModeRedraw: return kCAGravityResize; |
| | | case UIViewContentModeCenter: return kCAGravityCenter; |
| | | case UIViewContentModeTop: return kCAGravityTop; |
| | | case UIViewContentModeBottom: return kCAGravityBottom; |
| | | case UIViewContentModeLeft: return kCAGravityLeft; |
| | | case UIViewContentModeRight: return kCAGravityRight; |
| | | case UIViewContentModeTopLeft: return kCAGravityTopLeft; |
| | | case UIViewContentModeTopRight: return kCAGravityTopRight; |
| | | case UIViewContentModeBottomLeft: return kCAGravityBottomLeft; |
| | | case UIViewContentModeBottomRight: return kCAGravityBottomRight; |
| | | default: return kCAGravityResize; |
| | | } |
| | | } |
| | | |
| | | CGRect YYTextCGRectFitWithContentMode(CGRect rect, CGSize size, UIViewContentMode mode) { |
| | | rect = CGRectStandardize(rect); |
| | | size.width = size.width < 0 ? -size.width : size.width; |
| | | size.height = size.height < 0 ? -size.height : size.height; |
| | | CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); |
| | | switch (mode) { |
| | | case UIViewContentModeScaleAspectFit: |
| | | case UIViewContentModeScaleAspectFill: { |
| | | if (rect.size.width < 0.01 || rect.size.height < 0.01 || |
| | | size.width < 0.01 || size.height < 0.01) { |
| | | rect.origin = center; |
| | | rect.size = CGSizeZero; |
| | | } else { |
| | | CGFloat scale; |
| | | if (mode == UIViewContentModeScaleAspectFit) { |
| | | if (size.width / size.height < rect.size.width / rect.size.height) { |
| | | scale = rect.size.height / size.height; |
| | | } else { |
| | | scale = rect.size.width / size.width; |
| | | } |
| | | } else { |
| | | if (size.width / size.height < rect.size.width / rect.size.height) { |
| | | scale = rect.size.width / size.width; |
| | | } else { |
| | | scale = rect.size.height / size.height; |
| | | } |
| | | } |
| | | size.width *= scale; |
| | | size.height *= scale; |
| | | rect.size = size; |
| | | rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5); |
| | | } |
| | | } break; |
| | | case UIViewContentModeCenter: { |
| | | rect.size = size; |
| | | rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5); |
| | | } break; |
| | | case UIViewContentModeTop: { |
| | | rect.origin.x = center.x - size.width * 0.5; |
| | | rect.size = size; |
| | | } break; |
| | | case UIViewContentModeBottom: { |
| | | rect.origin.x = center.x - size.width * 0.5; |
| | | rect.origin.y += rect.size.height - size.height; |
| | | rect.size = size; |
| | | } break; |
| | | case UIViewContentModeLeft: { |
| | | rect.origin.y = center.y - size.height * 0.5; |
| | | rect.size = size; |
| | | } break; |
| | | case UIViewContentModeRight: { |
| | | rect.origin.y = center.y - size.height * 0.5; |
| | | rect.origin.x += rect.size.width - size.width; |
| | | rect.size = size; |
| | | } break; |
| | | case UIViewContentModeTopLeft: { |
| | | rect.size = size; |
| | | } break; |
| | | case UIViewContentModeTopRight: { |
| | | rect.origin.x += rect.size.width - size.width; |
| | | rect.size = size; |
| | | } break; |
| | | case UIViewContentModeBottomLeft: { |
| | | rect.origin.y += rect.size.height - size.height; |
| | | rect.size = size; |
| | | } break; |
| | | case UIViewContentModeBottomRight: { |
| | | rect.origin.x += rect.size.width - size.width; |
| | | rect.origin.y += rect.size.height - size.height; |
| | | rect.size = size; |
| | | } break; |
| | | case UIViewContentModeScaleToFill: |
| | | case UIViewContentModeRedraw: |
| | | default: { |
| | | rect = rect; |
| | | } |
| | | } |
| | | return rect; |
| | | } |
| | | |
| | | CGFloat YYTextScreenScale() { |
| | | static CGFloat scale; |
| | | static dispatch_once_t onceToken; |
| | | dispatch_once(&onceToken, ^{ |
| | | scale = [UIScreen mainScreen].scale; |
| | | }); |
| | | return scale; |
| | | } |
| | | |
| | | CGSize YYTextScreenSize() { |
| | | static CGSize size; |
| | | static dispatch_once_t onceToken; |
| | | dispatch_once(&onceToken, ^{ |
| | | size = [UIScreen mainScreen].bounds.size; |
| | | if (size.height < size.width) { |
| | | CGFloat tmp = size.height; |
| | | size.height = size.width; |
| | | size.width = tmp; |
| | | } |
| | | }); |
| | | return size; |
| | | } |
| | | |
| | | |
| | | BOOL YYTextIsAppExtension() { |
| | | static BOOL isAppExtension = NO; |
| | | static dispatch_once_t onceToken; |
| | | dispatch_once(&onceToken, ^{ |
| | | Class cls = NSClassFromString(@"UIApplication"); |
| | | if(!cls || ![cls respondsToSelector:@selector(sharedApplication)]) isAppExtension = YES; |
| | | if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]) isAppExtension = YES; |
| | | }); |
| | | return isAppExtension; |
| | | } |
| | | |
| | | UIApplication *YYTextSharedApplication() { |
| | | #pragma clang diagnostic push |
| | | #pragma clang diagnostic ignored "-Wundeclared-selector" |
| | | return YYTextIsAppExtension() ? nil : [UIApplication performSelector:@selector(sharedApplication)]; |
| | | #pragma clang diagnostic pop |
| | | } |