YYTextUtilities.h 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. //
  2. // YYTextUtilities.h
  3. // YYKit <https://github.com/ibireme/YYKit>
  4. //
  5. // Created by ibireme on 15/4/6.
  6. // Copyright (c) 2015 ibireme.
  7. //
  8. // This source code is licensed under the MIT-style license found in the
  9. // LICENSE file in the root directory of this source tree.
  10. //
  11. #import <UIKit/UIKit.h>
  12. #import <CoreText/CoreText.h>
  13. #if __has_include(<YYKit/YYKit.h>)
  14. #import <YYKit/YYKitMacro.h>
  15. #else
  16. #import "YYKitMacro.h"
  17. #endif
  18. YY_EXTERN_C_BEGIN
  19. NS_ASSUME_NONNULL_BEGIN
  20. /**
  21. Whether the character is 'line break char':
  22. U+000D (\\r or CR)
  23. U+2028 (Unicode line separator)
  24. U+000A (\\n or LF)
  25. U+2029 (Unicode paragraph separator)
  26. @param c A character
  27. @return YES or NO.
  28. */
  29. static inline BOOL YYTextIsLinebreakChar(unichar c) {
  30. switch (c) {
  31. case 0x000D:
  32. case 0x2028:
  33. case 0x000A:
  34. case 0x2029:
  35. return YES;
  36. default:
  37. return NO;
  38. }
  39. }
  40. /**
  41. Whether the string is a 'line break':
  42. U+000D (\\r or CR)
  43. U+2028 (Unicode line separator)
  44. U+000A (\\n or LF)
  45. U+2029 (Unicode paragraph separator)
  46. \\r\\n, in that order (also known as CRLF)
  47. @param str A string
  48. @return YES or NO.
  49. */
  50. static inline BOOL YYTextIsLinebreakString(NSString * _Nullable str) {
  51. if (str.length > 2 || str.length == 0) return NO;
  52. if (str.length == 1) {
  53. unichar c = [str characterAtIndex:0];
  54. return YYTextIsLinebreakChar(c);
  55. } else {
  56. return ([str characterAtIndex:0] == '\r') && ([str characterAtIndex:1] == '\n');
  57. }
  58. }
  59. /**
  60. If the string has a 'line break' suffix, return the 'line break' length.
  61. @param str A string.
  62. @return The length of the tail line break: 0, 1 or 2.
  63. */
  64. static inline NSUInteger YYTextLinebreakTailLength(NSString * _Nullable str) {
  65. if (str.length >= 2) {
  66. unichar c2 = [str characterAtIndex:str.length - 1];
  67. if (YYTextIsLinebreakChar(c2)) {
  68. unichar c1 = [str characterAtIndex:str.length - 2];
  69. if (c1 == '\r' && c2 == '\n') return 2;
  70. else return 1;
  71. } else {
  72. return 0;
  73. }
  74. } else if (str.length == 1) {
  75. return YYTextIsLinebreakChar([str characterAtIndex:0]) ? 1 : 0;
  76. } else {
  77. return 0;
  78. }
  79. }
  80. /**
  81. Convert `UIDataDetectorTypes` to `NSTextCheckingType`.
  82. @param types The `UIDataDetectorTypes` type.
  83. @return The `NSTextCheckingType` type.
  84. */
  85. static inline NSTextCheckingType NSTextCheckingTypeFromUIDataDetectorType(UIDataDetectorTypes types) {
  86. NSTextCheckingType t = 0;
  87. if (types & UIDataDetectorTypePhoneNumber) t |= NSTextCheckingTypePhoneNumber;
  88. if (types & UIDataDetectorTypeLink) t |= NSTextCheckingTypeLink;
  89. if (types & UIDataDetectorTypeAddress) t |= NSTextCheckingTypeAddress;
  90. if (types & UIDataDetectorTypeCalendarEvent) t |= NSTextCheckingTypeDate;
  91. return t;
  92. }
  93. /**
  94. Whether the font is `AppleColorEmoji` font.
  95. @param font A font.
  96. @return YES: the font is Emoji, NO: the font is not Emoji.
  97. */
  98. static inline BOOL UIFontIsEmoji(UIFont *font) {
  99. return [font.fontName isEqualToString:@"AppleColorEmoji"];
  100. }
  101. /**
  102. Whether the font is `AppleColorEmoji` font.
  103. @param font A font.
  104. @return YES: the font is Emoji, NO: the font is not Emoji.
  105. */
  106. static inline BOOL CTFontIsEmoji(CTFontRef font) {
  107. BOOL isEmoji = NO;
  108. CFStringRef name = CTFontCopyPostScriptName(font);
  109. if (name && CFEqual(CFSTR("AppleColorEmoji"), name)) isEmoji = YES;
  110. if (name) CFRelease(name);
  111. return isEmoji;
  112. }
  113. /**
  114. Whether the font is `AppleColorEmoji` font.
  115. @param font A font.
  116. @return YES: the font is Emoji, NO: the font is not Emoji.
  117. */
  118. static inline BOOL CGFontIsEmoji(CGFontRef font) {
  119. BOOL isEmoji = NO;
  120. CFStringRef name = CGFontCopyPostScriptName(font);
  121. if (name && CFEqual(CFSTR("AppleColorEmoji"), name)) isEmoji = YES;
  122. if (name) CFRelease(name);
  123. return isEmoji;
  124. }
  125. /**
  126. Whether the font contains color bitmap glyphs.
  127. @discussion Only `AppleColorEmoji` contains color bitmap glyphs in iOS system fonts.
  128. @param font A font.
  129. @return YES: the font contains color bitmap glyphs, NO: the font has no color bitmap glyph.
  130. */
  131. static inline BOOL CTFontContainsColorBitmapGlyphs(CTFontRef font) {
  132. return (CTFontGetSymbolicTraits(font) & kCTFontTraitColorGlyphs) != 0;
  133. }
  134. /**
  135. Whether the glyph is bitmap.
  136. @param font The glyph's font.
  137. @param glyph The glyph which is created from the specified font.
  138. @return YES: the glyph is bitmap, NO: the glyph is vector.
  139. */
  140. static inline BOOL CGGlyphIsBitmap(CTFontRef font, CGGlyph glyph) {
  141. if (!font && !glyph) return NO;
  142. if (!CTFontContainsColorBitmapGlyphs(font)) return NO;
  143. CGPathRef path = CTFontCreatePathForGlyph(font, glyph, NULL);
  144. if (path) {
  145. CFRelease(path);
  146. return NO;
  147. }
  148. return YES;
  149. }
  150. /**
  151. Get the `AppleColorEmoji` font's ascent with a specified font size.
  152. It may used to create custom emoji.
  153. @param fontSize The specified font size.
  154. @return The font ascent.
  155. */
  156. static inline CGFloat YYEmojiGetAscentWithFontSize(CGFloat fontSize) {
  157. if (fontSize < 16) {
  158. return 1.25 * fontSize;
  159. } else if (16 <= fontSize && fontSize <= 24) {
  160. return 0.5 * fontSize + 12;
  161. } else {
  162. return fontSize;
  163. }
  164. }
  165. /**
  166. Get the `AppleColorEmoji` font's descent with a specified font size.
  167. It may used to create custom emoji.
  168. @param fontSize The specified font size.
  169. @return The font descent.
  170. */
  171. static inline CGFloat YYEmojiGetDescentWithFontSize(CGFloat fontSize) {
  172. if (fontSize < 16) {
  173. return 0.390625 * fontSize;
  174. } else if (16 <= fontSize && fontSize <= 24) {
  175. return 0.15625 * fontSize + 3.75;
  176. } else {
  177. return 0.3125 * fontSize;
  178. }
  179. return 0;
  180. }
  181. /**
  182. Get the `AppleColorEmoji` font's glyph bounding rect with a specified font size.
  183. It may used to create custom emoji.
  184. @param fontSize The specified font size.
  185. @return The font glyph bounding rect.
  186. */
  187. static inline CGRect YYEmojiGetGlyphBoundingRectWithFontSize(CGFloat fontSize) {
  188. CGRect rect;
  189. rect.origin.x = 0.75;
  190. rect.size.width = rect.size.height = YYEmojiGetAscentWithFontSize(fontSize);
  191. if (fontSize < 16) {
  192. rect.origin.y = -0.2525 * fontSize;
  193. } else if (16 <= fontSize && fontSize <= 24) {
  194. rect.origin.y = 0.1225 * fontSize -6;
  195. } else {
  196. rect.origin.y = -0.1275 * fontSize;
  197. }
  198. return rect;
  199. }
  200. /**
  201. Convert a UTF-32 character (equal or larger than 0x10000) to two UTF-16 surrogate pair.
  202. @param char32 Input: a UTF-32 character (equal or larger than 0x10000, not in BMP)
  203. @param char16 Output: two UTF-16 characters.
  204. */
  205. static inline void UTF32CharToUTF16SurrogatePair(UTF32Char char32, UTF16Char char16[_Nonnull 2]) {
  206. char32 -= 0x10000;
  207. char16[0] = (char32 >> 10) + 0xD800;
  208. char16[1] = (char32 & 0x3FF) + 0xDC00;
  209. }
  210. /**
  211. Convert UTF-16 surrogate pair to a UTF-32 character.
  212. @param char16 Two UTF-16 characters.
  213. @return A single UTF-32 character.
  214. */
  215. static inline UTF32Char UTF16SurrogatePairToUTF32Char(UTF16Char char16[_Nonnull 2]) {
  216. return ((char16[0] - 0xD800) << 10) + (char16[1] - 0xDC00) + 0x10000;
  217. }
  218. /**
  219. Get the character set which should rotate in vertical form.
  220. @return The shared character set.
  221. */
  222. NSCharacterSet *YYTextVerticalFormRotateCharacterSet();
  223. /**
  224. Get the character set which should rotate and move in vertical form.
  225. @return The shared character set.
  226. */
  227. NSCharacterSet *YYTextVerticalFormRotateAndMoveCharacterSet();
  228. NS_ASSUME_NONNULL_END
  229. YY_EXTERN_C_END