summaryrefslogtreecommitdiff
path: root/cocoa/font.m
diff options
context:
space:
mode:
Diffstat (limited to 'cocoa/font.m')
-rw-r--r--cocoa/font.m222
1 files changed, 135 insertions, 87 deletions
diff --git a/cocoa/font.m b/cocoa/font.m
index 82e8ca594..15debd603 100644
--- a/cocoa/font.m
+++ b/cocoa/font.m
@@ -31,6 +31,14 @@
static NSLayoutManager *cocoa_prepare_layout_manager( const char *string, size_t length,
const plot_font_style_t *style );
+
+static CGFloat cocoa_layout_width( NSLayoutManager *layout );
+static CGFloat cocoa_layout_width_chars( NSLayoutManager *layout, size_t characters );
+static NSUInteger cocoa_glyph_for_location( NSLayoutManager *layout, CGFloat x );
+static size_t cocoa_bytes_for_characters( const char *string, size_t characters );
+static NSString *cocoa_string_from_utf8_characters( const char *string, size_t characters );
+static NSDictionary *cocoa_font_attributes( const plot_font_style_t *style );
+
static NSTextStorage *cocoa_text_storage = nil;
static NSTextContainer *cocoa_text_container = nil;
static CGFloat cocoa_font_scale_factor = 1.0;
@@ -39,10 +47,8 @@ static bool nsfont_width(const plot_font_style_t *style,
const char *string, size_t length,
int *width)
{
- NSCParameterAssert( NULL != width );
-
NSLayoutManager *layout = cocoa_prepare_layout_manager( string, length, style );
- *width = NULL != layout ? NSWidth( [layout usedRectForTextContainer: cocoa_text_container] ) : 0;
+ *width = cocoa_layout_width( layout );
return true;
}
@@ -51,24 +57,12 @@ static bool nsfont_position_in_string(const plot_font_style_t *style,
int x, size_t *char_offset, int *actual_x)
{
NSLayoutManager *layout = cocoa_prepare_layout_manager( string, length, style );
- CGFloat fraction = 0.0;
- NSUInteger glyphIndex = [layout glyphIndexForPoint: NSMakePoint( x, 0 )
- inTextContainer: cocoa_text_container
- fractionOfDistanceThroughGlyph: &fraction];
- if (fraction > 0) ++glyphIndex;
- NSUInteger chars = [layout characterIndexForGlyphAtIndex: glyphIndex];
+ if (layout == nil) return false;
- size_t offset = 0;
- while (chars-- > 0) {
- uint8_t ch = ((uint8_t *)string)[offset];
-
- if (0xC2 <= ch && ch <= 0xDF) offset += 2;
- else if (0xE0 <= ch && ch <= 0xEF) offset += 3;
- else if (0xF0 <= ch && ch <= 0xF4) offset += 4;
- else offset++;
- }
+ NSUInteger glyphIndex = cocoa_glyph_for_location( layout, x );
+ NSUInteger chars = [layout characterIndexForGlyphAtIndex: glyphIndex];
- *char_offset = offset;
+ *char_offset = cocoa_bytes_for_characters( string, chars );
*actual_x = [layout locationForGlyphAtIndex: glyphIndex].x;
return true;
@@ -78,65 +72,97 @@ static bool nsfont_split(const plot_font_style_t *style,
const char *string, size_t length,
int x, size_t *char_offset, int *actual_x)
{
- nsfont_position_in_string(style, string, length, x, char_offset,
- actual_x);
- if (*char_offset == length) return true;
+ NSLayoutManager *layout = cocoa_prepare_layout_manager( string, length, style );
+ if (layout == nil) return false;
- while ((string[*char_offset] != ' ') && (*char_offset > 0))
- (*char_offset)--;
+ NSUInteger glyphIndex = cocoa_glyph_for_location( layout, x );
+ NSUInteger chars = [layout characterIndexForGlyphAtIndex: glyphIndex];
+
+ if (chars == [cocoa_text_storage length] - 1) {
+ *char_offset = length;
+ *actual_x = cocoa_layout_width( layout );
+ return true;
+ }
- nsfont_position_in_string(style, string, *char_offset, x, char_offset,
- actual_x);
+ chars = [[cocoa_text_storage string] rangeOfString: @" " options: NSBackwardsSearch range: NSMakeRange( 0, chars )].location;
+ if (chars == NSNotFound) {
+ *char_offset = 0;
+ *actual_x = 0;
+ return true;
+ }
+
+ *char_offset = cocoa_bytes_for_characters( string, chars );
+ *actual_x = cocoa_layout_width_chars( layout, chars );
+
return true;
}
+const struct font_functions nsfont = {
+ nsfont_width,
+ nsfont_position_in_string,
+ nsfont_split
+};
+
+#pragma mark -
-static NSString *cocoa_font_family_name( plot_font_generic_family_t family )
+void cocoa_set_font_scale_factor( float newFactor )
{
- switch (family) {
- case PLOT_FONT_FAMILY_SERIF: return @"Times";
- case PLOT_FONT_FAMILY_SANS_SERIF: return @"Helvetica";
- case PLOT_FONT_FAMILY_MONOSPACE: return @"Courier";
- case PLOT_FONT_FAMILY_CURSIVE: return @"Apple Chancery";
- case PLOT_FONT_FAMILY_FANTASY: return @"Marker Felt";
- default: return nil;
- }
+ cocoa_font_scale_factor = newFactor;
}
-static NSFont *cocoa_font_get_nsfont( const plot_font_style_t *style )
+void cocoa_draw_string( int x, int y, const char *bytes, size_t length, const plot_font_style_t *style )
{
- NSFont *font = [NSFont fontWithName: cocoa_font_family_name( style->family )
- size: cocoa_font_scale_factor * (CGFloat)style->size / FONT_SIZE_SCALE];
-
- NSFontTraitMask traits = 0;
- if (style->flags & FONTF_ITALIC || style->flags & FONTF_OBLIQUE) traits |= NSItalicFontMask;
- if (style->flags & FONTF_SMALLCAPS) traits |= NSSmallCapsFontMask;
- if (style->weight > 400) traits |= NSBoldFontMask;
+ NSLayoutManager *layout = cocoa_prepare_layout_manager( bytes, length, style );
- if (0 != traits) {
- NSFontManager *fm = [NSFontManager sharedFontManager];
- font = [fm convertFont: font toHaveTrait: traits];
+ if ([cocoa_text_storage length] > 0) {
+ NSFont *font = [cocoa_text_storage attribute: NSFontAttributeName atIndex: 0 effectiveRange: NULL];
+ CGFloat baseline = [layout defaultBaselineOffsetForFont: font];
+
+ NSRange glyphRange = [layout glyphRangeForTextContainer: cocoa_text_container];
+ [layout drawGlyphsForGlyphRange: glyphRange atPoint: NSMakePoint( x, y - baseline )];
}
+}
+
+
+#pragma mark -
+
+static inline CGFloat cocoa_layout_width( NSLayoutManager *layout )
+{
+ if (layout == nil) return 0.0;
- return font;
+ return NSWidth( [layout usedRectForTextContainer: cocoa_text_container] );
}
-static NSDictionary *cocoa_font_attributes( const plot_font_style_t *style )
+static inline CGFloat cocoa_layout_width_chars( NSLayoutManager *layout, size_t characters )
{
- return [NSDictionary dictionaryWithObjectsAndKeys:
- cocoa_font_get_nsfont( style ), NSFontAttributeName,
- cocoa_convert_colour( style->foreground ), NSForegroundColorAttributeName,
- nil];
+ NSUInteger glyphIndex = [layout glyphIndexForCharacterAtIndex: characters];
+ return [layout locationForGlyphAtIndex: glyphIndex].x;
}
-static NSString *cocoa_string_from_utf8_characters( const char *string, size_t characters )
+static inline NSUInteger cocoa_glyph_for_location( NSLayoutManager *layout, CGFloat x )
{
- if (NULL == string || 0 == characters) return nil;
-
- return [[[NSString alloc] initWithBytes: string length:characters encoding:NSUTF8StringEncoding] autorelease];
+ CGFloat fraction = 0.0;
+ NSUInteger glyphIndex = [layout glyphIndexForPoint: NSMakePoint( x, 0 )
+ inTextContainer: cocoa_text_container
+ fractionOfDistanceThroughGlyph: &fraction];
+ if (fraction > 0) ++glyphIndex;
+ return glyphIndex;
}
+static inline size_t cocoa_bytes_for_characters( const char *string, size_t chars )
+{
+ size_t offset = 0;
+ while (chars-- > 0) {
+ uint8_t ch = ((uint8_t *)string)[offset];
+
+ if (0xC2 <= ch && ch <= 0xDF) offset += 2;
+ else if (0xE0 <= ch && ch <= 0xEF) offset += 3;
+ else if (0xF0 <= ch && ch <= 0xF4) offset += 4;
+ else offset++;
+ }
+ return offset;
+}
static NSLayoutManager *cocoa_prepare_layout_manager( const char *bytes, size_t length,
const plot_font_style_t *style )
@@ -144,55 +170,77 @@ static NSLayoutManager *cocoa_prepare_layout_manager( const char *bytes, size_t
if (NULL == bytes || 0 == length) return nil;
static NSLayoutManager *layout = nil;
-
+
if (nil == layout) {
cocoa_text_container = [[NSTextContainer alloc] initWithContainerSize: NSMakeSize( CGFLOAT_MAX, CGFLOAT_MAX )];
[cocoa_text_container setLineFragmentPadding: 0];
-
+
layout = [[NSLayoutManager alloc] init];
[layout addTextContainer: cocoa_text_container];
-
- cocoa_text_storage = [[NSTextStorage alloc] init];
- [cocoa_text_storage addLayoutManager: layout];
}
- NSString *string = cocoa_string_from_utf8_characters( bytes, length );
- if (nil == string) return nil;
+ NSString *string = [[[NSString alloc] initWithBytes: bytes length:length encoding:NSUTF8StringEncoding] autorelease];
- NSDictionary *attributes = cocoa_font_attributes( style );
- NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString: string attributes: attributes];
- if (![attributedString isEqualToAttributedString: cocoa_text_storage]) {
- [cocoa_text_storage setAttributedString: attributedString];
- [layout ensureLayoutForTextContainer: cocoa_text_container];
+ static NSString *oldString = 0;
+ static plot_font_style_t oldStyle = { 0 };
+
+ const bool styleChanged = memcmp( style, &oldStyle, sizeof oldStyle ) != 0;
+
+ if ([oldString isEqualToString: string] && !styleChanged) {
+ return layout;
}
- [attributedString release];
+
+ [oldString release];
+ oldString = [string copy];
+ oldStyle = *style;
+
+ static NSDictionary *attributes = nil;
+ if (styleChanged || attributes == nil) {
+ [attributes release];
+ attributes = [cocoa_font_attributes( style ) retain];
+ }
+
+ [cocoa_text_storage release];
+ cocoa_text_storage = [[NSTextStorage alloc] initWithString: string attributes: attributes];
+ [cocoa_text_storage addLayoutManager: layout];
+
+ [layout ensureLayoutForTextContainer: cocoa_text_container];
return layout;
}
-void cocoa_set_font_scale_factor( float newFactor )
-{
- cocoa_font_scale_factor = newFactor;
-}
+static NSString * const cocoa_font_families[PLOT_FONT_FAMILY_COUNT] = {
+ [PLOT_FONT_FAMILY_SERIF] = @"Times",
+ [PLOT_FONT_FAMILY_SANS_SERIF] = @"Helvetica",
+ [PLOT_FONT_FAMILY_MONOSPACE] = @"Courier",
+ [PLOT_FONT_FAMILY_CURSIVE] = @"Apple Chancery",
+ [PLOT_FONT_FAMILY_FANTASY] = @"Marker Felt"
+};
-void cocoa_draw_string( int x, int y, const char *bytes, size_t length, const plot_font_style_t *style )
+static inline NSFont *cocoa_font_get_nsfont( const plot_font_style_t *style )
{
- NSLayoutManager *layout = cocoa_prepare_layout_manager( bytes, length, style );
+ NSFont *font = [NSFont fontWithName: cocoa_font_families[style->family]
+ size: cocoa_font_scale_factor * (CGFloat)style->size / FONT_SIZE_SCALE];
- if ([cocoa_text_storage length] > 0) {
- NSFont *font = [cocoa_text_storage attribute: NSFontAttributeName atIndex: 0 effectiveRange: NULL];
- CGFloat baseline = [layout defaultBaselineOffsetForFont: font];
-
- NSRange glyphRange = [layout glyphRangeForTextContainer: cocoa_text_container];
- [layout drawGlyphsForGlyphRange: glyphRange atPoint: NSMakePoint( x, y - baseline )];
+ NSFontTraitMask traits = 0;
+ if (style->flags & FONTF_ITALIC || style->flags & FONTF_OBLIQUE) traits |= NSItalicFontMask;
+ if (style->flags & FONTF_SMALLCAPS) traits |= NSSmallCapsFontMask;
+ if (style->weight > 400) traits |= NSBoldFontMask;
+
+ if (0 != traits) {
+ NSFontManager *fm = [NSFontManager sharedFontManager];
+ font = [fm convertFont: font toHaveTrait: traits];
}
+
+ return font;
}
-const struct font_functions nsfont = {
- nsfont_width,
- nsfont_position_in_string,
- nsfont_split
-};
-
+static inline NSDictionary *cocoa_font_attributes( const plot_font_style_t *style )
+{
+ return [NSDictionary dictionaryWithObjectsAndKeys:
+ cocoa_font_get_nsfont( style ), NSFontAttributeName,
+ cocoa_convert_colour( style->foreground ), NSForegroundColorAttributeName,
+ nil];
+} \ No newline at end of file