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 -----------------
- iPad SDK : Preparing Dudel for a New Tool (part 4) - Creating a New Drawable Class
- 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
- iPad SDK : Preparing Dudel for a New Tool (part 3) - Creating the Text Tool
- iPad SDK : Preparing Dudel for a New Tool (part 2) - Implementing Changes to the Controller Class
- Coding JavaScript for Mobile Browsers (part 9) - Scripting Styles
- Coding JavaScript for Mobile Browsers (part 8) - DOM
- iPad SDK : Preparing Dudel for a New Tool (part 1) - Setting Up the GUI
- 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
 
 
Most View
- Parallel Programming with Microsoft .Net : Parallel Loops - The Basics
- Programming Excel with VBA and .NET : Tasks in Visual Basic - Do Math
- Organizing Artifacts in BizTalk Server 2009
- Windows 7 : Adding Folders and Files to the Default Website (part 3) - Adding a Folder to the Default Website
- The Art of SEO : Leveraging the Long Tail of Keyword Demand
- Windows Phone 7 : Finding Yourself
- Configuring Exchange Server 2003 to Coexist with X.400-Compliant Messaging Systems
- Windows 7 : Deleting Unnecessary Files
- iPad SDK : New Graphics Functionality - We Are All Tool Users (part 2) - The Pencil Tool
- Optimizing SQL Server for SharePoint 2010 (part 4) - Pre-Creating Your Content Databases
Top 10
- Windows Server 2003 : Restoring Active Directory
- Programming WCF Services : The Response Service (part 3) - Queued Service-Side Programming & Response Service-Side Programming
- Exchange Transport Server Architecture (part 2)
- Windows Server 2008 : Configuring FTP (part 13) - Configuring Directory Browsing
- Windows Server 2008 Server Core : Understanding Internal and External Commands
- Windows Server 2008 : Configuring Windows Media Services (part 6) - Configuring Source Settings
- jQuery 1.3 : The jQuery UI plugin library
- iPad SDK : New Graphics Functionality - We Are All Tool Users (part 5) - The Freehand Tool
- Manage Windows Server 2008 : Work with Preconfigured MMCs
- Active Directory Domain Services 2008: Disable the Directory Service Replication Auditing Subcategory