blob: c48edb169dff44f2e316cda68bca4a7d7319e61f [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/util/text_frame.h"
#include "base/logging.h"
#include "base/mac/foundation_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#pragma mark - FramedLine
@implementation FramedLine {
// Backing object for property of the same name.
id _line;
}
@synthesize stringRange = _stringRange;
@synthesize origin = _origin;
- (instancetype)initWithLine:(CTLineRef)line
stringRange:(NSRange)stringRange
origin:(CGPoint)origin {
if ((self = [super init])) {
DCHECK(line);
// CTLines created by ManualTextFramers all have zero for their string range
// locations, but its length should be equal to |stringRange|.
NSRange lineRange;
if (!base::mac::CFRangeToNSRange(CTLineGetStringRange(line), &lineRange)) {
return nil;
}
DCHECK_EQ(lineRange.length, stringRange.length);
_line = (__bridge id)(line);
_stringRange = stringRange;
_origin = origin;
}
return self;
}
- (NSString*)description {
return [NSString stringWithFormat:@"%@ line:%@, stringRange:%@, origin:%@",
[super description], _line,
NSStringFromRange(_stringRange),
NSStringFromCGPoint(_origin)];
}
- (CFIndex)lineOffsetForStringLocation:(NSUInteger)stringLocation {
if (stringLocation < self.stringRange.location ||
stringLocation >= self.stringRange.location + self.stringRange.length) {
return kCFNotFound;
}
NSRange lineRange;
if (!base::mac::CFRangeToNSRange(CTLineGetStringRange(self.line), &lineRange))
return kCFNotFound;
return lineRange.location + (stringLocation - self.stringRange.location);
}
#pragma mark Accessors
- (CTLineRef)line {
return (__bridge CTLineRef)(_line);
}
@end
#pragma mark - CoreTextTextFrame
@interface CoreTextTextFrame () {
// Backing object for property of the same name.
NSAttributedString* _string;
id _frame;
NSMutableArray* _lines;
}
// The CTFrameRef passed on initializaton.
@property(nonatomic, readonly) CTFrameRef frame;
// Populates |lines| using |frame|.
- (void)createFramedLines;
@end
@implementation CoreTextTextFrame
@synthesize bounds = _bounds;
- (instancetype)initWithString:(NSAttributedString*)string
bounds:(CGRect)bounds
frame:(CTFrameRef)frame {
if ((self = [super self])) {
DCHECK(string.string.length);
_string = string;
_bounds = bounds;
DCHECK(frame);
_frame = (__bridge id)(frame);
}
return self;
}
#pragma mark Accessors
- (NSAttributedString*)string {
return _string;
}
- (NSRange)framedRange {
NSRange range;
CFRange cfRange = CTFrameGetVisibleStringRange(self.frame);
if (base::mac::CFRangeToNSRange(cfRange, &range))
return range;
return NSMakeRange(NSNotFound, 0);
}
- (NSArray*)lines {
if (!_lines)
[self createFramedLines];
return _lines;
}
- (CTFrameRef)frame {
return (__bridge CTFrameRef)(_frame);
}
#pragma mark Private
- (void)createFramedLines {
NSArray* lines = base::mac::CFToNSCast(CTFrameGetLines(self.frame));
CGPoint origins[lines.count];
CTFrameGetLineOrigins(self.frame, CFRangeMake(0, 0), origins);
_lines = [[NSMutableArray alloc] initWithCapacity:lines.count];
for (NSUInteger line_idx = 0; line_idx < lines.count; ++line_idx) {
CTLineRef line = (__bridge CTLineRef)(lines[line_idx]);
NSRange stringRange;
CFRange cfStringRange = CTLineGetStringRange(line);
if (!base::mac::CFRangeToNSRange(cfStringRange, &stringRange))
break;
FramedLine* framedLine =
[[FramedLine alloc] initWithLine:line
stringRange:stringRange
origin:origins[line_idx]];
[_lines addObject:framedLine];
}
}
@end