Files
react-native-svg/ios/QuartzBookPack/TextDrawing/Drawing-Text.m

175 lines
6.9 KiB
Objective-C

/*
Erica Sadun, http://ericasadun.com
*/
#import "Drawing-Text.h"
#import "Utility.h"
#pragma mark - Drawing
void DrawStringInRect(NSString *string, CGRect rect, UIFont *font, NSTextAlignment alignment, UIColor *color)
{
CGContextRef context = UIGraphicsGetCurrentContext();
if (context == NULL) return;
NSRange range = NSMakeRange(0, string.length);
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
style.alignment = alignment;
style.lineBreakMode = NSLineBreakByWordWrapping;
attributes[NSFontAttributeName] = font;
attributes[NSForegroundColorAttributeName] = color;
attributes[NSParagraphStyleAttributeName] = style;
[attributedString addAttributes:attributes range:range];
CGRect destRect = [string boundingRectWithSize:CGSizeMake(rect.size.width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
CGRect outputRect = RectCenteredInRect(destRect, rect);
[attributedString drawInRect:outputRect];
}
#pragma mark - Unwrapped
UIFont *FontForUnwrappedString(NSString *string, NSString *fontFace, CGRect rect)
{
CGFloat fontSize = 1;
UIFont *font = [UIFont fontWithName:fontFace size:fontSize];
CGSize destSize = [string sizeWithAttributes:@{NSFontAttributeName:font}];
while ((destSize.width < rect.size.width) && (destSize.height < rect.size.height))
{
fontSize++;
UIFont *proposedFont = [UIFont fontWithName:fontFace size:fontSize];
destSize = [string sizeWithAttributes:@{NSFontAttributeName:proposedFont}];
if ((destSize.height > rect.size.height) || (destSize.width > rect.size.width))
return font;
font = proposedFont;
}
return font;
}
void DrawUnwrappedStringInRect(NSString *string, CGRect rect, NSString *fontFace, NSTextAlignment alignment, UIColor *color)
{
UIFont *font = FontForUnwrappedString(string, fontFace, rect);
DrawStringInRect(string, rect, font, alignment, color);
}
void DrawStringCenteredInRect(NSString *string, UIFont *font, CGRect rect)
{
CGContextRef context = UIGraphicsGetCurrentContext();
if (context == NULL) COMPLAIN_AND_BAIL(@"No context to draw into", nil);
// Calculate string size
CGSize stringSize = [string sizeWithAttributes:@{NSFontAttributeName:font}];
// Find the target rectangle
CGRect target = RectAroundCenter(RectGetCenter(rect), stringSize);
// Draw the string
[string drawInRect:target withAttributes:@{NSFontAttributeName:font}];
}
#pragma mark - Wrapping
UIFont *FontForWrappedString(NSString *string, NSString *fontFace, CGRect rect, CGFloat tolerance)
{
if (rect.size.height < 1.0f) return nil;
CGFloat adjustedWidth = tolerance * rect.size.width;
CGSize measureSize = CGSizeMake(adjustedWidth, CGFLOAT_MAX);
// Initialize the proposed font
CGFloat fontSize = 1;
UIFont *proposedFont = [UIFont fontWithName:fontFace size:fontSize];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
attributes[NSParagraphStyleAttributeName] = paragraphStyle;
attributes[NSFontAttributeName] = proposedFont;
// Measure the target
CGSize targetSize = [string boundingRectWithSize:measureSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size;
// Double until the size is exceeded
while (targetSize.height <= rect.size.height)
{
// Establish a new proposed font
fontSize *= 2;
proposedFont = [UIFont fontWithName:fontFace size:fontSize];
// Measure the target
attributes[NSFontAttributeName] = proposedFont;
targetSize = [string boundingRectWithSize:measureSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size;
// Break when the calculated height is too much
if (targetSize.height > rect.size.height)
break;
}
// Search between the previous and current font sizes
CGFloat minFontSize = fontSize / 2;
CGFloat maxFontSize = fontSize;
while (1)
{
// Get the midpoint between the two
CGFloat midPoint = (minFontSize + (maxFontSize - minFontSize) / 2);
proposedFont = [UIFont fontWithName:fontFace size:midPoint];
attributes[NSFontAttributeName] = proposedFont;
targetSize = [string boundingRectWithSize:measureSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size;
// Look up one font size
UIFont *nextFont = [UIFont fontWithName:fontFace size:midPoint + 1];
attributes[NSFontAttributeName] = nextFont;
CGSize nextTargetSize = [string boundingRectWithSize:measureSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size;;
// Test both fonts
CGFloat tooBig = targetSize.height > rect.size.height;
CGFloat nextIsTooBig = nextTargetSize.height > rect.size.height;
// If the current is sized right but the next is too big, win
if (!tooBig && nextIsTooBig)
return [UIFont fontWithName:fontFace size:midPoint];
// Adjust the search space
if (tooBig)
maxFontSize = midPoint;
else
minFontSize = midPoint;
}
// Should never get here
return [UIFont fontWithName:fontFace size:fontSize / 2];
}
CGSize AttributedStringSize(NSAttributedString *string, CGSize constrainedSize)
{
return [string boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin context:nil].size;
}
void DrawWrappedStringInRect(NSString *string, CGRect rect, NSString *fontFace, NSTextAlignment alignment, UIColor *color)
{
UIFont *font = FontForWrappedString(string, fontFace, rect, 0.95);
NSMutableAttributedString *s = [[NSMutableAttributedString alloc] initWithString:string];
NSRange r = NSMakeRange(0, string.length);
[s addAttribute:NSFontAttributeName value:font range:r];
[s addAttribute:NSForegroundColorAttributeName value:color range:r];
NSMutableParagraphStyle *p = [NSParagraphStyle defaultParagraphStyle].mutableCopy;
p.hyphenationFactor = 0.25f;
p.alignment = alignment;
[s addAttribute:NSParagraphStyleAttributeName value:p range:r];
CGRect stringBounds = SizeMakeRect(AttributedStringSize(s, rect.size));
stringBounds.size.width = fminf(rect.size.width, stringBounds.size.width);
CGRect dest = RectCenteredInRect(stringBounds, rect);
dest.size.height = CGFLOAT_MAX;
[s drawInRect:dest];
}