Epub阅读器0.0.1
This commit is contained in:
@@ -0,0 +1,698 @@
|
||||
//
|
||||
// DTCoreTextGlyphRun.m
|
||||
// WeRead
|
||||
//
|
||||
// Reverse-engineered from binary analysis
|
||||
// Based on DTCoreText open-source library, customized by WeRead
|
||||
//
|
||||
// This file contains pseudo-code reconstruction of the DTCoreTextGlyphRun
|
||||
// class based on ivar analysis, method signatures, and the open-source DTCoreText library.
|
||||
//
|
||||
|
||||
#import "DTCoreTextGlyphRun.h"
|
||||
#import "DTCoreTextLayoutLine.h"
|
||||
#import <CoreText/CoreText.h>
|
||||
|
||||
#pragma mark - DTTextAttachment Stub
|
||||
|
||||
/**
|
||||
* Stub for DTTextAttachment class.
|
||||
* Represents an embedded object (image, view, etc.) in the text.
|
||||
*/
|
||||
@interface DTTextAttachment : NSObject
|
||||
|
||||
@property (nonatomic, strong) UIImage *image;
|
||||
@property (nonatomic, assign) CGSize displaySize;
|
||||
@property (nonatomic, assign) CGRect frame;
|
||||
@property (nonatomic, copy) NSString *contentType;
|
||||
|
||||
@end
|
||||
|
||||
@implementation DTTextAttachment
|
||||
@end
|
||||
|
||||
#pragma mark - Private Interface
|
||||
|
||||
@interface DTCoreTextGlyphRun () {
|
||||
// Cached glyph data
|
||||
CGGlyph *_glyphs;
|
||||
CGPoint *_positions;
|
||||
CGFloat *_advances;
|
||||
CFIndex _glyphCount;
|
||||
|
||||
// Whether glyph data has been extracted
|
||||
BOOL _glyphsExtracted;
|
||||
|
||||
// Cached metrics
|
||||
CGFloat _cachedAscent;
|
||||
CGFloat _cachedDescent;
|
||||
CGFloat _cachedLeading;
|
||||
CGFloat _cachedWidth;
|
||||
BOOL _metricsCached;
|
||||
|
||||
// Internal lock
|
||||
NSLock *_lock;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - DTCoreTextGlyphRun Implementation
|
||||
|
||||
@implementation DTCoreTextGlyphRun
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)initWithCTRun:(CTRunRef)ctRun
|
||||
frame:(CGRect)frame
|
||||
range:(NSRange)range
|
||||
attributes:(NSDictionary *)attributes
|
||||
index:(NSUInteger)index {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_lock = [[NSLock alloc] init];
|
||||
|
||||
// Store the CTRun with a retain
|
||||
if (ctRun) {
|
||||
_ctRun = (CTRunRef)CFRetain(ctRun);
|
||||
}
|
||||
|
||||
_frame = frame;
|
||||
_stringRange = range;
|
||||
_attributes = [attributes copy];
|
||||
_runIndex = index;
|
||||
|
||||
// Initialize state
|
||||
_glyphsExtracted = NO;
|
||||
_metricsCached = NO;
|
||||
_glyphs = NULL;
|
||||
_positions = NULL;
|
||||
_advances = NULL;
|
||||
_glyphCount = 0;
|
||||
|
||||
// Extract common attributes
|
||||
[self extractAttributes];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (_ctRun) {
|
||||
CFRelease(_ctRun);
|
||||
_ctRun = NULL;
|
||||
}
|
||||
|
||||
// Free allocated glyph data
|
||||
if (_glyphs) {
|
||||
free(_glyphs);
|
||||
_glyphs = NULL;
|
||||
}
|
||||
if (_positions) {
|
||||
free(_positions);
|
||||
_positions = NULL;
|
||||
}
|
||||
if (_advances) {
|
||||
free(_advances);
|
||||
_advances = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Attribute Extraction
|
||||
|
||||
/**
|
||||
* Extracts common attributes from the attributes dictionary.
|
||||
*/
|
||||
- (void)extractAttributes {
|
||||
if (!_attributes) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract font
|
||||
CTFontRef ctFont = (__bridge CTFontRef)_attributes[(__bridge NSString *)kCTFontAttributeName];
|
||||
if (ctFont) {
|
||||
_font = [UIFont fontWithDescriptor:[UIFontDescriptor fontDescriptorWithCTFont:ctFont]
|
||||
size:CTFontGetSize(ctFont)];
|
||||
}
|
||||
|
||||
// Extract text color
|
||||
CGColorRef textColor = (__bridge CGColorRef)_attributes[(__bridge NSString *)kCTForegroundColorAttributeName];
|
||||
if (textColor) {
|
||||
_textColor = [UIColor colorWithCGColor:textColor];
|
||||
}
|
||||
|
||||
// Extract background color
|
||||
CGColorRef bgColor = (__bridge CGColorRef)_attributes[@"DTBackgroundColor"];
|
||||
if (bgColor) {
|
||||
_backgroundColor = [UIColor colorWithCGColor:bgColor];
|
||||
}
|
||||
|
||||
// Extract strikethrough
|
||||
NSNumber *strikethrough = _attributes[(__bridge NSString *)kCTSuperscriptAttributeName];
|
||||
if (strikethrough) {
|
||||
_hasStrikethrough = [strikethrough boolValue];
|
||||
}
|
||||
|
||||
// Extract underline
|
||||
NSNumber *underlineStyle = _attributes[(__bridge NSString *)kCTUnderlineColorAttributeName];
|
||||
if (underlineStyle) {
|
||||
_hasUnderline = YES;
|
||||
_underlineStyle = [underlineStyle integerValue];
|
||||
}
|
||||
|
||||
// Check for attachment
|
||||
_attachment = _attributes[@"DTTextAttachment"];
|
||||
if (_attachment) {
|
||||
_isAttachment = YES;
|
||||
}
|
||||
|
||||
// Extract writing direction
|
||||
NSArray *writingDirection = _attributes[(__bridge NSString *)kCTWritingDirectionAttributeName];
|
||||
if ([writingDirection count] > 0) {
|
||||
_writingDirection = [writingDirection[0] integerValue];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Glyph Extraction
|
||||
|
||||
/**
|
||||
* Extracts glyph data from the CTRun.
|
||||
*
|
||||
* This method extracts:
|
||||
* - Glyph values (CGGlyph)
|
||||
* - Glyph positions (CGPoint)
|
||||
* - Glyph advances (CGFloat)
|
||||
*/
|
||||
- (void)extractGlyphs {
|
||||
if (_glyphsExtracted || !_ctRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
[_lock lock];
|
||||
|
||||
// Get the number of glyphs
|
||||
_glyphCount = CTRunGetGlyphCount(_ctRun);
|
||||
|
||||
if (_glyphCount == 0) {
|
||||
_glyphsExtracted = YES;
|
||||
[_lock unlock];
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate memory for glyph data
|
||||
_glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * _glyphCount);
|
||||
_positions = (CGPoint *)malloc(sizeof(CGPoint) * _glyphCount);
|
||||
_advances = (CGFloat *)malloc(sizeof(CGFloat) * _glyphCount);
|
||||
|
||||
// Extract glyph values
|
||||
CTRunGetGlyphs(_ctRun, CFRangeMake(0, 0), _glyphs);
|
||||
|
||||
// Extract glyph positions
|
||||
CTRunGetPositions(_ctRun, CFRangeMake(0, 0), _positions);
|
||||
|
||||
// Extract glyph advances
|
||||
CTRunGetAdvances(_ctRun, CFRangeMake(0, 0), (CGSize *)_advances);
|
||||
|
||||
_glyphsExtracted = YES;
|
||||
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
#pragma mark - Glyph Access
|
||||
|
||||
- (CGGlyph)glyphAtIndex:(NSUInteger)index {
|
||||
[self extractGlyphs];
|
||||
|
||||
if (index >= _glyphCount || !_glyphs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _glyphs[index];
|
||||
}
|
||||
|
||||
- (CGPoint)positionForGlyphAtIndex:(NSUInteger)index {
|
||||
[self extractGlyphs];
|
||||
|
||||
if (index >= _glyphCount || !_positions) {
|
||||
return CGPointZero;
|
||||
}
|
||||
|
||||
return _positions[index];
|
||||
}
|
||||
|
||||
- (CGFloat)advanceForGlyphAtIndex:(NSUInteger)index {
|
||||
[self extractGlyphs];
|
||||
|
||||
if (index >= _glyphCount || !_advances) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _advances[index];
|
||||
}
|
||||
|
||||
- (CGRect)rectForGlyphAtIndex:(NSUInteger)index {
|
||||
[self extractGlyphs];
|
||||
|
||||
if (index >= _glyphCount) {
|
||||
return CGRectNull;
|
||||
}
|
||||
|
||||
CGPoint position = _positions[index];
|
||||
CGFloat advance = _advances[index];
|
||||
|
||||
// Calculate the rect for this glyph
|
||||
// The position is relative to the run's origin
|
||||
return CGRectMake(
|
||||
_frame.origin.x + position.x,
|
||||
_frame.origin.y,
|
||||
advance,
|
||||
_frame.size.height
|
||||
);
|
||||
}
|
||||
|
||||
#pragma mark - Path Operations
|
||||
|
||||
/**
|
||||
* Creates a CGPath containing all glyphs in this run.
|
||||
*
|
||||
* This method creates a path by transforming each glyph's outline
|
||||
* to its position within the run. This is used for:
|
||||
* - Custom drawing with effects
|
||||
* - Hit testing with complex shapes
|
||||
* - Creating outlines for selection
|
||||
*
|
||||
* Algorithm:
|
||||
* 1. Extract all glyph data
|
||||
* 2. For each glyph:
|
||||
* a. Get the glyph's path from the font
|
||||
* b. Transform to the glyph's position
|
||||
* c. Add to the combined path
|
||||
* 3. Return the combined path
|
||||
*
|
||||
* @return A CGPath containing all glyph outlines
|
||||
*/
|
||||
- (CGPathRef)newPathWithGlyphs {
|
||||
[self extractGlyphs];
|
||||
|
||||
if (!_ctRun || _glyphCount == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create a mutable path for the combined glyphs
|
||||
CGMutablePathRef combinedPath = CGPathCreateMutable();
|
||||
|
||||
// Get the font from attributes
|
||||
CTFontRef font = (__bridge CTFontRef)_attributes[(__bridge NSString *)kCTFontAttributeName];
|
||||
if (!font) {
|
||||
return combinedPath;
|
||||
}
|
||||
|
||||
// Process each glyph
|
||||
for (CFIndex i = 0; i < _glyphCount; i++) {
|
||||
CGGlyph glyph = _glyphs[i];
|
||||
CGPoint position = _positions[i];
|
||||
|
||||
// Get the path for this glyph from the font
|
||||
CGPathRef glyphPath = CTFontCreatePathForGlyph(font, glyph, NULL);
|
||||
|
||||
if (glyphPath) {
|
||||
// Create a transform to position the glyph
|
||||
// The position is relative to the run's origin
|
||||
CGAffineTransform transform = CGAffineTransformMakeTranslation(
|
||||
_frame.origin.x + position.x,
|
||||
_frame.origin.y + position.y
|
||||
);
|
||||
|
||||
// Add the transformed glyph path to the combined path
|
||||
CGPathAddPath(combinedPath, &transform, glyphPath);
|
||||
|
||||
// Release the individual glyph path
|
||||
CGPathRelease(glyphPath);
|
||||
}
|
||||
}
|
||||
|
||||
return combinedPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CGPath for a specific glyph.
|
||||
*/
|
||||
- (CGPathRef)newPathForGlyphAtIndex:(NSUInteger)index {
|
||||
[self extractGlyphs];
|
||||
|
||||
if (!_ctRun || index >= _glyphCount) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the font from attributes
|
||||
CTFontRef font = (__bridge CTFontRef)_attributes[(__bridge NSString *)kCTFontAttributeName];
|
||||
if (!font) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CGGlyph glyph = _glyphs[index];
|
||||
CGPoint position = _positions[index];
|
||||
|
||||
// Get the path for this glyph
|
||||
CGPathRef glyphPath = CTFontCreatePathForGlyph(font, glyph, NULL);
|
||||
|
||||
if (!glyphPath) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create a transform to position the glyph
|
||||
CGAffineTransform transform = CGAffineTransformMakeTranslation(
|
||||
_frame.origin.x + position.x,
|
||||
_frame.origin.y + position.y
|
||||
);
|
||||
|
||||
// Create a new path with the transform applied
|
||||
CGMutablePathRef transformedPath = CGPathCreateMutable();
|
||||
CGPathAddPath(transformedPath, &transform, glyphPath);
|
||||
|
||||
// Release the original glyph path
|
||||
CGPathRelease(glyphPath);
|
||||
|
||||
return transformedPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a bounding path for all glyphs.
|
||||
*/
|
||||
- (CGPathRef)newBoundingPath {
|
||||
[self extractGlyphs];
|
||||
|
||||
if (_glyphCount == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create a simple rectangular path that bounds all glyphs
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
CGPathAddRect(path, NULL, _frame);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#pragma mark - Image Operations
|
||||
|
||||
/**
|
||||
* Returns the image for the glyph at the specified index.
|
||||
*
|
||||
* This is used for custom rendering where glyphs are replaced with images.
|
||||
*/
|
||||
- (UIImage *)imageForGlyphAtIndex:(NSUInteger)index {
|
||||
if (!_glyphImages || index >= [_glyphImages count]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return _glyphImages[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounding rect for the glyph image.
|
||||
*/
|
||||
- (CGRect)imageRectForGlyphAtIndex:(NSUInteger)index {
|
||||
[self extractGlyphs];
|
||||
|
||||
if (index >= _glyphCount) {
|
||||
return CGRectNull;
|
||||
}
|
||||
|
||||
CGPoint position = _positions[index];
|
||||
CGFloat advance = _advances[index];
|
||||
|
||||
// The image rect is centered on the glyph position
|
||||
return CGRectMake(
|
||||
_frame.origin.x + position.x,
|
||||
_frame.origin.y,
|
||||
advance,
|
||||
_frame.size.height
|
||||
);
|
||||
}
|
||||
|
||||
#pragma mark - Hit Testing
|
||||
|
||||
/**
|
||||
* Returns the glyph index at the given point.
|
||||
*
|
||||
* This method checks if the point falls within any glyph's bounding rect.
|
||||
*
|
||||
* @param point The point to test
|
||||
* @return The glyph index, or NSNotFound
|
||||
*/
|
||||
- (NSUInteger)glyphIndexAtPoint:(CGPoint)point {
|
||||
[self extractGlyphs];
|
||||
|
||||
for (NSUInteger i = 0; i < _glyphCount; i++) {
|
||||
CGRect glyphRect = [self rectForGlyphAtIndex:i];
|
||||
|
||||
if (CGRectContainsPoint(glyphRect, point)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string index at the given point.
|
||||
*/
|
||||
- (NSUInteger)stringIndexAtPoint:(CGPoint)point {
|
||||
NSUInteger glyphIndex = [self glyphIndexAtPoint:point];
|
||||
|
||||
if (glyphIndex == NSNotFound) {
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
// Convert glyph index to string index
|
||||
// This assumes a 1:1 mapping between glyphs and characters
|
||||
// For complex scripts, this may need more sophisticated mapping
|
||||
return _stringRange.location + glyphIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rect for a given string index.
|
||||
*/
|
||||
- (CGRect)rectForStringIndex:(NSUInteger)index {
|
||||
if (index < _stringRange.location ||
|
||||
index >= _stringRange.location + _stringRange.length) {
|
||||
return CGRectNull;
|
||||
}
|
||||
|
||||
// Convert string index to glyph index
|
||||
NSUInteger glyphIndex = index - _stringRange.location;
|
||||
|
||||
return [self rectForGlyphAtIndex:glyphIndex];
|
||||
}
|
||||
|
||||
#pragma mark - Drawing
|
||||
|
||||
/**
|
||||
* Draws this run into a CGContext.
|
||||
*
|
||||
* This method draws the glyphs using CTLineDraw or by manually
|
||||
* drawing each glyph at its position.
|
||||
*
|
||||
* @param context The CGContext to draw into
|
||||
*/
|
||||
- (void)drawInContext:(CGContextRef)context {
|
||||
if (!context || !_ctRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save graphics state
|
||||
CGContextSaveGState(context);
|
||||
|
||||
// Draw the run using CoreText
|
||||
CTRunDraw(_ctRun, context, CFRangeMake(0, 0));
|
||||
|
||||
// Restore graphics state
|
||||
CGContextRestoreGState(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws this run with a specific color.
|
||||
*/
|
||||
- (void)drawInContext:(CGContextRef)context withColor:(UIColor *)color {
|
||||
if (!context || !_ctRun || !color) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save graphics state
|
||||
CGContextSaveGState(context);
|
||||
|
||||
// Set the text color
|
||||
CGContextSetFillColorWithColor(context, color.CGColor);
|
||||
|
||||
// Draw the run
|
||||
CTRunDraw(_ctRun, context, CFRangeMake(0, 0));
|
||||
|
||||
// Restore graphics state
|
||||
CGContextRestoreGState(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the attachment (if any) into the context.
|
||||
*/
|
||||
- (void)drawAttachmentInContext:(CGContextRef)context {
|
||||
if (!context || !_attachment || !_attachment.image) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save graphics state
|
||||
CGContextSaveGState(context);
|
||||
|
||||
// Draw the attachment image at the run's frame
|
||||
CGContextDrawImage(context, _frame, _attachment.image.CGImage);
|
||||
|
||||
// Restore graphics state
|
||||
CGContextRestoreGState(context);
|
||||
}
|
||||
|
||||
#pragma mark - Run Comparison
|
||||
|
||||
/**
|
||||
* Compares this run to another run for ordering.
|
||||
*/
|
||||
- (NSComparisonResult)compareToRun:(DTCoreTextGlyphRun *)otherRun {
|
||||
// Compare by string range location
|
||||
if (_stringRange.location < otherRun.stringRange.location) {
|
||||
return NSOrderedAscending;
|
||||
} else if (_stringRange.location > otherRun.stringRange.location) {
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
|
||||
// If same location, compare by run index
|
||||
if (_runIndex < otherRun.runIndex) {
|
||||
return NSOrderedAscending;
|
||||
} else if (_runIndex > otherRun.runIndex) {
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
|
||||
return NSOrderedSame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this run contains the given string index.
|
||||
*/
|
||||
- (BOOL)containsStringIndex:(NSUInteger)index {
|
||||
return index >= _stringRange.location &&
|
||||
index < _stringRange.location + _stringRange.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this run intersects with the given range.
|
||||
*/
|
||||
- (BOOL)intersectsRange:(NSRange)range {
|
||||
NSRange intersection = NSIntersectionRange(_stringRange, range);
|
||||
return intersection.length > 0;
|
||||
}
|
||||
|
||||
#pragma mark - Metrics
|
||||
|
||||
- (void)calculateMetrics {
|
||||
if (_metricsCached || !_ctRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
[_lock lock];
|
||||
|
||||
// Get typographic bounds from CTRun
|
||||
_cachedWidth = CTRunGetTypographicBounds(_ctRun, CFRangeMake(0, 0),
|
||||
&_cachedAscent, &_cachedDescent, &_cachedLeading);
|
||||
|
||||
_metricsCached = YES;
|
||||
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
- (CGFloat)getTypographicBoundsAscent:(CGFloat *)ascent
|
||||
descent:(CGFloat *)descent
|
||||
leading:(CGFloat *)leading {
|
||||
[self calculateMetrics];
|
||||
|
||||
if (ascent) *ascent = _cachedAscent;
|
||||
if (descent) *descent = _cachedDescent;
|
||||
if (leading) *leading = _cachedLeading;
|
||||
|
||||
return _cachedWidth;
|
||||
}
|
||||
|
||||
- (CGRect)bounds {
|
||||
[self calculateMetrics];
|
||||
|
||||
return CGRectMake(0, -_cachedDescent, _cachedWidth, _cachedAscent + _cachedDescent);
|
||||
}
|
||||
|
||||
- (CGFloat)width {
|
||||
[self calculateMetrics];
|
||||
return _cachedWidth;
|
||||
}
|
||||
|
||||
- (CGFloat)height {
|
||||
[self calculateMetrics];
|
||||
return _cachedAscent + _cachedDescent;
|
||||
}
|
||||
|
||||
- (NSUInteger)numberOfGlyphs {
|
||||
[self extractGlyphs];
|
||||
return _glyphCount;
|
||||
}
|
||||
|
||||
- (NSArray<NSNumber *> *)glyphs {
|
||||
[self extractGlyphs];
|
||||
|
||||
NSMutableArray *glyphArray = [NSMutableArray arrayWithCapacity:_glyphCount];
|
||||
for (CFIndex i = 0; i < _glyphCount; i++) {
|
||||
[glyphArray addObject:@(_glyphs[i])];
|
||||
}
|
||||
|
||||
return [glyphArray copy];
|
||||
}
|
||||
|
||||
- (NSArray<NSValue *> *)glyphPositions {
|
||||
[self extractGlyphs];
|
||||
|
||||
NSMutableArray *positionArray = [NSMutableArray arrayWithCapacity:_glyphCount];
|
||||
for (CFIndex i = 0; i < _glyphCount; i++) {
|
||||
[positionArray addObject:[NSValue valueWithCGPoint:_positions[i]]];
|
||||
}
|
||||
|
||||
return [positionArray copy];
|
||||
}
|
||||
|
||||
- (NSArray<NSNumber *> *)glyphAdvances {
|
||||
[self extractGlyphs];
|
||||
|
||||
NSMutableArray *advanceArray = [NSMutableArray arrayWithCapacity:_glyphCount];
|
||||
for (CFIndex i = 0; i < _glyphCount; i++) {
|
||||
[advanceArray addObject:@(_advances[i])];
|
||||
}
|
||||
|
||||
return [advanceArray copy];
|
||||
}
|
||||
|
||||
- (BOOL)isRTL {
|
||||
return _writingDirection == kCTWritingDirectionRightToLeft;
|
||||
}
|
||||
|
||||
- (BOOL)isWhitespace {
|
||||
if (!_ctRun || _glyphCount == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Check if all characters in the range are whitespace
|
||||
// This would need access to the full attributed string
|
||||
// For now, return NO as a conservative default
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isNewline {
|
||||
if (!_ctRun || _glyphCount == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Check if the run contains only newline characters
|
||||
// This would need access to the full attributed string
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user