//
|
// YYYTextLine.m
|
// YYText <https://github.com/ibireme/YYText>
|
//
|
// Created by ibireme on 15/3/3.
|
// 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 "YYTextLine.h"
|
#import "YYTextUtilities.h"
|
|
|
@implementation YYTextLine {
|
CGFloat _firstGlyphPos; // first glyph position for baseline, typically 0.
|
}
|
|
+ (instancetype)lineWithCTLine:(CTLineRef)CTLine position:(CGPoint)position vertical:(BOOL)isVertical {
|
if (!CTLine) return nil;
|
YYTextLine *line = [self new];
|
line->_position = position;
|
line->_vertical = isVertical;
|
[line setCTLine:CTLine];
|
return line;
|
}
|
|
- (void)dealloc {
|
if (_CTLine) CFRelease(_CTLine);
|
}
|
|
- (void)setCTLine:(_Nonnull CTLineRef)CTLine {
|
if (_CTLine != CTLine) {
|
if (CTLine) CFRetain(CTLine);
|
if (_CTLine) CFRelease(_CTLine);
|
_CTLine = CTLine;
|
if (_CTLine) {
|
_lineWidth = CTLineGetTypographicBounds(_CTLine, &_ascent, &_descent, &_leading);
|
CFRange range = CTLineGetStringRange(_CTLine);
|
_range = NSMakeRange(range.location, range.length);
|
if (CTLineGetGlyphCount(_CTLine) > 0) {
|
CFArrayRef runs = CTLineGetGlyphRuns(_CTLine);
|
CTRunRef run = CFArrayGetValueAtIndex(runs, 0);
|
CGPoint pos;
|
CTRunGetPositions(run, CFRangeMake(0, 1), &pos);
|
_firstGlyphPos = pos.x;
|
} else {
|
_firstGlyphPos = 0;
|
}
|
_trailingWhitespaceWidth = CTLineGetTrailingWhitespaceWidth(_CTLine);
|
} else {
|
_lineWidth = _ascent = _descent = _leading = _firstGlyphPos = _trailingWhitespaceWidth = 0;
|
_range = NSMakeRange(0, 0);
|
}
|
[self reloadBounds];
|
}
|
}
|
|
- (void)setPosition:(CGPoint)position {
|
_position = position;
|
[self reloadBounds];
|
}
|
|
- (void)reloadBounds {
|
if (_vertical) {
|
_bounds = CGRectMake(_position.x - _descent, _position.y, _ascent + _descent, _lineWidth);
|
_bounds.origin.y += _firstGlyphPos;
|
} else {
|
_bounds = CGRectMake(_position.x, _position.y - _ascent, _lineWidth, _ascent + _descent);
|
_bounds.origin.x += _firstGlyphPos;
|
}
|
|
_attachments = nil;
|
_attachmentRanges = nil;
|
_attachmentRects = nil;
|
if (!_CTLine) return;
|
CFArrayRef runs = CTLineGetGlyphRuns(_CTLine);
|
NSUInteger runCount = CFArrayGetCount(runs);
|
if (runCount == 0) return;
|
|
NSMutableArray *attachments = [NSMutableArray new];
|
NSMutableArray *attachmentRanges = [NSMutableArray new];
|
NSMutableArray *attachmentRects = [NSMutableArray new];
|
for (NSUInteger r = 0; r < runCount; r++) {
|
CTRunRef run = CFArrayGetValueAtIndex(runs, r);
|
CFIndex glyphCount = CTRunGetGlyphCount(run);
|
if (glyphCount == 0) continue;
|
NSDictionary *attrs = (id)CTRunGetAttributes(run);
|
YYTextAttachment *attachment = attrs[YYTextAttachmentAttributeName];
|
if (attachment) {
|
CGPoint runPosition = CGPointZero;
|
CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition);
|
|
CGFloat ascent, descent, leading, runWidth;
|
CGRect runTypoBounds;
|
runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, &leading);
|
|
if (_vertical) {
|
YYTEXT_SWAP(runPosition.x, runPosition.y);
|
runPosition.y = _position.y + runPosition.y;
|
runTypoBounds = CGRectMake(_position.x + runPosition.x - descent, runPosition.y , ascent + descent, runWidth);
|
} else {
|
runPosition.x += _position.x;
|
runPosition.y = _position.y - runPosition.y;
|
runTypoBounds = CGRectMake(runPosition.x, runPosition.y - ascent, runWidth, ascent + descent);
|
}
|
|
NSRange runRange = YYTextNSRangeFromCFRange(CTRunGetStringRange(run));
|
[attachments addObject:attachment];
|
[attachmentRanges addObject:[NSValue valueWithRange:runRange]];
|
[attachmentRects addObject:[NSValue valueWithCGRect:runTypoBounds]];
|
}
|
}
|
_attachments = attachments.count ? attachments : nil;
|
_attachmentRanges = attachmentRanges.count ? attachmentRanges : nil;
|
_attachmentRects = attachmentRects.count ? attachmentRects : nil;
|
}
|
|
- (CGSize)size {
|
return _bounds.size;
|
}
|
|
- (CGFloat)width {
|
return CGRectGetWidth(_bounds);
|
}
|
|
- (CGFloat)height {
|
return CGRectGetHeight(_bounds);
|
}
|
|
- (CGFloat)top {
|
return CGRectGetMinY(_bounds);
|
}
|
|
- (CGFloat)bottom {
|
return CGRectGetMaxY(_bounds);
|
}
|
|
- (CGFloat)left {
|
return CGRectGetMinX(_bounds);
|
}
|
|
- (CGFloat)right {
|
return CGRectGetMaxX(_bounds);
|
}
|
|
- (NSString *)description {
|
NSMutableString *desc = @"".mutableCopy;
|
NSRange range = self.range;
|
[desc appendFormat:@"<YYTextLine: %p> row:%zd range:%tu,%tu",self, self.row, range.location, range.length];
|
[desc appendFormat:@" position:%@",NSStringFromCGPoint(self.position)];
|
[desc appendFormat:@" bounds:%@",NSStringFromCGRect(self.bounds)];
|
return desc;
|
}
|
|
@end
|
|
|
@implementation YYTextRunGlyphRange
|
+ (instancetype)rangeWithRange:(NSRange)range drawMode:(YYTextRunGlyphDrawMode)mode {
|
YYTextRunGlyphRange *one = [self new];
|
one.glyphRangeInRun = range;
|
one.drawMode = mode;
|
return one;
|
}
|
@end
|