Permalink
Browse files

Merge branch 'master' of git://github.com/Cocoanetics/DTCoreText

  • Loading branch information...
2 parents 278b87c + ae93360 commit dd61ab005298c09e34f054ccd2a6462cc03c492f @joepasq committed Feb 22, 2012
@@ -10,15 +10,7 @@
static NSCache *_fontCache = nil;
static NSMutableDictionary *_fontOverrides = nil;
-
-static dispatch_semaphore_t fontLock;
-
-@interface DTCoreTextFontDescriptor ()
-
-// generated fonts are cached
-+ (NSCache *)fontCache;
-
-@end
+static dispatch_queue_t _fontQueue;
@implementation DTCoreTextFontDescriptor
{
@@ -35,29 +27,15 @@ @implementation DTCoreTextFontDescriptor
+ (void)initialize
{
- if(self == [DTCoreTextFontDescriptor class]) {
- fontLock = dispatch_semaphore_create(1);
- }
-}
-
-+ (NSCache *)fontCache
-{
- if (!_fontCache)
+ // only this class (and not subclasses) do this
+ if (self == [DTCoreTextFontDescriptor class])
{
_fontCache = [[NSCache alloc] init];
- }
-
- return _fontCache;
-}
-+ (NSMutableDictionary *)fontOverrides
-{
- if (!_fontOverrides)
- {
- _fontOverrides = [[NSMutableDictionary alloc] init];
+ _fontQueue = dispatch_queue_create("DTCoreTextFontDescriptor", 0);
-
- // see if there is an overrides table to preload
+ // init/load of overrides
+ _fontOverrides = [[NSMutableDictionary alloc] init];
NSString *path = [[NSBundle mainBundle] pathForResource:@"DTCoreTextFontOverrides" ofType:@"plist"];
NSArray *fileArray = [NSArray arrayWithContentsOfFile:path];
@@ -80,38 +58,34 @@ + (NSMutableDictionary *)fontOverrides
}
}
}
-
- return _fontOverrides;
}
+ (void)setSmallCapsFontName:(NSString *)fontName forFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic
{
- NSString *key = [NSString stringWithFormat:@"%@-%d-%d-smallcaps", fontFamily, bold, italic];
-
- [[DTCoreTextFontDescriptor fontOverrides] setObject:fontName forKey:key];
+ NSString *key = [NSString stringWithFormat:@"%@-%d-%d-smallcaps", fontFamily, bold, italic];
+ [_fontOverrides setObject:fontName forKey:key];
}
+ (NSString *)smallCapsFontNameforFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic
{
- NSString *key = [NSString stringWithFormat:@"%@-%d-%d-smallcaps", fontFamily, bold, italic];
-
- return [[DTCoreTextFontDescriptor fontOverrides] objectForKey:key];
+ NSString *key = [NSString stringWithFormat:@"%@-%d-%d-smallcaps", fontFamily, bold, italic];
+ return [_fontOverrides objectForKey:key];
}
+ (void)setOverrideFontName:(NSString *)fontName forFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic
{
- NSString *key = [NSString stringWithFormat:@"%@-%d-%d-override", fontFamily, bold, italic];
-
- [[DTCoreTextFontDescriptor fontOverrides] setObject:fontName forKey:key];
+ NSString *key = [NSString stringWithFormat:@"%@-%d-%d-override", fontFamily, bold, italic];
+ [_fontOverrides setObject:fontName forKey:key];
}
+ (NSString *)overrideFontNameforFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic
{
- NSString *key = [NSString stringWithFormat:@"%@-%d-%d-override", fontFamily, bold, italic];
-
- return [[DTCoreTextFontDescriptor fontOverrides] objectForKey:key];
+ NSString *key = [NSString stringWithFormat:@"%@-%d-%d-override", fontFamily, bold, italic];
+ return [_fontOverrides objectForKey:key];
}
+#pragma mark Initializing
+
+ (DTCoreTextFontDescriptor *)fontDescriptorWithFontAttributes:(NSDictionary *)attributes
{
return [[DTCoreTextFontDescriptor alloc] initWithFontAttributes:attributes];
@@ -370,21 +344,16 @@ - (BOOL)supportsNativeSmallCaps
#pragma mark Finding Font
-- (CTFontRef)newMatchingFont
+- (CTFontRef)_findOrMakeMatchingFont
{
- dispatch_semaphore_wait(fontLock, DISPATCH_TIME_FOREVER);
-
NSDictionary *attributes = [self fontAttributes];
- NSCache *fontCache = [DTCoreTextFontDescriptor fontCache];
NSString *cacheKey = [attributes description];
- CTFontRef cachedFont = (__bridge CTFontRef)[fontCache objectForKey:cacheKey];
+ CTFontRef cachedFont = (__bridge_retained CTFontRef)[_fontCache objectForKey:cacheKey];
if (cachedFont)
{
- CFRetain(cachedFont);
- dispatch_semaphore_signal(fontLock);
return cachedFont;
}
@@ -406,7 +375,7 @@ - (CTFontRef)newMatchingFont
{
overrideFontName = [DTCoreTextFontDescriptor overrideFontNameforFontFamily:fontFamily bold:self.boldTrait italic:self.italicTrait];
}
-
+
if (overrideFontName)
{
usedName = overrideFontName;
@@ -462,10 +431,22 @@ - (CTFontRef)newMatchingFont
if (matchingFont)
{
// cache it
- [fontCache setObject:(__bridge id)(matchingFont) forKey:cacheKey]; // if you CFBridgeRelease you get a crash
+ [_fontCache setObject:(__bridge id)(matchingFont) forKey:cacheKey];
}
- dispatch_semaphore_signal(fontLock);
- return matchingFont;
+
+ return matchingFont; // returns a +1 reference
+}
+
+- (CTFontRef)newMatchingFont
+{
+ __block CTFontRef retFont;
+
+ // all calls get queued
+ dispatch_sync(_fontQueue, ^{
+ retFont = [self _findOrMakeMatchingFont];
+ });
+
+ return retFont;
}
// two font descriptors are equal if their attributes has identical hash codes
@@ -102,8 +102,142 @@ - (NSString *)description
return [self.lines description];
}
-- (void)buildLines
+#pragma mark Building the Lines
+
+// returns the head indent for this line, firstLine = YES for the first line in a paragraph, NO for subsequent lines
+- (CGFloat)_calculatedIndentAtIndex:(NSUInteger)index isFirstLineInParagraph:(BOOL)firstLine
+{
+ CTParagraphStyleRef paragraphStyle = (__bridge CTParagraphStyleRef)[_attributedStringFragment attribute:(id)kCTParagraphStyleAttributeName atIndex:index effectiveRange:NULL];
+
+ CGFloat indent = 0;
+
+ if (firstLine)
+ {
+ CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(indent), &indent);
+ }
+ else
+ {
+ CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierHeadIndent, sizeof(indent), &indent);
+ }
+
+ return indent;
+}
+
+
+
+/* Builds the array of lines with the internal typesetter of our framesetter. No need to correct line origins in this case because they are placed correctly in the first place.
+ */
+- (void)_buildLinesWithTypesetter
+{
+ // only build lines if frame is legal
+ if (_frame.size.width<=0)
+ {
+ return;
+ }
+
+ // framesetter keeps internal reference, no need to retain
+ CTTypesetterRef typesetter = CTFramesetterGetTypesetter(_framesetter);
+
+ CFIndex lastIndex = [_attributedStringFragment length];
+
+ CFRange lineRange = CFRangeMake(0, 0);
+ NSMutableArray *typesetLines = [NSMutableArray array];
+
+ CGPoint lineOrigin = _frame.origin;
+
+ DTCoreTextLayoutLine *previousLine = nil;
+
+ // need the paragraph ranges to know if a line is at the beginning of paragraph
+ NSMutableArray *paragraphRanges = [[self paragraphRanges] mutableCopy];
+
+ NSRange currentParagraphRange = [[paragraphRanges objectAtIndex:0] rangeValue];
+
+ do
+ {
+ while (lineRange.location >= (currentParagraphRange.location+currentParagraphRange.length))
+ {
+ // we are outside of this paragraph, so we go to the next
+ [paragraphRanges removeObjectAtIndex:0];
+
+ currentParagraphRange = [[paragraphRanges objectAtIndex:0] rangeValue];
+ }
+
+ BOOL isAtBeginOfParagraph = (currentParagraphRange.location == lineRange.location);
+
+
+ CGFloat offset = [self _calculatedIndentAtIndex:lineRange.location isFirstLineInParagraph:isAtBeginOfParagraph];
+ lineOrigin.x = offset + _frame.origin.x;
+
+ // find how many characters we get into this line
+ lineRange.length = CTTypesetterSuggestLineBreakWithOffset(typesetter, lineRange.location, _frame.size.width - offset, offset);
+
+ // create a line to fit
+ CTLineRef line = CTTypesetterCreateLine(typesetter, lineRange);
+
+ // wrap it
+ DTCoreTextLayoutLine *newLine = [[DTCoreTextLayoutLine alloc] initWithLine:line layoutFrame:self];
+ CFRelease(line);
+
+ // get line height in px if it is specified for this line
+ CGFloat lineHeight = [newLine calculatedLineHeight];
+
+ // get the correct baseline origin
+ if (previousLine)
+ {
+ if (lineHeight==0)
+ {
+ lineHeight = previousLine.descent + newLine.ascent;
+ }
+
+ lineHeight += [previousLine paragraphSpacing:YES] + [newLine calculatedLeading];
+ }
+ else
+ {
+ if (lineHeight>0)
+ {
+ if (lineHeight<newLine.ascent)
+ {
+ // special case, we fake it to look like CoreText
+ lineHeight -= newLine.descent;
+ }
+ }
+ else
+ {
+ lineHeight = newLine.ascent;
+ }
+ }
+
+ lineOrigin.y += lineHeight;
+
+ newLine.baselineOrigin = lineOrigin;
+ [typesetLines addObject:newLine];
+
+ lineRange.location += lineRange.length;
+
+ previousLine = newLine;
+ }
+ while (lineRange.location < lastIndex);
+
+ _lines = typesetLines;
+
+ // at this point we can correct the frame if it is open-ended
+ if ([_lines count] && _frame.size.height == CGFLOAT_OPEN_HEIGHT)
+ {
+ // actual frame is spanned between first and last lines
+ DTCoreTextLayoutLine *lastLine = [_lines lastObject];
+
+ _frame.size.height = ceilf((CGRectGetMaxY(lastLine.frame) - _frame.origin.y + 1.5f));
+ }
+}
+
+
+
+- (void)_buildLines
{
+ // replaced previous approach with manual type setting
+ [self _buildLinesWithTypesetter];
+ return;
+/*
// get lines (don't own it so no release)
CFArrayRef cflines = CTFrameGetLines(_textFrame);
@@ -127,7 +261,8 @@ - (void)buildLines
lineOrigin.y = _frame.size.height - lineOrigin.y + _frame.origin.y;
lineOrigin.x += _frame.origin.x;
- DTCoreTextLayoutLine *newLine = [[DTCoreTextLayoutLine alloc] initWithLine:(__bridge CTLineRef)oneLine layoutFrame:self origin:lineOrigin];
+ DTCoreTextLayoutLine *newLine = [[DTCoreTextLayoutLine alloc] initWithLine:(__bridge CTLineRef)oneLine layoutFrame:self];
+ newLine.baselineOrigin = lineOrigin;
[tmpLines addObject:newLine];
@@ -138,7 +273,7 @@ - (void)buildLines
_lines = tmpLines;
// line origins are wrong on last line of paragraphs
- [self correctLineOrigins];
+ //[self correctLineOrigins];
// --- begin workaround for image squishing bug in iOS < 4.2
@@ -157,13 +292,14 @@ - (void)buildLines
_frame.size.height = ceilf((CGRectGetMaxY(lastLine.frame) - _frame.origin.y + 1.5f));
}
+ */
}
- (NSArray *)lines
{
if (!_lines)
{
- [self buildLines];
+ [self _buildLines];
}
return _lines;
@@ -622,7 +758,7 @@ - (CGRect)frame
{
if (_frame.size.height == CGFLOAT_OPEN_HEIGHT && !_lines)
{
- [self buildLines]; // corrects frame if open-ended
+ [self _buildLines]; // corrects frame if open-ended
}
if (![self.lines count])
@@ -631,15 +767,6 @@ - (CGRect)frame
}
return _frame;
- //
- // // actual frame is spanned between first and last lines
- // DTCoreTextLayoutLine *firstLine = [self.lines objectAtIndex:0];
- // DTCoreTextLayoutLine *lastLine = [self.lines lastObject];
- //
- // CGPoint origin = CGPointMake(roundf(firstLine.frame.origin.x), roundf(firstLine.frame.origin.y));
- // CGSize size = CGSizeMake(_frame.size.width, roundf(CGRectGetMaxY(lastLine.frame) - firstLine.frame.origin.y + 1));
- //
- // return (CGRect){origin, size};
}
- (DTCoreTextLayoutLine *)lineContainingIndex:(NSUInteger)index
Oops, something went wrong.

0 comments on commit dd61ab0

Please sign in to comment.