Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

arnieterm

macrumors regular
Original poster
Aug 28, 2008
201
0
Hi all
I am in the process of rendering rich text using CoreText in iphone os sdk 4. The drawing can be single page or multi page. I have subclassed the UIView whose draw rect is as given below:

Code:
- (void)drawRect:(CGRect)rect{			
	// Initialize a graphics context and set the text matrix to a known value.
	CGContextRef context = UIGraphicsGetCurrentContext();		  
	
	float viewHeight = self.bounds.size.height;	
	CGContextTranslateCTM(context, 0, viewHeight);
	CGContextScaleCTM(context, 1.0, -1.0);
	CGContextSetTextMatrix(context, CGAffineTransformIdentity);	 	
	CGRect textBounds = rect;			
	textBounds.origin.y = -10;
	NSArray* arrayOfElements = [_delegate getArrayOfDataToRender];
	int arrayIndex = 0;
	for (arrayIndex = startIndex; arrayIndex < [arrayOfElements count]; arrayIndex++) {		
		id currentElement = [arrayOfElements objectAtIndex:arrayIndex];		
		if ( [currentElement isKindOfClass:[NSMutableAttributedString class]] ) {							
			CFMutableAttributedStringRef attrString = NULL;
			if (( startRange.location>0 ) && (arrayIndex == startIndex)) {					
				attrString = (CFMutableAttributedStringRef) ([ ((NSAttributedString*)currentElement) attributedSubstringFromRange:startRange] );												
			}else {
				attrString = (CFMutableAttributedStringRef)currentElement;				
			}	
			CGMutablePathRef path = CGPathCreateMutable(); 			
			CGPathAddRect(path, NULL, textBounds);			
			CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
			CFRange fullAttributedStringRange = CFRangeMake(0, CFStringGetLength((CFStringRef)attrString));						
			CTFrameRef frame = CTFramesetterCreateFrame(framesetter, fullAttributedStringRange, path, NULL);	
			NSLog(@"Before drawing the available rectangle is :	%@",NSStringFromCGRect(textBounds));
			if (frame != NULL) {
				CTFrameDraw(frame, context);				
				CFRange visibleRange = CTFrameGetVisibleStringRange(frame);	
				if (visibleRange.length < fullAttributedStringRange.length) {							
					NSRange invisibleStringRange = NSMakeRange(visibleRange.length , fullAttributedStringRange.length - visibleRange.length );					
					CFRelease(frame);
					frame = NULL;					
					CFRelease(framesetter);					
					framesetter = NULL;
					CFRelease(path);	
					path = NULL;
					attrString = NULL;										
					[_delegate insertNextPage: arrayIndex: invisibleStringRange];					   					
					break;
				}				
				
				if (arrayIndex == 5) {
					NSLog(@"Text rendered	`:%@",[ ((NSMutableAttributedString*)attrString) string]);
				}
				CGFloat aHeight = [self measureFrame: frame: context];
                                 //I need to do the below given stuff in order to bring the various frames closer to each other. Not an applicable approach at all
				if (CFArrayGetCount(CTFrameGetLines(frame)) >1) {
					textBounds.origin.y -= (aHeight)/2;
					textBounds.size.height -= (aHeight);	
				}else {
					textBounds.origin.y -= aHeight;			
					textBounds.size.height -= (aHeight);	
				}				
				NSLog(@"Height is		:%f",aHeight);
				NSLog(@"After drawing the available rectangle is :	%@",NSStringFromCGRect(textBounds));
				NSLog(@"************************\n\n");
				
				CFRelease(frame);
				frame = NULL;
				CFRelease(framesetter);				
				framesetter = NULL;
				CFRelease(path);
				path = NULL;
				attrString = NULL;				
			}			
		}else if ([currentElement isKindOfClass:[UIImage class]]) {
			/*
			UIImage* currentImage = (UIImage*)currentElement;
			CGSize imgSize = currentImage.size;
			if (imgSize.height > textBounds.size.height) {
				[_delegate insertNextPage: arrayIndex: NSMakeRange(0, 0)];					   					
				break;
			}
			CGRect imageFrame = CGRectMake((textBounds.size.width - imgSize.width)/2, -(textBounds.origin.y ), imgSize.width, imgSize.height);			
			UIImageView* imgvw = [ [ UIImageView alloc] initWithFrame: imageFrame];
			imgvw.image = currentImage;
			[self addSubview:imgvw];
			[imgvw release];
			textBounds.origin.y -= imgSize.height ;	
			 */
		}
		
	}
	if (arrayIndex == [arrayOfElements count] ) {
		[_delegate drawingFinished];		   
	}
}

Here "_delegate" points to another class instance that initiates the process of rendering the rich text.
My problem is that I am getting the height of rendered text using the function call measureFrame which is little bigger than the actual one which results in bigger gaps between paragraphs. Also it is quite confusing when it comes to dealing with coordinate system for CoreText in iphone. The function that is calulating the height of CTFrame is as given below

Code:
-(CGFloat)measureFrame: (CTFrameRef)frame : (CGContextRef)ctx{
	CFArrayRef lines = CTFrameGetLines(frame);
	CGFloat height = 0.0;
	CGFloat height2 = 0.0;
	CGFloat ascent, descent, leading;
	for (CFIndex index = 0; index < CFArrayGetCount(lines); index++) {
		CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, index);
		CTLineGetTypographicBounds(line, &ascent,  &descent, &leading);	
		CGRect lineHeight = CTLineGetImageBounds(line, ctx);
		height += lineHeight.size.height;
		height2 += (ascent + fabsf(descent) + leading);		
	}
	NSLog(@"Height				%f			Height2		:%f",height, height2);
	return ceil(height);
I have also tried "CTLineGetTypographicBounds" instead of CTLineGetImageBounds and also considered the last line in each frame.
Still getting bigger frame height.
Whats wrong here?
Any idea??
Thanks
Arnieterm
 
Modified the function for measuring the height of CTFrame as below:

Code:
-(CGFloat)measureFrame: (CTFrameRef)frame{	
	
	CFArrayRef lines = CTFrameGetLines(frame);	
	NSUInteger n = CFArrayGetCount(lines);
	CTLineRef firstLine;	
	CGFloat height = 0.0;
	CGFloat asscent, descent, leading;
	if (n == 1) {
		firstLine = (CTLineRef)[(NSArray*)lines objectAtIndex:0];
		CTLineGetTypographicBounds(firstLine, &asscent, &descent, &leading);
		height = asscent + descent + leading;		
		return height;
	}
	CFIndex numLines = CFArrayGetCount(lines);
	CFIndex lastLineIndex = numLines - 1;	
	for( CFIndex index = 0; index < numLines; index++){
		CTLineRef line = (CTLineRef) CFArrayGetValueAtIndex(lines, index);
		CTLineGetTypographicBounds(line, &asscent, &descent, &leading);
		if (index != lastLineIndex) {
			height += asscent + leading;
		}else {
			height += leading;
		}
	}
	
	return height;
}
It is working fine for me but I think it may require improvement
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.