Created
July 17, 2013 03:23
-
-
Save Shilo/6017433 to your computer and use it in GitHub Desktop.
A category for Sparrow 2.X that allows a bitmap SPTextField to justify align.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SPTextField+Justify.h | |
// Sparrow 2.X | |
// | |
// Created by Shilo White on 7/16/13. | |
// | |
#import "SPTextField.h" | |
#define SPVAlignJustify SPVAlignBottom+1 | |
#define SPHAlignJustify SPHAlignRight+1 | |
#define SPHAlignJustifyLastLineLeft SPHAlignJustify+1 | |
#define SPHAlignJustifyLastLineCenter SPHAlignJustifyLastLineLeft+1 | |
#define SPHAlignJustifyLastLineRight SPHAlignJustifyLastLineCenter+1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SPTextField+Justify.m | |
// Sparrow 2.X | |
// | |
// Created by Shilo White on 7/16/13. | |
// | |
#import "SPTextField+Justify.h" | |
#import "SPBitmapFont.h" | |
#define CHAR_SPACE 32 | |
#define CHAR_TAB 9 | |
#define CHAR_NEWLINE 10 | |
#define CHAR_CARRIAGE_RETURN 13 | |
@interface SPTextField () | |
{ | |
float _fontSize; | |
uint _color; | |
NSString *_text; | |
NSString *_fontName; | |
SPHAlign _hAlign; | |
SPVAlign _vAlign; | |
BOOL _autoScale; | |
SPQuadBatch *_contents; | |
SPRectangle *_textBounds; | |
SPQuad *_hitArea; | |
} | |
@end | |
@implementation SPTextField (Justified) | |
- (void)createRenderedContents | |
{ | |
float width = _hitArea.width; | |
float height = _hitArea.height; | |
float fontSize = _fontSize == SP_NATIVE_FONT_SIZE ? SP_DEFAULT_FONT_SIZE : _fontSize; | |
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_6_0 | |
NSLineBreakMode lbm = NSLineBreakByTruncatingTail; | |
#else | |
UILineBreakMode lbm = UILineBreakModeTailTruncation; | |
#endif | |
CGSize textSize; | |
if (_autoScale) | |
{ | |
CGSize maxSize = CGSizeMake(width, FLT_MAX); | |
fontSize += 1.0f; | |
do | |
{ | |
fontSize -= 1.0f; | |
textSize = [_text sizeWithFont:[UIFont fontWithName:_fontName size:fontSize] | |
constrainedToSize:maxSize lineBreakMode:lbm]; | |
} while (textSize.height > height); | |
} | |
else | |
{ | |
textSize = [_text sizeWithFont:[UIFont fontWithName:_fontName size:fontSize] | |
constrainedToSize:CGSizeMake(width, height) lineBreakMode:lbm]; | |
} | |
float xOffset = 0; | |
if (_hAlign == SPHAlignCenter) xOffset = (width - textSize.width) / 2.0f; | |
else if (_hAlign == SPHAlignRight) xOffset = width - textSize.width; | |
float yOffset = 0; | |
if (_vAlign == SPVAlignCenter) yOffset = (height - textSize.height) / 2.0f; | |
else if (_vAlign == SPVAlignBottom) yOffset = height - textSize.height; | |
if (!_textBounds) _textBounds = [[SPRectangle alloc] init]; | |
[_textBounds setX:xOffset y:yOffset width:textSize.width height:textSize.height]; | |
SPTexture *texture = [[SPTexture alloc] initWithWidth:width height:height generateMipmaps:YES | |
draw:^(CGContextRef context) | |
{ | |
float red = SP_COLOR_PART_RED(_color) / 255.0f; | |
float green = SP_COLOR_PART_GREEN(_color) / 255.0f; | |
float blue = SP_COLOR_PART_BLUE(_color) / 255.0f; | |
CGContextSetRGBFillColor(context, red, green, blue, 1.0f); | |
if (_hAlign >= SPHAlignJustify) NSLog(@"[WARNING] SPTextField SPHAlignJustify is not supported for native font support. (Use bitmap fonts instead)"); | |
if (_vAlign >= SPVAlignJustify) NSLog(@"[WARNING] SPTextField SPVAlignJustify is not supported for native font support. (Use bitmap fonts instead)"); | |
[_text drawInRect:CGRectMake(0, yOffset, width, height) | |
withFont:[UIFont fontWithName:_fontName size:fontSize] | |
lineBreakMode:lbm alignment:(UITextAlignment)(_hAlign == SPHAlignJustify)?SPHAlignLeft:_hAlign]; | |
}]; | |
SPImage *image = [[SPImage alloc] initWithTexture:texture]; | |
[_contents addQuad:image]; | |
} | |
@end | |
@interface SPCharLocation2 : SPPoolObject | |
@property (nonatomic) SPBitmapChar *bitmapChar; | |
@property (nonatomic) float scale; | |
@property (nonatomic) float x; | |
@property (nonatomic) float y; | |
- (id)initWithChar:(SPBitmapChar *)bitmapChar; | |
@end | |
@implementation SPCharLocation2 | |
- (id)initWithChar:(SPBitmapChar *)bitmapChar | |
{ | |
if ((self = [super init])) | |
_bitmapChar = bitmapChar; | |
return self; | |
} | |
SP_IMPLEMENT_MEMORY_POOL(); | |
@end | |
@interface SPBitmapFont () | |
{ | |
float _size; | |
float _lineHeight; | |
} | |
@end | |
@implementation SPBitmapFont (Justify) | |
- (NSMutableArray *)arrangeCharsInAreaWithWidth:(float)width height:(float)height | |
text:(NSString *)text fontSize:(float)size | |
hAlign:(SPHAlign)hAlign vAlign:(SPVAlign)vAlign | |
autoScale:(BOOL)autoScale kerning:(BOOL)kerning | |
{ | |
if (text.length == 0) return [NSMutableArray array]; | |
if (size < 0) size *= -_size; | |
NSMutableArray *lines; | |
NSMutableArray *whitespaces; | |
float scale; | |
float containerWidth; | |
float containerHeight; | |
BOOL finished = NO; | |
BOOL isVAlignJustify = (vAlign == SPVAlignJustify); | |
BOOL isHAlignJustify = (hAlign == SPHAlignJustify || | |
hAlign == SPHAlignJustifyLastLineLeft || | |
hAlign == SPHAlignJustifyLastLineCenter || | |
hAlign == SPHAlignJustifyLastLineRight); | |
while (!finished) | |
{ | |
lines = [NSMutableArray array]; | |
if (isHAlignJustify) | |
whitespaces = [NSMutableArray array]; | |
scale = size / _size; | |
containerWidth = width / scale; | |
containerHeight = height / scale; | |
if (_lineHeight <= containerHeight) | |
{ | |
uint numOfWhiteSpaces = 0; | |
int lastWhiteSpace = -1; | |
int lastCharID = -1; | |
int numChars = text.length; | |
float currentX = 0; | |
float currentY = 0; | |
NSMutableArray *currentLine = [NSMutableArray array]; | |
for (int i=0; i<numChars; i++) | |
{ | |
BOOL lineFull = NO; | |
int charID = [text characterAtIndex:i]; | |
SPBitmapChar *bitmapChar = [self charByID:charID]; | |
if (charID == CHAR_NEWLINE || charID == CHAR_CARRIAGE_RETURN) | |
{ | |
lineFull = YES; | |
} | |
else if (!bitmapChar) | |
{ | |
NSLog(@"Missing character: %d", charID); | |
} | |
else | |
{ | |
if (charID == CHAR_SPACE || charID == CHAR_TAB) { | |
lastWhiteSpace = i; | |
if (isHAlignJustify) | |
numOfWhiteSpaces++; | |
} | |
if (kerning) | |
currentX += [bitmapChar kerningToChar:lastCharID]; | |
SPCharLocation2 *charLocation = [[SPCharLocation2 alloc] initWithChar:bitmapChar]; | |
charLocation.x = currentX + bitmapChar.xOffset; | |
charLocation.y = currentY + bitmapChar.yOffset; | |
[currentLine addObject:charLocation]; | |
currentX += bitmapChar.xAdvance; | |
lastCharID = charID; | |
if (charLocation.x + bitmapChar.width > containerWidth) | |
{ | |
// remove characters and add them again to next line | |
int numCharsToRemove = lastWhiteSpace == -1 ? 1 : i - lastWhiteSpace; | |
int removeIndex = currentLine.count - numCharsToRemove; | |
[currentLine removeObjectsInRange:NSMakeRange(removeIndex, numCharsToRemove)]; | |
if (currentLine.count == 0) | |
break; | |
i -= numCharsToRemove; | |
lineFull = YES; | |
} | |
} | |
if (i == numChars - 1) | |
{ | |
if (isHAlignJustify) { | |
[whitespaces addObject:[NSNumber numberWithUnsignedInt:numOfWhiteSpaces]]; | |
numOfWhiteSpaces = 0; | |
} | |
[lines addObject:currentLine]; | |
finished = YES; | |
} | |
else if (lineFull) | |
{ | |
[lines addObject:currentLine]; | |
if (lastWhiteSpace == i) { | |
[currentLine removeLastObject]; | |
if (isHAlignJustify) | |
numOfWhiteSpaces--; | |
} | |
if (isHAlignJustify) { | |
[whitespaces addObject:[NSNumber numberWithUnsignedInt:numOfWhiteSpaces]]; | |
numOfWhiteSpaces = 0; | |
} | |
if (currentY + 2*_lineHeight <= containerHeight) | |
{ | |
currentLine = [NSMutableArray array]; | |
currentX = 0.0f; | |
currentY += _lineHeight; | |
lastWhiteSpace = -1; | |
lastCharID = -1; | |
} | |
else | |
{ | |
break; | |
} | |
} | |
} // for each char | |
} // if (_lineHeight < containerHeight) | |
if (autoScale && !finished) | |
{ | |
size -= 1; | |
[lines removeAllObjects]; | |
} | |
else | |
{ | |
finished = YES; | |
} | |
} // while (!finished) | |
NSMutableArray *finalLocations = [NSMutableArray array]; | |
int numLines = lines.count; | |
float bottom = numLines * _lineHeight; | |
int yOffset = 0; | |
if (vAlign == SPVAlignBottom) yOffset = containerHeight - bottom; | |
else if (vAlign == SPVAlignCenter) yOffset = (containerHeight - bottom) / 2; | |
for (NSArray *line in lines) | |
{ | |
int numChars = line.count; | |
if (!numChars) continue; | |
int xOffset = 0; | |
SPCharLocation2 *lastLocation = [line lastObject]; | |
float right = lastLocation.x - lastLocation.bitmapChar.xOffset | |
+ lastLocation.bitmapChar.xAdvance; | |
if (hAlign == SPHAlignRight) xOffset = containerWidth - right; | |
else if (hAlign == SPHAlignCenter) xOffset = (containerWidth - right) / 2; | |
for (SPCharLocation2 *charLocation in line) | |
{ | |
charLocation.x = scale * (charLocation.x + xOffset); | |
charLocation.y = scale * (charLocation.y + yOffset); | |
charLocation.scale = scale; | |
if (charLocation.bitmapChar.width > 0 && charLocation.bitmapChar.height > 0) | |
[finalLocations addObject:charLocation]; | |
} | |
} | |
if (isHAlignJustify || isVAlignJustify) | |
{ | |
float bottom = 0.0f; | |
float bottomSpacing = 0.0f; | |
float yOffset = 0.0f; | |
uint numOfLineSpaces = 0; | |
if (isVAlignJustify) | |
{ | |
bottom = (lines.count * _lineHeight) * scale; | |
bottomSpacing = height - bottom; | |
numOfLineSpaces = MAX(lines.count-1, 0); | |
} | |
for (NSMutableArray *line in lines) | |
{ | |
float right = 0.0f; | |
float rightSpacing = 0.0f; | |
float xOffset = 0.0f; | |
uint numOfWhiteSpaces = 0; | |
if (isHAlignJustify && line.count) { | |
SPCharLocation2 *lastLocation = [line lastObject]; | |
right = lastLocation.x + (lastLocation.bitmapChar.width * scale); | |
rightSpacing = width - right; | |
numOfWhiteSpaces = [[whitespaces objectAtIndex:[lines indexOfObject:line]] unsignedIntValue]; | |
} | |
BOOL hAlignLineIndependent = ([lines indexOfObject:line] == lines.count-1) && ((hAlign >= SPHAlignJustifyLastLineLeft)); | |
if (hAlignLineIndependent) { | |
if (hAlign == SPHAlignJustifyLastLineLeft) xOffset = 0; | |
else if (hAlign == SPHAlignJustifyLastLineCenter) xOffset = rightSpacing/2; | |
else if (hAlign == SPHAlignJustifyLastLineRight) xOffset = rightSpacing; | |
} | |
if (numOfLineSpaces || numOfWhiteSpaces) | |
{ | |
for (SPCharLocation2 *charLocation in line) | |
{ | |
if (hAlignLineIndependent) { | |
charLocation.x += xOffset; | |
} else if (numOfWhiteSpaces) { | |
charLocation.x += xOffset; | |
int charID = charLocation.bitmapChar.charID; | |
if (charID == CHAR_SPACE || charID == CHAR_TAB) | |
xOffset += rightSpacing/numOfWhiteSpaces; | |
} | |
if (numOfLineSpaces) | |
charLocation.y += yOffset; | |
} | |
if (numOfLineSpaces) | |
yOffset += bottomSpacing/numOfLineSpaces; | |
} | |
} | |
} | |
return finalLocations; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment