Programming4us
         
 
 
Programming

iPad SDK : Preparing Dudel for a New Tool (part 5) - Rendering Multiple Styles

3/8/2011 3:28:59 PM

6. Rendering Multiple Styles

So, we now have a tool for drawing text, but we still haven't reached the core of what's interesting about Core Text: rendering multiple styles. At this point, lacking a standard GUI widget that gives us anything like a WYSIWYG display while editing the text, there's no really nice way to enter rich text as you can in a word processor, with buttons to change fonts or set colors.

Fortunately for me, I know that you're a computer programmer, and chances are you're already familiar with a way of marking text attributes that isn't as nice, but is applicable to a wide range of problems: HTML! Let's extend our text-rendering algorithm to include a very basic parsing of the text that the user enters, looking for embedded tags that we can use to assign attributes to the text.

I'm going to show you a very simple approach that uses an NSScanner object to scan through the entire text string, searching for just a single kind of tag: <font> (and its matching end tag). It will use the specified values to add attributes to the text. What we're doing here is just barely what I would call "parsing," and will probably make you cringe if your computer science education is less rusty than mine. I'm also well aware that the font tag has been deprecated for years, but it's sure an easy way to do quick-'n-dirty markup compared to using CSS! And it works well for our purposes here.

Edit the beginning of the draw method of TextDrawingInfo as shown here, removing the crossed-out lines and replacing them with the bold line:

- (void)draw {
CGContextRef context = UIGraphicsGetCurrentContext();

//NSMutableAttributedString *attrString = [[[NSMutableAttributedString alloc] initWithString:self.text] autorelease];
//[attrString addAttribute:(NSString *)(kCTForegroundColorAttributeName)
value:(id)self.strokeColor.CGColor range:NSMakeRange(0, [self.text length])];
NSAttributedString *attrString = [self attributedStringFromMarkup:self.text];
CTFramesetterRef framesetter =
CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);


Now we need to define the attributedStringFromMarkup: method in the same class (anywhere above the draw method should be fine). This uses NSScanner to look for the tags it knows about, and makes one big NSMutableAttributedString out of a number of smaller NSAttributedStrings generated between tags. Here, you also see a little usage of CTFontRef, which is Core Text's own way of referring to fonts.

- (NSAttributedString *)attributedStringFromMarkup:(NSString *)markup {
NSMutableAttributedString *attrString =
[[[NSMutableAttributedString alloc] initWithString:@""] autorelease];
NSString *nextTextChunk = nil;
NSScanner *markupScanner = [NSScanner scannerWithString:markup];
CGFloat fontSize = 0.0;
NSString *fontFace = nil;
UIColor *fontColor = nil;
while (![markupScanner isAtEnd]) {
[markupScanner scanUpToString:@"<" intoString:&nextTextChunk];
[markupScanner scanString:@"<" intoString:NULL];
if ([nextTextChunk length] > 0) {
CTFontRef currentFont =
CTFontCreateWithName((CFStringRef)(fontFace ? fontFace : self.font.fontName),
(fontSize != 0.0 ? fontSize : self.font.pointSize),
NULL);
UIColor *color = fontColor ? fontColor : self.strokeColor;
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
(id)color.CGColor, kCTForegroundColorAttributeName,
(id)currentFont, kCTFontAttributeName,
nil];
NSAttributedString *newPiece = [[[NSAttributedString alloc]
initWithString:nextTextChunk attributes:attrs] autorelease];
[attrString appendAttributedString:newPiece];



CFRelease(currentFont);
}
NSString *elementData = nil;
[markupScanner scanUpToString:@">" intoString:&elementData];
[markupScanner scanString:@">" intoString:NULL];
if (elementData) {
if ([elementData length] > 3 &&
[[elementData substringToIndex:4] isEqual:@"font"]) {
fontFace = fontFaceNameFromString(elementData);
fontSize = fontSizeFromString(elementData);
fontColor = fontColorFromString(elementData);
} else if ([elementData length] > 4 &&
[[elementData substringToIndex:5] isEqual:@"/font"]) {
// reset all values
fontSize = 0.0;
fontFace = nil;
fontColor = nil;
}
}
}
return attrString;
}


This method, in turn, offloads the parsing of the font element attributes to the following three functions. Put these directly above the attributedStringFromMarkup:@implementation block is totally fine.) method. (Although it may seem wrong, putting them inside the

static NSString *fontFaceNameFromString(NSString *attrData) {
NSScanner *attributeDataScanner = [NSScanner scannerWithString:attrData];
NSString *faceName = nil;
if ([attributeDataScanner scanUpToString:@"face=\"" intoString:NULL]) {
[attributeDataScanner scanString:@"face=\"" intoString:NULL];
if ([attributeDataScanner scanUpToString:@"\"" intoString:&faceName]) {
return faceName;
}
}
return nil;
}
static CGFloat fontSizeFromString(NSString *attrData) {
NSScanner *attributeDataScanner = [NSScanner scannerWithString:attrData];
NSString *sizeString = nil;
if ([attributeDataScanner scanUpToString:@"size=\"" intoString:NULL]) {
[attributeDataScanner scanString:@"size=\"" intoString:NULL];
if ([attributeDataScanner scanUpToString:@"\"" intoString:&sizeString]) {
return [sizeString floatValue];
}
}
return 0.0;
}
static UIColor *fontColorFromString(NSString *attrData) {
return nil;
}


You'll notice that the third method, fontColorFromString(), isn't shown in a completed form here. In the interests of time and space, and not wandering too far afield from our main topic, let's leave that as an exercise for the reader, shall we?

With this in place, we now have a way to define some characteristics of the text we enter! Build and run Dudel, and create some new objects using the Text tool to try it out. Here are some suggestions for putting it through its paces:

  • Create a paragraph with some <font size="64">really big text</font> and then more normal-sized text.

  • Try sticking some <font face="Courier">Courier into the mix</font> to see how multiple fonts are rendered

Mix and match these however you like. Our parser is far from perfect, and throwing something like nested font tags at it will probably confuse it, but at least it's something!

Other -----------------
- jQuery 1.3 : AJAX - Loading data on demand (part 3) - Loading an XML document
- jQuery 1.3 : AJAX - Loading data on demand (part 2) - Working with JavaScript objects
- jQuery 1.3 : AJAX - Loading data on demand (part 1) - Appending HTML
- Coding JavaScript for Mobile Browsers (part 13) - Zoom and rotate gestures
- Coding JavaScript for Mobile Browsers (part 12) - Swipe gesture
- Coding JavaScript for Mobile Browsers (part 11)
- Coding JavaScript for Mobile Browsers (part 10) - Event Handling
- Coding JavaScript for Mobile Browsers (part 9) - Scripting Styles
- Coding JavaScript for Mobile Browsers (part 8) - DOM
- Coding JavaScript for Mobile Browsers (part 7)
- Coding JavaScript for Mobile Browsers (part 6)
- iPad SDK : The Structure of Core Text
- iPad SDK : PDF Generation
- jQuery 1.3 : Sorting and paging (part 5) - Finessing the sort keys
- jQuery 1.3 : Sorting and paging (part 4)
- jQuery 1.3 : Sorting and paging (part 3) - Using a comparator to sort table rows
- jQuery 1.3 : Sorting and paging (part 2) - JavaScript sorting
- jQuery 1.3 : Sorting and paging (part 1) - Server-side sorting
- Coding JavaScript for Mobile Browsers (part 5)
- Coding JavaScript for Mobile Browsers (part 4)
 
 
Most View
- Sharepoint 2010 : Social Networking - Engaging People
- SharePoint 2010 : Out of the Box Workflows
- Exchange Server 2010 : Federation Scenarios (part 2) - Calendar and Contacts Sharing
- PerformancePoint Services 2010 (part 2) - Installing and Configuring PerformancePoint Services 2010
- SOA with .NET and Windows Azure : Service Implementation with WCF (part 1)
- Exchange Server 2010 : Deploying Unified Messaging (part 1)
- Migrating Databases and Data to SQL Azure (part 4) - Fixing the Script
- SQL server 2012 : T-SQL Enhancements - The GROUPING SETS Operator (part 1) - Rolling Up by Level, Rolling Up All Level Combinations
- Using Non-Windows Systems to Access Exchange Server 2007 : Outlook Express
- SQL Injection Attacks and Defense : Accessing the File System (part 1) - Reading Files
Top 10
- Implementing Edge Services for an Exchange Server 2007 Environment : Utilizing the Basic Sender and Recipient Connection Filters (part 3) - Configuring Recipient Filtering
- Implementing Edge Services for an Exchange Server 2007 Environment : Utilizing the Basic Sender and Recipient Connection Filters (part 2)
- Implementing Edge Services for an Exchange Server 2007 Environment : Utilizing the Basic Sender and Recipient Connection Filters (part 1)
- Implementing Edge Services for an Exchange Server 2007 Environment : Installing and Configuring the Edge Transport Server Components
- What's New in SharePoint 2013 (part 7) - BCS
- What's New in SharePoint 2013 (part 6) - SEARCH
- What's New in SharePoint 2013 (part 6) - WEB CONTENT MANAGEMENT
- What's New in SharePoint 2013 (part 5) - ENTERPRISE CONTENT MANAGEMENT
- What's New in SharePoint 2013 (part 4) - WORKFLOWS
- What's New in SharePoint 2013 (part 3) - REMOTE EVENTS