mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-09-03 03:10:47 +00:00
* Fixed license * Update all licenses * Update Dangerfile for new license * Update already updated licenses * Closer… * Closer… * Closer… * Closer… * Closer… * Closer… * Closer… * Closer… * Closer… * Closer… * Closer…
259 lines
13 KiB
Objective-C
259 lines
13 KiB
Objective-C
//
|
|
// ASTextKitCoreTextAdditions.m
|
|
// Texture
|
|
//
|
|
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
|
// This source code is licensed under the BSD-style license found in the
|
|
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
|
|
// grant of patent rights can be found in the PATENTS file in the same directory.
|
|
//
|
|
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
|
|
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
|
|
#import <AsyncDisplayKit/ASTextKitCoreTextAdditions.h>
|
|
|
|
#import <CoreText/CTFont.h>
|
|
#import <CoreText/CTStringAttributes.h>
|
|
|
|
#import <AsyncDisplayKit/ASAssert.h>
|
|
|
|
#pragma mark - Public
|
|
BOOL ASAttributeWithNameIsUnsupportedCoreTextAttribute(NSString *attributeName)
|
|
{
|
|
static NSSet *coreTextAttributes;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
coreTextAttributes = [NSSet setWithObjects:(__bridge id)kCTForegroundColorAttributeName,
|
|
kCTForegroundColorFromContextAttributeName,
|
|
kCTForegroundColorAttributeName,
|
|
kCTStrokeColorAttributeName,
|
|
kCTUnderlineStyleAttributeName,
|
|
kCTVerticalFormsAttributeName,
|
|
kCTRunDelegateAttributeName,
|
|
kCTBaselineClassAttributeName,
|
|
kCTBaselineInfoAttributeName,
|
|
kCTBaselineReferenceInfoAttributeName,
|
|
kCTUnderlineColorAttributeName,
|
|
nil];
|
|
});
|
|
return [coreTextAttributes containsObject:attributeName];
|
|
}
|
|
|
|
NSDictionary *NSAttributedStringAttributesForCoreTextAttributes(NSDictionary *coreTextAttributes)
|
|
{
|
|
NSMutableDictionary *cleanAttributes = [[NSMutableDictionary alloc] initWithCapacity:coreTextAttributes.count];
|
|
|
|
[coreTextAttributes enumerateKeysAndObjectsUsingBlock:^(NSString *coreTextKey, id coreTextValue, BOOL *stop) {
|
|
// The following attributes are not supported on NSAttributedString. Should they become available, we should add them.
|
|
/*
|
|
kCTForegroundColorFromContextAttributeName
|
|
kCTSuperscriptAttributeName
|
|
kCTGlyphInfoAttributeName
|
|
kCTCharacterShapeAttributeName
|
|
kCTLanguageAttributeName
|
|
kCTRunDelegateAttributeName
|
|
kCTBaselineClassAttributeName
|
|
kCTBaselineInfoAttributeName
|
|
kCTBaselineReferenceInfoAttributeName
|
|
kCTWritingDirectionAttributeName
|
|
kCTUnderlineColorAttributeName
|
|
*/
|
|
|
|
// Conversely, the following attributes are not supported on CFAttributedString. Should they become available, we should add them.
|
|
/*
|
|
NSStrikethroughStyleAttributeName
|
|
NSShadowAttributeName
|
|
NSBackgroundColorAttributeName
|
|
*/
|
|
|
|
// kCTFontAttributeName -> NSFontAttributeName
|
|
if ([coreTextKey isEqualToString:(NSString *)kCTFontAttributeName]) {
|
|
CTFontRef coreTextFont = (__bridge CTFontRef)coreTextValue;
|
|
NSString *fontName = (__bridge_transfer NSString *)CTFontCopyPostScriptName(coreTextFont);
|
|
CGFloat fontSize = CTFontGetSize(coreTextFont);
|
|
UIFont *font = [UIFont fontWithName:fontName size:fontSize];
|
|
ASDisplayNodeCAssertNotNil(font, @"unable to load font %@ with size %f", fontName, fontSize);
|
|
if (font == nil) {
|
|
// Gracefully fail if we were unable to load the font.
|
|
font = [UIFont systemFontOfSize:fontSize];
|
|
}
|
|
cleanAttributes[NSFontAttributeName] = font;
|
|
}
|
|
// kCTKernAttributeName -> NSKernAttributeName
|
|
else if ([coreTextKey isEqualToString:(NSString *)kCTKernAttributeName]) {
|
|
cleanAttributes[NSKernAttributeName] = (NSNumber *)coreTextValue;
|
|
}
|
|
// kCTLigatureAttributeName -> NSLigatureAttributeName
|
|
else if ([coreTextKey isEqualToString:(NSString *)kCTLigatureAttributeName]) {
|
|
cleanAttributes[NSLigatureAttributeName] = (NSNumber *)coreTextValue;
|
|
}
|
|
// kCTForegroundColorAttributeName -> NSForegroundColorAttributeName
|
|
else if ([coreTextKey isEqualToString:(NSString *)kCTForegroundColorAttributeName]) {
|
|
cleanAttributes[NSForegroundColorAttributeName] = [UIColor colorWithCGColor:(CGColorRef)coreTextValue];
|
|
}
|
|
// kCTParagraphStyleAttributeName -> NSParagraphStyleAttributeName
|
|
else if ([coreTextKey isEqualToString:(NSString *)kCTParagraphStyleAttributeName] && ![coreTextValue isKindOfClass:[NSParagraphStyle class]]) {
|
|
cleanAttributes[NSParagraphStyleAttributeName] = [NSParagraphStyle paragraphStyleWithCTParagraphStyle:(CTParagraphStyleRef)coreTextValue];
|
|
}
|
|
// kCTStrokeWidthAttributeName -> NSStrokeWidthAttributeName
|
|
else if ([coreTextKey isEqualToString:(NSString *)kCTStrokeWidthAttributeName]) {
|
|
cleanAttributes[NSStrokeWidthAttributeName] = (NSNumber *)coreTextValue;
|
|
}
|
|
// kCTStrokeColorAttributeName -> NSStrokeColorAttributeName
|
|
else if ([coreTextKey isEqualToString:(NSString *)kCTStrokeColorAttributeName]) {
|
|
cleanAttributes[NSStrokeColorAttributeName] = [UIColor colorWithCGColor:(CGColorRef)coreTextValue];
|
|
}
|
|
// kCTUnderlineStyleAttributeName -> NSUnderlineStyleAttributeName
|
|
else if ([coreTextKey isEqualToString:(NSString *)kCTUnderlineStyleAttributeName]) {
|
|
cleanAttributes[NSUnderlineStyleAttributeName] = (NSNumber *)coreTextValue;
|
|
}
|
|
// kCTVerticalFormsAttributeName -> NSVerticalGlyphFormAttributeName
|
|
else if ([coreTextKey isEqualToString:(NSString *)kCTVerticalFormsAttributeName]) {
|
|
BOOL flag = (BOOL)CFBooleanGetValue((CFBooleanRef)coreTextValue);
|
|
cleanAttributes[NSVerticalGlyphFormAttributeName] = @((int)flag); // NSVerticalGlyphFormAttributeName is documented to be an NSNumber with an integer that's either 0 or 1.
|
|
}
|
|
// Don't filter out any internal text attributes
|
|
else if (!ASAttributeWithNameIsUnsupportedCoreTextAttribute(coreTextKey)){
|
|
cleanAttributes[coreTextKey] = coreTextValue;
|
|
}
|
|
}];
|
|
|
|
return cleanAttributes;
|
|
}
|
|
|
|
NSAttributedString *ASCleanseAttributedStringOfCoreTextAttributes(NSAttributedString *dirtyAttributedString)
|
|
{
|
|
if (!dirtyAttributedString)
|
|
return nil;
|
|
|
|
// First see if there are any core text attributes on the string
|
|
__block BOOL containsCoreTextAttributes = NO;
|
|
[dirtyAttributedString enumerateAttributesInRange:NSMakeRange(0, dirtyAttributedString.length)
|
|
options:0
|
|
usingBlock:^(NSDictionary *dirtyAttributes, NSRange range, BOOL *stop) {
|
|
[dirtyAttributes enumerateKeysAndObjectsUsingBlock:^(NSString *coreTextKey, id coreTextValue, BOOL *innerStop) {
|
|
if (ASAttributeWithNameIsUnsupportedCoreTextAttribute(coreTextKey)) {
|
|
containsCoreTextAttributes = YES;
|
|
*innerStop = YES;
|
|
}
|
|
}];
|
|
*stop = containsCoreTextAttributes;
|
|
}];
|
|
if (containsCoreTextAttributes) {
|
|
|
|
NSString *plainString = dirtyAttributedString.string;
|
|
NSMutableAttributedString *cleanAttributedString = [[NSMutableAttributedString alloc] initWithString:plainString];
|
|
|
|
// Iterate over all of the attributes, cleaning them as appropriate and applying them as we go.
|
|
[dirtyAttributedString enumerateAttributesInRange:NSMakeRange(0, plainString.length)
|
|
options:0
|
|
usingBlock:^(NSDictionary *dirtyAttributes, NSRange range, BOOL *stop) {
|
|
[cleanAttributedString addAttributes:NSAttributedStringAttributesForCoreTextAttributes(dirtyAttributes) range:range];
|
|
}];
|
|
|
|
return cleanAttributedString;
|
|
} else {
|
|
return [dirtyAttributedString copy];
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark -
|
|
@implementation NSParagraphStyle (ASTextKitCoreTextAdditions)
|
|
|
|
+ (instancetype)paragraphStyleWithCTParagraphStyle:(CTParagraphStyleRef)coreTextParagraphStyle;
|
|
{
|
|
NSMutableParagraphStyle *newParagraphStyle = [[NSMutableParagraphStyle alloc] init];
|
|
|
|
if (!coreTextParagraphStyle)
|
|
return newParagraphStyle;
|
|
|
|
// The following paragraph style specifiers are not supported on NSParagraphStyle. Should they become available, we should add them.
|
|
/*
|
|
kCTParagraphStyleSpecifierTabStops
|
|
kCTParagraphStyleSpecifierDefaultTabInterval
|
|
kCTParagraphStyleSpecifierMaximumLineSpacing
|
|
kCTParagraphStyleSpecifierMinimumLineSpacing
|
|
kCTParagraphStyleSpecifierLineSpacingAdjustment
|
|
kCTParagraphStyleSpecifierLineBoundsOptions
|
|
*/
|
|
|
|
// Conversely, the following paragraph styles are not supported on CTParagraphStyle. Should they become available, we should add them.
|
|
/*
|
|
hyphenationFactor
|
|
*/
|
|
|
|
// kCTParagraphStyleSpecifierAlignment -> alignment
|
|
CTTextAlignment coreTextAlignment;
|
|
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierAlignment, sizeof(coreTextAlignment), &coreTextAlignment))
|
|
newParagraphStyle.alignment = NSTextAlignmentFromCTTextAlignment(coreTextAlignment);
|
|
|
|
// kCTParagraphStyleSpecifierFirstLineHeadIndent -> firstLineHeadIndent
|
|
CGFloat firstLineHeadIndent;
|
|
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(firstLineHeadIndent), &firstLineHeadIndent))
|
|
newParagraphStyle.firstLineHeadIndent = firstLineHeadIndent;
|
|
|
|
// kCTParagraphStyleSpecifierHeadIndent -> headIndent
|
|
CGFloat headIndent;
|
|
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierHeadIndent, sizeof(headIndent), &headIndent))
|
|
newParagraphStyle.headIndent = headIndent;
|
|
|
|
// kCTParagraphStyleSpecifierTailIndent -> tailIndent
|
|
CGFloat tailIndent;
|
|
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierTailIndent, sizeof(tailIndent), &tailIndent))
|
|
newParagraphStyle.tailIndent = tailIndent;
|
|
|
|
// kCTParagraphStyleSpecifierLineBreakMode -> lineBreakMode
|
|
CTLineBreakMode coreTextLineBreakMode;
|
|
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierLineBreakMode, sizeof(coreTextLineBreakMode), &coreTextLineBreakMode))
|
|
newParagraphStyle.lineBreakMode = (NSLineBreakMode)coreTextLineBreakMode; // They're the same enum.
|
|
|
|
// kCTParagraphStyleSpecifierLineHeightMultiple -> lineHeightMultiple
|
|
CGFloat lineHeightMultiple;
|
|
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(lineHeightMultiple), &lineHeightMultiple))
|
|
newParagraphStyle.lineHeightMultiple = lineHeightMultiple;
|
|
|
|
// kCTParagraphStyleSpecifierMaximumLineHeight -> maximumLineHeight
|
|
CGFloat maximumLineHeight;
|
|
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(maximumLineHeight), &maximumLineHeight))
|
|
newParagraphStyle.maximumLineHeight = maximumLineHeight;
|
|
|
|
// kCTParagraphStyleSpecifierMinimumLineHeight -> minimumLineHeight
|
|
CGFloat minimumLineHeight;
|
|
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(minimumLineHeight), &minimumLineHeight))
|
|
newParagraphStyle.minimumLineHeight = minimumLineHeight;
|
|
|
|
// kCTParagraphStyleSpecifierLineSpacing -> lineSpacing
|
|
// Note that kCTParagraphStyleSpecifierLineSpacing is deprecated and will die soon. We should not be using it.
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
CGFloat lineSpacing;
|
|
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierLineSpacing, sizeof(lineSpacing), &lineSpacing))
|
|
newParagraphStyle.lineSpacing = lineSpacing;
|
|
#pragma clang diagnostic pop
|
|
|
|
// kCTParagraphStyleSpecifierParagraphSpacing -> paragraphSpacing
|
|
CGFloat paragraphSpacing;
|
|
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierParagraphSpacing, sizeof(paragraphSpacing), ¶graphSpacing))
|
|
newParagraphStyle.paragraphSpacing = paragraphSpacing;
|
|
|
|
// kCTParagraphStyleSpecifierParagraphSpacingBefore -> paragraphSpacingBefore
|
|
CGFloat paragraphSpacingBefore;
|
|
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(paragraphSpacingBefore), ¶graphSpacingBefore))
|
|
newParagraphStyle.paragraphSpacingBefore = paragraphSpacingBefore;
|
|
|
|
// kCTParagraphStyleSpecifierBaseWritingDirection -> baseWritingDirection
|
|
CTWritingDirection coreTextBaseWritingDirection;
|
|
if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, kCTParagraphStyleSpecifierBaseWritingDirection, sizeof(coreTextBaseWritingDirection), &coreTextBaseWritingDirection))
|
|
newParagraphStyle.baseWritingDirection = (NSWritingDirection)coreTextBaseWritingDirection; // They're the same enum.
|
|
|
|
return newParagraphStyle;
|
|
}
|
|
|
|
@end
|