Advertisement
yohannp

Untitled

Aug 12th, 2012
176
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. //
  2. //  FTCoreTextView.m
  3. //  FTCoreText
  4. //
  5. //  Created by Francesco Freezone <cescofry@gmail.com> on 20/07/2011.
  6. //  Copyright 2011 Fuerte International. All rights reserved.
  7. //
  8.  
  9. #import "FTCoreTextView.h"
  10. #import <QuartzCore/QuartzCore.h>
  11. #import <CoreText/CoreText.h>
  12.  
  13. #import "UIImageView+AFNetworking.h"
  14.  
  15. #define SYSTEM_VERSION_LESS_THAN(v)         ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
  16.  
  17.  
  18. NSString * const FTCoreTextTagDefault = @"_default";
  19. NSString * const FTCoreTextTagImage = @"_image";
  20. NSString * const FTCoreTextTagBullet = @"_bullet";
  21. NSString * const FTCoreTextTagPage = @"_page";
  22. NSString * const FTCoreTextTagLink = @"_link";
  23.  
  24. NSString * const FTCoreTextDataURL = @"url";
  25. NSString * const FTCoreTextDataName = @"FTCoreTextDataName";
  26. NSString * const FTCoreTextDataFrame = @"FTCoreTextDataFrame";
  27. NSString * const FTCoreTextDataAttributes = @"FTCoreTextDataAttributes";
  28.  
  29. typedef enum {
  30.     FTCoreTextTagTypeOpen,
  31.     FTCoreTextTagTypeClose,
  32.     FTCoreTextTagTypeSelfClose
  33. } FTCoreTextTagType;
  34.  
  35. @interface FTCoreTextNode : NSObject
  36.  
  37. @property (nonatomic, assign) FTCoreTextNode    *supernode;
  38. @property (nonatomic, retain) NSArray           *subnodes;
  39. @property (nonatomic, copy)   FTCoreTextStyle   *style;
  40. @property (nonatomic, assign) NSRange           styleRange;
  41. @property (nonatomic, assign) BOOL              isClosed;
  42. @property (nonatomic, assign) NSInteger         startLocation;
  43. @property (nonatomic, assign) BOOL              isLink;
  44. @property (nonatomic, assign) BOOL              isImage;
  45. @property (nonatomic, assign) BOOL              isBullet;
  46. @property (nonatomic, retain) NSString          *imageName;
  47.  
  48. - (NSString *)descriptionOfTree;
  49. - (NSString *)descriptionToRoot;
  50. - (void)addSubnode:(FTCoreTextNode *)node;
  51. - (void)adjustStylesAndSubstylesRangesByRange:(NSRange)insertedRange;
  52. - (void)insertSubnode:(FTCoreTextNode *)subnode atIndex:(NSUInteger)index;
  53. - (void)insertSubnode:(FTCoreTextNode *)subnode beforeNode:(FTCoreTextNode *)node;
  54. - (FTCoreTextNode *)previousNode;
  55. - (FTCoreTextNode *)nextNode;
  56. - (NSUInteger)nodeIndex;
  57. - (FTCoreTextNode *)subnodeAtIndex:(NSUInteger)index;
  58.  
  59. @end
  60.  
  61. @implementation FTCoreTextNode
  62.  
  63. @synthesize supernode = _supernode;
  64. @synthesize subnodes = _subnodes;
  65. @synthesize style = _style;
  66. @synthesize styleRange = _styleRange;
  67. @synthesize isClosed = _isClosed;
  68. @synthesize isLink = _isLink;
  69. @synthesize isImage = _isImage;
  70. @synthesize startLocation = _startLocation;
  71. @synthesize isBullet = _isBullet;
  72. @synthesize imageName = _imageName;
  73.  
  74. - (NSArray *)subnodes
  75. {
  76.     if (_subnodes == nil) {
  77.         _subnodes = [NSMutableArray new];
  78.     }
  79.     return _subnodes;
  80. }
  81.  
  82. - (void)addSubnode:(FTCoreTextNode *)node
  83. {
  84.     [self insertSubnode:node atIndex:[_subnodes count]];
  85. }
  86.  
  87. - (void)insertSubnode:(FTCoreTextNode *)subnode atIndex:(NSUInteger)index
  88. {
  89.     subnode.supernode = self;
  90.    
  91.     NSMutableArray *subnodes = (NSMutableArray *)self.subnodes;
  92.     if (index <= [_subnodes count]) {
  93.         [subnodes insertObject:subnode atIndex:index];
  94.     }
  95.     else {
  96.         [subnodes addObject:subnode];
  97.     }
  98. }
  99.  
  100. - (void)insertSubnode:(FTCoreTextNode *)subnode beforeNode:(FTCoreTextNode *)node
  101. {
  102.     NSInteger existingNodeIndex = [_subnodes indexOfObject:node];
  103.     if (existingNodeIndex == NSNotFound) {
  104.         [self addSubnode:subnode];
  105.     }
  106.     else {
  107.         [self insertSubnode:subnode atIndex:existingNodeIndex];
  108.     }
  109. }
  110.  
  111. - (NSInteger)numberOfParents
  112. {
  113.     NSInteger returnedValue = 0;
  114.     FTCoreTextNode *node = self.supernode;
  115.     while (node) {
  116.         returnedValue++;
  117.         node = node.supernode;
  118.     }
  119.     return returnedValue;
  120. }
  121.  
  122. - (NSString *)description
  123. {
  124.     return [NSString stringWithFormat:@"%@\t-\t%@ - \t%@", [super description], _style.name, NSStringFromRange(_styleRange)];
  125. }
  126.  
  127. - (NSString *)descriptionToRoot
  128. {
  129.     NSMutableString *description = [NSMutableString stringWithString:@"\n\n"];
  130.    
  131.     FTCoreTextNode *node = self;
  132.     do {
  133.         [description insertString:[NSString stringWithFormat:@"%@",[self description]] atIndex:0];
  134.        
  135.         for (int i = 0; i < [self numberOfParents]; i++) {
  136.             [description insertString:@"\t" atIndex:0];
  137.         }
  138.         [description insertString:@"\n" atIndex:0];
  139.         node = node.supernode;
  140.        
  141.     } while (node);
  142.    
  143.     return description;
  144. }
  145.  
  146. - (NSString *)descriptionOfTree
  147. {
  148.     NSMutableString *description = [NSMutableString string];
  149.     for (int i = 0; i < [self numberOfParents]; i++) {
  150.         [description insertString:@"\t" atIndex:0];
  151.     }
  152.     [description appendFormat:@"%@\n", [self description]];
  153.     for (FTCoreTextNode *node in _subnodes) {
  154.         [description appendString:[node descriptionOfTree]];
  155.     }
  156.     return description;
  157. }
  158.  
  159. - (NSArray *)_allSubnodes
  160. {
  161.     NSMutableArray *subnodes = [[NSMutableArray new] autorelease];
  162.     for (FTCoreTextNode *node in _subnodes) {
  163.         [subnodes addObject:node];
  164.         if (node.subnodes) [subnodes addObjectsFromArray:[node _allSubnodes]];
  165.     }
  166.    
  167.     return subnodes;
  168. }
  169.  
  170. //return an array of nodes starting with the current and recursively adding all its child nodes
  171. - (NSArray *)allSubnodes
  172. {
  173.     NSAutoreleasePool *pool = [NSAutoreleasePool new];
  174.     NSArray *allSubnodes = [[self _allSubnodes] copy];
  175.     [pool release];
  176.     NSMutableArray *returnedArray = [NSMutableArray arrayWithObject:self];
  177.     [returnedArray addObjectsFromArray:allSubnodes];
  178.     [allSubnodes release];
  179.     return returnedArray;
  180. }
  181.  
  182. - (void)adjustStylesAndSubstylesRangesByRange:(NSRange)insertedRange
  183. {
  184.     NSRange range = self.styleRange;
  185.     if (range.length + range.location > insertedRange.location) {
  186.         range.location += insertedRange.length;
  187.     }
  188.     self.styleRange = range;
  189.    
  190.     for (FTCoreTextNode *node in _subnodes) {
  191.         [node adjustStylesAndSubstylesRangesByRange:insertedRange];
  192.     }
  193. }
  194.  
  195. - (NSUInteger)nodeIndex
  196. {
  197.     return [_supernode.subnodes indexOfObject:self];
  198. }
  199.  
  200. - (FTCoreTextNode *)subnodeAtIndex:(NSUInteger)index
  201. {
  202.     if (index < [_subnodes count]) {
  203.         return [_subnodes objectAtIndex:index];
  204.     }
  205.     return nil;
  206. }
  207.  
  208. - (FTCoreTextNode *)previousNode
  209. {
  210.     NSUInteger index = [self nodeIndex];
  211.     if (index != NSNotFound) {
  212.         return [_supernode subnodeAtIndex:index - 1];
  213.     }
  214.     return nil;
  215. }
  216.  
  217. - (FTCoreTextNode *)nextNode
  218. {
  219.     NSUInteger index = [self nodeIndex];
  220.     if (index != NSNotFound) {
  221.         return [_supernode subnodeAtIndex:index + 1];
  222.     }
  223.     return nil;
  224. }
  225.  
  226. - (void)dealloc
  227. {
  228.     [_subnodes release];
  229.     [_style release];
  230.     [_imageName release];
  231.     [super dealloc];
  232. }
  233.  
  234. @end
  235.  
  236.  
  237.  
  238. @interface FTCoreTextView ()
  239.  
  240. @property (nonatomic, assign) CTFramesetterRef framesetter;
  241. @property (nonatomic, retain) FTCoreTextNode *rootNode;
  242.  
  243. - (void)updateFramesetterIfNeeded;
  244. - (void)processText;
  245. CTFontRef CTFontCreateFromUIFont(UIFont *font);
  246. - (BOOL)isSystemUnder3_2;
  247. UITextAlignment UITextAlignmentFromCoreTextAlignment(FTCoreTextAlignement alignment);
  248. NSInteger rangeSort(NSString *range1, NSString *range2, void *context);
  249. - (void)drawImages;
  250. - (void)doInit;
  251. - (void)didMakeChanges;
  252. - (NSString *)defaultTagNameForKey:(NSString *)tagKey;
  253. - (NSMutableArray *)divideTextInPages:(NSString *)string;
  254.  
  255. @end
  256.  
  257. @implementation FTCoreTextView
  258.  
  259. @synthesize text = _text;
  260. @synthesize processedString = _processedString;
  261. @synthesize path = _path;
  262. @synthesize URLs = _URLs;
  263. @synthesize images = _images;
  264. @synthesize delegate = _delegate;
  265. @synthesize framesetter = _framesetter;
  266. @synthesize rootNode = _rootNode;
  267. @synthesize shadowColor = _shadowColor;
  268. @synthesize shadowOffset = _shadowOffset;
  269. @synthesize attributedString = _attributedString;
  270.  
  271. #pragma mark - Tools methods
  272.  
  273. NSInteger rangeSort(NSString *range1, NSString *range2, void *context)
  274. {
  275.     NSRange r1 = NSRangeFromString(range1);
  276.     NSRange r2 = NSRangeFromString(range2);
  277.    
  278.     if (r1.location < r2.location)
  279.         return NSOrderedAscending;
  280.     else if (r1.location > r2.location)
  281.         return NSOrderedDescending;
  282.     else
  283.         return NSOrderedSame;
  284. }
  285.  
  286. CTFontRef CTFontCreateFromUIFont(UIFont *font)
  287. {
  288.     CTFontRef ctFont = CTFontCreateWithName((CFStringRef)font.fontName,
  289.                                             font.pointSize,
  290.                                             NULL);
  291.     return ctFont;
  292. }
  293.  
  294. UITextAlignment UITextAlignmentFromCoreTextAlignment(FTCoreTextAlignement alignment)
  295. {
  296.     switch (alignment) {
  297.         case FTCoreTextAlignementCenter:
  298.             return UITextAlignmentCenter;
  299.             break;
  300.         case FTCoreTextAlignementRight:
  301.             return UITextAlignmentRight;
  302.             break;
  303.         default:
  304.             return UITextAlignmentLeft;
  305.             break;
  306.     }
  307. }
  308.  
  309. - (BOOL)isSystemUnder3_2
  310. {
  311.     static BOOL checked = NO;
  312.     static BOOL systemUnder3_2;
  313.     if (!checked) {
  314.         checked = YES;
  315.         systemUnder3_2 = SYSTEM_VERSION_LESS_THAN(@"3.2");
  316.     }
  317.     return systemUnder3_2;
  318. }
  319.  
  320. #pragma mark - FTCoreTextView business
  321. #pragma mark -
  322.  
  323. - (void)changeDefaultTag:(NSString *)coreTextTag toTag:(NSString *)newDefaultTag
  324. {
  325.     if ([_defaultsTags objectForKey:coreTextTag] == nil) {
  326.         [NSException raise:NSInvalidArgumentException format:@"%@ is not a default tag of FTCoreTextView. Use the constant FTCoreTextTag constants.", coreTextTag];
  327.     }
  328.     else {
  329.         [_defaultsTags setObject:newDefaultTag forKey:coreTextTag];
  330.     }
  331. }
  332.  
  333. - (NSString *)defaultTagNameForKey:(NSString *)tagKey
  334. {
  335.     return [_defaultsTags objectForKey:tagKey];
  336. }
  337.  
  338. - (void)didMakeChanges
  339. {
  340.     _coreTextViewFlags.updatedAttrString = NO;
  341.     _coreTextViewFlags.updatedFramesetter = NO;
  342. }
  343.  
  344. #pragma mark - UI related
  345.  
  346. - (NSDictionary *)dataForPoint:(CGPoint)point
  347. {
  348.     NSMutableDictionary *returnedDict = [NSMutableDictionary dictionary];
  349.    
  350.     CGMutablePathRef mainPath = CGPathCreateMutable();
  351.     if (!_path) {
  352.         CGPathAddRect(mainPath, NULL, CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height));
  353.     }
  354.     else {
  355.         CGPathAddPath(mainPath, NULL, _path);
  356.     }
  357.    
  358.     CTFrameRef ctframe = CTFramesetterCreateFrame(_framesetter, CFRangeMake(0, 0), mainPath, NULL);
  359.     CGPathRelease(mainPath);
  360.    
  361.     NSArray *lines = (NSArray *)CTFrameGetLines(ctframe);
  362.     NSInteger lineCount = [lines count];
  363.     CGPoint origins[lineCount];
  364.    
  365.     if (lineCount != 0) {
  366.        
  367.         CTFrameGetLineOrigins(ctframe, CFRangeMake(0, 0), origins);
  368.        
  369.         for (int i = 0; i < lineCount; i++) {
  370.             CGPoint baselineOrigin = origins[i];
  371.             //the view is inverted, the y origin of the baseline is upside down
  372.             baselineOrigin.y = CGRectGetHeight(self.frame) - baselineOrigin.y;
  373.            
  374.             CTLineRef line = (CTLineRef)[lines objectAtIndex:i];
  375.             CGFloat ascent, descent;
  376.             CGFloat lineWidth = CTLineGetTypographicBounds(line, &ascent, &descent, NULL);
  377.            
  378.             CGRect lineFrame = CGRectMake(baselineOrigin.x, baselineOrigin.y - ascent, lineWidth, ascent + descent);
  379.            
  380.             if (CGRectContainsPoint(lineFrame, point)) {
  381.                 //we look if the position of the touch is correct on the line
  382.                
  383.                 CFIndex index = CTLineGetStringIndexForPosition(line, point);
  384.                
  385.                 NSArray *urlsKeys = [_URLs allKeys];
  386.                
  387.                 for (NSString *key in urlsKeys) {
  388.                     NSRange range = NSRangeFromString(key);
  389.                     if (index >= range.location && index < range.location + range.length) {
  390.                         NSURL *url = [_URLs objectForKey:key];
  391.                         if (url) [returnedDict setObject:url forKey:FTCoreTextDataURL];
  392.                         break;
  393.                     }
  394.                 }
  395.                
  396.                 //frame
  397.                 CFArrayRef runs = CTLineGetGlyphRuns(line);
  398.                 for(CFIndex j = 0; j < CFArrayGetCount(runs); j++) {
  399.                     CTRunRef run = CFArrayGetValueAtIndex(runs, j);
  400.                     NSDictionary* attributes = (NSDictionary*)CTRunGetAttributes(run);
  401.                    
  402.                     NSString *name = [attributes objectForKey:FTCoreTextDataName];
  403.                     if (![name isEqualToString:@"_link"]) continue;
  404.                    
  405.                     [returnedDict setObject:attributes forKey:FTCoreTextDataAttributes];
  406.                    
  407.                     CGRect runBounds;
  408.                     runBounds.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, NULL); //8
  409.                     runBounds.size.height = ascent + descent;
  410.                    
  411.                     CGFloat xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL); //9
  412.                     runBounds.origin.x = baselineOrigin.x + self.frame.origin.x + xOffset + 0;
  413.                     runBounds.origin.y = baselineOrigin.y + lineFrame.size.height - ascent;
  414.                    
  415.                     [returnedDict setObject:NSStringFromCGRect(runBounds) forKey:FTCoreTextDataFrame];
  416.                    
  417.                 }
  418.                
  419.                
  420.             }
  421.             if (returnedDict.count > 0) break;
  422.         }
  423.     }
  424.    
  425.     CFRelease(ctframe);
  426.    
  427.     return returnedDict;
  428. }
  429.  
  430.  
  431. - (void)updateFramesetterIfNeeded
  432. {
  433.     if (!_coreTextViewFlags.updatedAttrString) {
  434.         if (_framesetter != NULL) CFRelease(_framesetter);
  435.         _framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)self.attributedString);
  436.         _coreTextViewFlags.updatedAttrString = YES;
  437.     }
  438. }
  439.  
  440. /*!
  441.  * @abstract get the supposed size of the drawn text
  442.  *
  443.  */
  444.  
  445. - (CGSize)suggestedSizeConstrainedToSize:(CGSize)size
  446. {
  447.     CGSize suggestedSize;
  448.     if ([self isSystemUnder3_2]) {
  449.         if (_processedString == nil) {
  450.             [self processText];
  451.         }
  452.         FTCoreTextStyle *style = [_styles objectForKey:[self defaultTagNameForKey:FTCoreTextTagDefault]];
  453.         suggestedSize = [_processedString sizeWithFont:style.font constrainedToSize:CGSizeMake(self.bounds.size.width, MAXFLOAT)];
  454.     }
  455.     else {
  456.         [self updateFramesetterIfNeeded];
  457.         if (_framesetter == NULL) {
  458.             return CGSizeZero;
  459.         }
  460.         suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(_framesetter, CFRangeMake(0, 0), NULL, size, NULL);
  461.         suggestedSize = CGSizeMake(ceilf(suggestedSize.width), ceilf(suggestedSize.height));
  462.     }
  463.     return suggestedSize;
  464. }
  465.  
  466. /*!
  467.  * @abstract handy method to fit to the suggested height in one call
  468.  *
  469.  */
  470.  
  471. - (void)fitToSuggestedHeight
  472. {
  473.     CGSize suggestedSize = [self suggestedSizeConstrainedToSize:CGSizeMake(CGRectGetWidth(self.frame), MAXFLOAT)];
  474.     CGRect viewFrame = self.frame;
  475.     viewFrame.size.height = suggestedSize.height;
  476.     self.frame = viewFrame;
  477. }
  478.  
  479. #pragma mark - Text processing
  480.  
  481. /*!
  482.  * @abstract remove the tags from the text and create a tree representation of the text
  483.  *
  484.  */
  485.  
  486. - (void)processText
  487. {
  488.     if (!_text || [_text length] == 0) return;
  489.    
  490.     [_URLs removeAllObjects];
  491.     [_images removeAllObjects];
  492.    
  493.     FTCoreTextNode *rootNode = [[FTCoreTextNode new] autorelease];
  494.     rootNode.style = [_styles objectForKey:[self defaultTagNameForKey:FTCoreTextTagDefault]];
  495.    
  496.     FTCoreTextNode *currentSupernode = rootNode;
  497.    
  498.     NSMutableString *processedString = [NSMutableString stringWithString:_text];
  499.    
  500.     BOOL finished = NO;
  501.     NSRange remainingRange = NSMakeRange(0, [processedString length]);
  502.    
  503.     NSString *regEx = @"<(/){0,1}.*?( /){0,1}>";
  504.    
  505.     BOOL systemUnder3_2 = ([self isSystemUnder3_2]);
  506.    
  507.     NSArray *possibleTags = nil;
  508.     if (systemUnder3_2) {
  509.        
  510.         NSMutableArray *tagsNames = [NSMutableArray new];
  511.         NSMutableArray *tags = [NSMutableArray new];
  512.         for (FTCoreTextStyle *style in _styles.allValues) {
  513.             [tagsNames addObject:style.name];
  514.         }
  515.         if (![tagsNames containsObject:FTCoreTextTagBullet]) [tagsNames addObject:FTCoreTextTagBullet];
  516.         if (![tagsNames containsObject:FTCoreTextTagImage]) [tagsNames addObject:FTCoreTextTagImage];
  517.         if (![tagsNames containsObject:FTCoreTextTagPage]) [tagsNames addObject:FTCoreTextTagPage];
  518.        
  519.         for (NSString *tagName in tagsNames) {
  520.             [tags addObject:[NSString stringWithFormat:@"<%@>", tagName]];
  521.             [tags addObject:[NSString stringWithFormat:@"</%@>", tagName]];
  522.             [tags addObject:[NSString stringWithFormat:@"<%@ />", tagName]];
  523.         }
  524.        
  525.         possibleTags = tags;
  526.     }
  527.    
  528.     while (!finished) {
  529.        
  530.         NSRange tagRange;
  531.         if (systemUnder3_2) {
  532.            
  533.             NSMutableArray *ranges = [NSMutableArray new];
  534.             for (NSString *tag in possibleTags) {
  535.                 NSRange tagRange = [processedString rangeOfString:tag options:0 range:remainingRange];
  536.                 if (tagRange.location != NSNotFound) {
  537.                     [ranges addObject:NSStringFromRange(tagRange)];
  538.                 }
  539.             }
  540.             NSArray *sortedRanges = [ranges sortedArrayUsingFunction:rangeSort context:NULL];
  541.             [ranges release];
  542.            
  543.             if (sortedRanges.count == 0) {
  544.                 tagRange.location = NSNotFound;
  545.             }
  546.             else {
  547.                 tagRange = NSRangeFromString([sortedRanges objectAtIndex:0]);
  548.             }
  549.         }
  550.         else {
  551.             tagRange = [processedString rangeOfString:regEx options:NSRegularExpressionSearch range:remainingRange];
  552.         }
  553.        
  554.         if (tagRange.location == NSNotFound) {
  555.             if (currentSupernode != rootNode && !currentSupernode.isClosed) {
  556.                 NSLog(@"FTCoreTextView :%@ - Couldn't parse text because tag '%@' at position %d is not closed - aborting rendering", self, currentSupernode.style.name, currentSupernode.startLocation);
  557.                 return;
  558.             }
  559.             finished = YES;
  560.             continue;
  561.         }
  562.        
  563.         NSString *fullTag = [processedString substringWithRange:tagRange];
  564.         FTCoreTextTagType tagType;
  565.        
  566.         if ([fullTag rangeOfString:@"</"].location == 0) {
  567.             tagType = FTCoreTextTagTypeClose;
  568.         }
  569.         else if ([fullTag rangeOfString:@"/>"].location == NSNotFound && [fullTag rangeOfString:@" />"].location == NSNotFound) {
  570.             tagType = FTCoreTextTagTypeOpen;
  571.         }
  572.         else {
  573.             tagType = FTCoreTextTagTypeSelfClose;
  574.         }
  575.        
  576.         NSArray *tagsComponents = [fullTag componentsSeparatedByString:@" "];
  577.         NSString *tagName = (tagsComponents.count > 0) ? [tagsComponents objectAtIndex:0] : fullTag;
  578.         tagName = [tagName stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"< />"]];
  579.        
  580.         FTCoreTextStyle *style = [_styles objectForKey:tagName];
  581.        
  582.         if (style == nil) {
  583.             style = [_styles objectForKey:[self defaultTagNameForKey:FTCoreTextTagDefault]];
  584.             NSLog(@"FTCoreTextView :%@ - Couldn't find style for tag '%@'", self, tagName);
  585.         }
  586.        
  587.         switch (tagType) {
  588.             case FTCoreTextTagTypeOpen:
  589.             {
  590.                 if (currentSupernode.isLink || currentSupernode.isImage) {
  591.                     NSString *predefinedTag = nil;
  592.                     if (currentSupernode.isLink) predefinedTag = [self defaultTagNameForKey:FTCoreTextTagLink];
  593.                     else if (currentSupernode.isImage) predefinedTag = [self defaultTagNameForKey:FTCoreTextTagImage];
  594.                     NSLog(@"FTCoreTextView :%@ - You can't open a new tag inside a '%@' tag - aborting rendering", self, predefinedTag);
  595.                     return;
  596.                 }
  597.                
  598.                 FTCoreTextNode *newNode = [FTCoreTextNode new];
  599.                 newNode.style = style;
  600.                 newNode.startLocation = tagRange.location;
  601.                
  602.                 if ([tagName isEqualToString:[self defaultTagNameForKey:FTCoreTextTagLink]]) {
  603.                     newNode.isLink = YES;
  604.                 }
  605.                 else if ([tagName isEqualToString:[self defaultTagNameForKey:FTCoreTextTagBullet]]) {
  606.                     newNode.isBullet = YES;
  607.                    
  608.                     NSString *appendedString = [NSString stringWithFormat:@"%@\t", newNode.style.bulletCharacter];
  609.                    
  610.                     [processedString insertString:appendedString atIndex:tagRange.location + tagRange.length];
  611.                    
  612.                     //bullet styling
  613.                     FTCoreTextStyle *bulletStyle = [FTCoreTextStyle new];
  614.                     bulletStyle.name = @"_FTBulletStyle";
  615.                     bulletStyle.font = newNode.style.bulletFont;
  616.                     bulletStyle.color = newNode.style.bulletColor;
  617.                     bulletStyle.applyParagraphStyling = NO;
  618.                     bulletStyle.paragraphInset = UIEdgeInsetsMake(0, 0, 0, newNode.style.paragraphInset.left);
  619.                    
  620.                     FTCoreTextNode *bulletNode = [FTCoreTextNode new];
  621.                     bulletNode.style = bulletStyle;
  622.                     [bulletStyle release];
  623.                     bulletNode.styleRange = NSMakeRange(tagRange.location, [appendedString length]);
  624.                    
  625.                     [newNode addSubnode:bulletNode];
  626.                     [bulletNode release];
  627.                 }
  628.                 else if ([tagName isEqualToString:[self defaultTagNameForKey:FTCoreTextTagImage]]) {
  629.                     newNode.isImage = YES;
  630.                 }
  631.                
  632.                 [processedString replaceCharactersInRange:tagRange withString:@""];
  633.                
  634.                 [currentSupernode addSubnode:newNode];
  635.                 [newNode release];
  636.                
  637.                 currentSupernode = newNode;
  638.                
  639.                 remainingRange.location = tagRange.location;
  640.                 remainingRange.length = [processedString length] - tagRange.location;
  641.             }
  642.                 break;
  643.             case FTCoreTextTagTypeClose:
  644.             {
  645.                 if ((![currentSupernode.style.name isEqualToString:[self defaultTagNameForKey:FTCoreTextTagDefault]] && ![currentSupernode.style.name isEqualToString:tagName]) ) {
  646.                     NSLog(@"FTCoreTextView :%@ - Closing tag '%@' at range %@ doesn't match open tag '%@' - aborting rendering", self, fullTag, NSStringFromRange(tagRange), currentSupernode.style.name);
  647.                     return;
  648.                 }
  649.                
  650.                 currentSupernode.isClosed = YES;
  651.                
  652.                 if (currentSupernode.isLink) {
  653.                     //replace active string with url text
  654.                    
  655.                     NSRange elementContentRange = NSMakeRange(currentSupernode.startLocation, tagRange.location - currentSupernode.startLocation);
  656.                     NSString *elementContent = [processedString substringWithRange:elementContentRange];
  657.                     NSRange pipeRange = [elementContent rangeOfString:@"|"];
  658.                     NSString *urlString = nil;
  659.                     NSString *urlDescription = nil;
  660.                     if (pipeRange.location != NSNotFound) {
  661.                         urlString = [elementContent substringToIndex:pipeRange.location];
  662.                         urlDescription = [elementContent substringFromIndex:pipeRange.location + 1];
  663.                     }
  664.                    
  665.                     [processedString replaceCharactersInRange:NSMakeRange(elementContentRange.location, elementContentRange.length + tagRange.length) withString:urlDescription];
  666.                     if (![urlString hasPrefix:@"http://"]) {
  667.                         urlString = [NSString stringWithFormat:@"http://%@", urlString];
  668.                     }
  669.                     NSURL *url = [NSURL URLWithString:urlString];
  670.                     NSRange urlDescriptionRange = NSMakeRange(elementContentRange.location, [urlDescription length]);
  671.                     [_URLs setObject:url forKey:NSStringFromRange(urlDescriptionRange)];
  672.                    
  673.                     currentSupernode.styleRange = urlDescriptionRange;
  674.                 }
  675.                 else if (currentSupernode.isImage) {
  676.                     //replace active string with emptySpace
  677.                    
  678.                     NSRange elementContentRange = NSMakeRange(currentSupernode.startLocation, tagRange.location - currentSupernode.startLocation);
  679.                     NSString *elementContent = [processedString substringWithRange:elementContentRange];
  680.                    
  681.                     UIImageView *imgData = [[UIImageView alloc] init];
  682.                     [imgData setImageWithURL:[NSURL URLWithString:elementContent] placeholderImage:[UIImage imageNamed:@"placeholder.jpg"]];
  683.                     UIImage *img = imgData.image;
  684.                    
  685.                     if (img) {
  686.                         NSString *lines = @"\n";
  687.                         float leading = img.size.height;
  688.                         currentSupernode.style.leading = leading;
  689.                        
  690.                         currentSupernode.imageName = elementContent;
  691.                         [processedString replaceCharactersInRange:NSMakeRange(elementContentRange.location, elementContentRange.length + tagRange.length) withString:lines];
  692.                        
  693.                         [_images addObject:currentSupernode];
  694.                         currentSupernode.styleRange = NSMakeRange(elementContentRange.location, [lines length]);
  695.                        
  696.                     }
  697.                     else {
  698.                         NSLog(@"FTCoreTextView :%@ - Couldn't find image '%@' in main bundle", self, elementContentRange);
  699.                         [processedString replaceCharactersInRange:tagRange withString:@""];
  700.                     }
  701.                 }
  702.                 else {
  703.                     currentSupernode.styleRange = NSMakeRange(currentSupernode.startLocation, tagRange.location - currentSupernode.startLocation);
  704.                     [processedString replaceCharactersInRange:tagRange withString:@""];
  705.                 }
  706.                
  707.                 if ([currentSupernode.style.appendedCharacter length] > 0) {
  708.                     [processedString insertString:currentSupernode.style.appendedCharacter atIndex:currentSupernode.styleRange.location + currentSupernode.styleRange.length];
  709.                     NSRange newStyleRange = currentSupernode.styleRange;
  710.                     newStyleRange.length += [currentSupernode.style.appendedCharacter length];
  711.                     currentSupernode.styleRange = newStyleRange;
  712.                 }
  713.                
  714.                 if (style.paragraphInset.top > 0) {
  715.                     if (![style.name isEqualToString:[self defaultTagNameForKey:FTCoreTextTagBullet]] ||  [[currentSupernode previousNode].style.name isEqualToString:[self defaultTagNameForKey:FTCoreTextTagBullet]]) {
  716.                        
  717.                         //fix: add a new line for each new line and set its height to 'top' value
  718.                         [processedString insertString:@"\n" atIndex:currentSupernode.startLocation];
  719.                         NSRange topSpacingStyleRange = NSMakeRange(currentSupernode.startLocation, [@"\n" length]);
  720.                         FTCoreTextStyle *topSpacingStyle = [[FTCoreTextStyle alloc] init];
  721.                         topSpacingStyle.name = [NSString stringWithFormat:@"_FTTopSpacingStyle_%@", currentSupernode.style.name];
  722.                         topSpacingStyle.minLineHeight = currentSupernode.style.paragraphInset.top;
  723.                         topSpacingStyle.maxLineHeight = currentSupernode.style.paragraphInset.top;
  724.                         FTCoreTextNode *topSpacingNode = [[FTCoreTextNode alloc] init];
  725.                         topSpacingNode.style = topSpacingStyle;
  726.                         [topSpacingStyle release];
  727.                        
  728.                         topSpacingNode.styleRange = topSpacingStyleRange;
  729.                        
  730.                         [currentSupernode.supernode insertSubnode:topSpacingNode beforeNode:currentSupernode];
  731.                         [topSpacingNode release];
  732.                        
  733.                         [currentSupernode adjustStylesAndSubstylesRangesByRange:topSpacingStyleRange];
  734.                     }
  735.                 }
  736.                
  737.                 remainingRange.location = currentSupernode.styleRange.location + currentSupernode.styleRange.length;
  738.                 remainingRange.length = [processedString length] - remainingRange.location;
  739.                
  740.                 currentSupernode = currentSupernode.supernode;
  741.             }
  742.                 break;
  743.             case FTCoreTextTagTypeSelfClose:
  744.             {
  745.                 FTCoreTextNode *newNode = [FTCoreTextNode new];
  746.                 newNode.style = style;
  747.                 [processedString replaceCharactersInRange:tagRange withString:newNode.style.appendedCharacter];
  748.                 newNode.styleRange = NSMakeRange(tagRange.location, [newNode.style.appendedCharacter length]);
  749.                 [currentSupernode addSubnode:newNode];
  750.                 [newNode release];
  751.                
  752.                 remainingRange.location = tagRange.location;
  753.                 remainingRange.length = [processedString length] - tagRange.location;
  754.             }
  755.                 break;
  756.         }
  757.     }
  758.    
  759.     rootNode.styleRange = NSMakeRange(0, [processedString length]);
  760.    
  761.     self.rootNode = rootNode;
  762.     self.processedString = processedString;
  763. }
  764.  
  765. /*!
  766.  * @abstract Remove all the tags and return a clean text to be used
  767.  *
  768.  */
  769.  
  770. + (NSString *)stripTagsForString:(NSString *)string
  771. {
  772.     FTCoreTextView *instance = [[FTCoreTextView alloc] initWithFrame:CGRectZero];
  773.     [instance setText:string];
  774.     [instance processText];
  775.     NSString *result = [[instance.processedString copy] autorelease];
  776.     [instance release];
  777.     return result;
  778. }
  779.  
  780. /*!
  781.  * @abstract divide the text in different pages according to the tags <_page/> found
  782.  *
  783.  */
  784.  
  785. + (NSArray *)pagesFromText:(NSString *)string
  786. {
  787.     FTCoreTextView *instance = [[FTCoreTextView alloc] initWithFrame:CGRectZero];
  788.     NSArray *result = [instance divideTextInPages:string];
  789.     [instance release];
  790.     return (NSArray *)result;
  791. }
  792.  
  793. /*!
  794.  * @abstract divide the text in different pages according to the tags <_page/> found
  795.  *
  796.  */
  797.  
  798. - (NSMutableArray *)divideTextInPages:(NSString *)string
  799. {
  800.     NSMutableArray *result = [NSMutableArray array];
  801.     int prevStart = 0;
  802.     while (YES) {
  803.         NSRange rangeStart = [string rangeOfString:[NSString stringWithFormat:@"<%@/>", [self defaultTagNameForKey:FTCoreTextTagPage]]];
  804.         if (rangeStart.location == NSNotFound) rangeStart = [string rangeOfString:[NSString stringWithFormat:@"<%@ />", [self defaultTagNameForKey:FTCoreTextTagPage]]];
  805.        
  806.         if (rangeStart.location != NSNotFound) {
  807.             NSString *page = [string substringWithRange:NSMakeRange(prevStart, rangeStart.location)];
  808.             [result addObject:page];
  809.             string = [string stringByReplacingCharactersInRange:rangeStart withString:@""];
  810.             prevStart = rangeStart.location;
  811.         }
  812.         else {
  813.             NSString *page = [string substringWithRange:NSMakeRange(prevStart, (string.length - prevStart))];
  814.             [result addObject:page];
  815.             break;
  816.         }
  817.     }
  818.     return result;
  819. }
  820.  
  821. #pragma mark Styling
  822.  
  823. - (void)addStyle:(FTCoreTextStyle *)style
  824. {
  825.     [_styles setValue:style forKey:style.name];
  826.     [self didMakeChanges];
  827.     if ([self superview]) [self setNeedsDisplay];
  828. }
  829.  
  830. - (void)addStyles:(NSArray *)styles
  831. {
  832.     for (FTCoreTextStyle *style in styles) {
  833.         [_styles setValue:style forKey:style.name];
  834.     }
  835.     [self didMakeChanges];
  836.     if ([self superview]) [self setNeedsDisplay];
  837. }
  838.  
  839. - (void)removeAllStyles
  840. {
  841.     [_styles removeAllObjects];
  842.     [self didMakeChanges];
  843.     if ([self superview]) [self setNeedsDisplay];
  844. }
  845.  
  846. - (void)applyStyle:(FTCoreTextStyle *)style inRange:(NSRange)styleRange onString:(NSMutableAttributedString **)attributedString
  847. {
  848.     [*attributedString addAttribute:(id)FTCoreTextDataName
  849.                               value:(id)style.name
  850.                               range:styleRange];
  851.    
  852.     [*attributedString addAttribute:(id)kCTForegroundColorAttributeName
  853.                               value:(id)style.color.CGColor
  854.                               range:styleRange];
  855.    
  856.     if (style.isUnderLined) {
  857.         NSNumber *underline = [NSNumber numberWithInt:kCTUnderlineStyleSingle];
  858.         [*attributedString addAttribute:(id)kCTUnderlineStyleAttributeName
  859.                                   value:(id)underline
  860.                                   range:styleRange];
  861.     }
  862.    
  863.     CTFontRef ctFont = CTFontCreateFromUIFont(style.font);
  864.    
  865.     [*attributedString addAttribute:(id)kCTFontAttributeName
  866.                               value:(id)ctFont
  867.                               range:styleRange];
  868.     CFRelease(ctFont);
  869.    
  870.     CTTextAlignment alignment = style.textAlignment;
  871.     CGFloat maxLineHeight = style.maxLineHeight;
  872.     CGFloat minLineHeight = style.minLineHeight;
  873.     CGFloat paragraphLeading = style.leading;
  874.    
  875.     CGFloat paragraphSpacingBefore = style.paragraphInset.top;
  876.     CGFloat paragraphSpacingAfter = style.paragraphInset.bottom;
  877.     CGFloat paragraphFirstLineHeadIntent = style.paragraphInset.left;
  878.     CGFloat paragraphHeadIntent = style.paragraphInset.left;
  879.     CGFloat paragraphTailIntent = style.paragraphInset.right;
  880.    
  881.     //if (SYSTEM_VERSION_LESS_THAN(@"5.0")) {
  882.     paragraphSpacingBefore = 0;
  883.     //}
  884.    
  885.     CFIndex numberOfSettings = 9;
  886.     CGFloat tabSpacing = 28.f;
  887.    
  888.     BOOL applyParagraphStyling = style.applyParagraphStyling;
  889.    
  890.     if ([style.name isEqualToString:[self defaultTagNameForKey:FTCoreTextTagBullet]]) {
  891.         applyParagraphStyling = YES;
  892.     }
  893.     else if ([style.name isEqualToString:@"_FTBulletStyle"]) {
  894.         applyParagraphStyling = YES;
  895.         numberOfSettings++;
  896.         tabSpacing = style.paragraphInset.right;
  897.         paragraphSpacingBefore = 0;
  898.         paragraphSpacingAfter = 0;
  899.         paragraphFirstLineHeadIntent = 0;
  900.         paragraphTailIntent = 0;
  901.     }
  902.     else if ([style.name hasPrefix:@"_FTTopSpacingStyle"]) {
  903.         [*attributedString removeAttribute:(id)kCTParagraphStyleAttributeName range:styleRange];
  904.     }
  905.    
  906.     if (applyParagraphStyling) {
  907.        
  908.         CTTextTabRef tabArray[] = { CTTextTabCreate(0, tabSpacing, NULL) };
  909.        
  910.         CFArrayRef tabStops = CFArrayCreate( kCFAllocatorDefault, (const void**) tabArray, 1, &kCFTypeArrayCallBacks );
  911.         CFRelease(tabArray[0]);
  912.        
  913.         CTParagraphStyleSetting settings[] = {
  914.             {kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment},
  915.             {kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(CGFloat), &maxLineHeight},
  916.             {kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(CGFloat), &minLineHeight},
  917.             {kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(CGFloat), &paragraphSpacingBefore},
  918.             {kCTParagraphStyleSpecifierParagraphSpacing, sizeof(CGFloat), &paragraphSpacingAfter},
  919.             {kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(CGFloat), &paragraphFirstLineHeadIntent},
  920.             {kCTParagraphStyleSpecifierHeadIndent, sizeof(CGFloat), &paragraphHeadIntent},
  921.             {kCTParagraphStyleSpecifierTailIndent, sizeof(CGFloat), &paragraphTailIntent},
  922.             {kCTParagraphStyleSpecifierLineSpacing, sizeof(CGFloat), &paragraphLeading},
  923.             {kCTParagraphStyleSpecifierTabStops, sizeof(CFArrayRef), &tabStops}//always at the end
  924.         };
  925.        
  926.         CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, numberOfSettings);
  927.         [*attributedString addAttribute:(id)kCTParagraphStyleAttributeName
  928.                                   value:(id)paragraphStyle
  929.                                   range:styleRange];
  930.         CFRelease(tabStops);
  931.         CFRelease(paragraphStyle);
  932.     }
  933. }
  934.  
  935. #pragma mark - Object lifecycle
  936.  
  937. - (id)initWithFrame:(CGRect)frame
  938. {
  939.     return [self initWithFrame:frame andAttributedString:nil];
  940. }
  941.  
  942. - (id)initWithFrame:(CGRect)frame andAttributedString:(NSAttributedString *)attributedString
  943. {
  944.     self = [super initWithFrame:frame];
  945.     if (self) {
  946.         _attributedString = [attributedString retain];
  947.         [self doInit];
  948.     }
  949.     return self;
  950. }
  951.  
  952. - (id)initWithCoder:(NSCoder *)aDecoder
  953. {
  954.     self = [super initWithCoder:aDecoder];
  955.     if (self) {
  956.         [self doInit];
  957.     }
  958.     return self;
  959. }
  960.  
  961. - (void)doInit
  962. {
  963.     // Initialization code
  964.     _framesetter = NULL;
  965.     _styles = [[NSMutableDictionary alloc] init];
  966.     _URLs = [[NSMutableDictionary alloc] init];
  967.     _images = [[NSMutableArray alloc] init];
  968.     self.opaque = NO;
  969.     self.backgroundColor = [UIColor clearColor];
  970.     self.contentMode = UIViewContentModeRedraw;
  971.     [self setUserInteractionEnabled:YES];
  972.    
  973.     FTCoreTextStyle *defaultStyle = [FTCoreTextStyle styleWithName:FTCoreTextTagDefault];
  974.     [self addStyle:defaultStyle];
  975.    
  976.     FTCoreTextStyle *linksStyle = [defaultStyle copy];
  977.     linksStyle.color = [UIColor blueColor];
  978.     linksStyle.name = FTCoreTextTagLink;
  979.     [_styles setValue:linksStyle forKey:linksStyle.name];
  980.     [linksStyle release];
  981.    
  982.     _defaultsTags = [[NSMutableDictionary dictionaryWithObjectsAndKeys:FTCoreTextTagDefault, FTCoreTextTagDefault,
  983.                       FTCoreTextTagLink, FTCoreTextTagLink,
  984.                       FTCoreTextTagImage, FTCoreTextTagImage,
  985.                       FTCoreTextTagPage, FTCoreTextTagPage,
  986.                       FTCoreTextTagBullet, FTCoreTextTagBullet,
  987.                       nil] retain];
  988. }
  989.  
  990. - (void)dealloc
  991. {
  992.     if (_framesetter) CFRelease(_framesetter);
  993.     if (_path) CGPathRelease(_path);
  994.     [_rootNode release];
  995.     [_text release];
  996.     [_styles release];
  997.     [_processedString release];
  998.     [_URLs release];
  999.     [_images release];
  1000.     [_shadowColor release];
  1001.     [_attributedString release];
  1002.     [_defaultsTags release];
  1003.     [super dealloc];
  1004. }
  1005.  
  1006. #pragma mark - Custom Setters
  1007.  
  1008. - (void)setText:(NSString *)text
  1009. {
  1010.     [_text release];
  1011.     _text = [text retain];
  1012.     _coreTextViewFlags.textChangesMade = YES;
  1013.     [self didMakeChanges];
  1014.     if ([self superview]) [self setNeedsDisplay];
  1015. }
  1016.  
  1017. //only here to assure compatibility with previous versions
  1018. - (void)setStyles:(NSDictionary *)styles
  1019. {
  1020.     [_styles release];
  1021.     _styles = [styles mutableCopy];
  1022.     [self didMakeChanges];
  1023.     if ([self superview]) [self setNeedsDisplay];
  1024. }
  1025.  
  1026. - (void)setPath:(CGPathRef)path
  1027. {
  1028.     _path = CGPathRetain(path);
  1029.     [self didMakeChanges];
  1030.     if ([self superview]) [self setNeedsDisplay];
  1031. }
  1032.  
  1033. - (void)setShadowColor:(UIColor *)shadowColor
  1034. {
  1035.     [_shadowColor release];
  1036.     _shadowColor = [shadowColor retain];
  1037.     if ([self superview]) [self setNeedsDisplay];
  1038. }
  1039.  
  1040. - (void)setShadowOffset:(CGSize)shadowOffset
  1041. {
  1042.     _shadowOffset = shadowOffset;
  1043.     if ([self superview]) [self setNeedsDisplay];
  1044. }
  1045.  
  1046. #pragma mark - Custom Getters
  1047.  
  1048. //only here to assure compatibility with previous versions
  1049. - (NSArray *)stylesArray
  1050. {
  1051.     return [self styles];
  1052. }
  1053.  
  1054. - (NSArray *)styles
  1055. {
  1056.     return [_styles allValues];
  1057. }
  1058.  
  1059. - (NSAttributedString *)attributedString
  1060. {
  1061.     if (!_coreTextViewFlags.updatedAttrString) {
  1062.         _coreTextViewFlags.updatedAttrString = YES;
  1063.        
  1064.         if (_processedString == nil || _coreTextViewFlags.textChangesMade) {
  1065.             _coreTextViewFlags.textChangesMade = NO;
  1066.             [self processText];
  1067.         }
  1068.        
  1069.         if (_processedString) {
  1070.            
  1071.             NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:_processedString];
  1072.            
  1073.             for (FTCoreTextNode *node in [_rootNode allSubnodes]) {
  1074.                 [self applyStyle:node.style inRange:node.styleRange onString:&string];
  1075.             }
  1076.            
  1077.             [_attributedString release];
  1078.             _attributedString = string;
  1079.         }
  1080.     }
  1081.     return _attributedString;
  1082. }
  1083.  
  1084. #pragma mark - View lifecycle
  1085.  
  1086. /*!
  1087.  * @abstract draw the actual coretext on the context
  1088.  *
  1089.  */
  1090.  
  1091. - (void)drawRect:(CGRect)rect
  1092. {
  1093.     CGContextRef context = UIGraphicsGetCurrentContext();
  1094.    
  1095.     [self.backgroundColor setFill];
  1096.     CGContextFillRect(context, rect);
  1097.    
  1098.     if ([self isSystemUnder3_2]) {
  1099.        
  1100.         if (_processedString == nil || _coreTextViewFlags.textChangesMade) {
  1101.             _coreTextViewFlags.textChangesMade = NO;
  1102.             [self processText];
  1103.         }
  1104.        
  1105.         if (_shadowColor) {
  1106.             CGContextSetShadowWithColor(context, _shadowOffset, 0.f, _shadowColor.CGColor);
  1107.         }
  1108.        
  1109.         FTCoreTextStyle *defaultStyle = [_styles objectForKey:[self defaultTagNameForKey:FTCoreTextTagDefault]];
  1110.         [defaultStyle.color setFill];
  1111.         [_processedString drawInRect:rect
  1112.                             withFont:defaultStyle.font
  1113.                        lineBreakMode:UILineBreakModeWordWrap
  1114.                            alignment:UITextAlignmentFromCoreTextAlignment(defaultStyle.textAlignment)];
  1115.     }
  1116.     else {
  1117.         [self updateFramesetterIfNeeded];
  1118.        
  1119.         CGMutablePathRef mainPath = CGPathCreateMutable();
  1120.        
  1121.         if (!_path) {
  1122.             CGPathAddRect(mainPath, NULL, CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height));
  1123.         }
  1124.         else {
  1125.             CGPathAddPath(mainPath, NULL, _path);
  1126.         }
  1127.        
  1128.         CTFrameRef drawFrame = CTFramesetterCreateFrame(_framesetter, CFRangeMake(0, 0), mainPath, NULL);
  1129.        
  1130.         if (drawFrame == NULL) {
  1131.             NSLog(@"FTCoreText unable to render: %@", self.processedString);
  1132.         }
  1133.         else {
  1134.             //draw images
  1135.             if ([_images count] > 0) [self drawImages];
  1136.            
  1137.             if (_shadowColor) {
  1138.                 CGContextSetShadowWithColor(context, _shadowOffset, 0.f, _shadowColor.CGColor);
  1139.             }
  1140.            
  1141.             CGContextSetTextMatrix(context, CGAffineTransformIdentity);
  1142.             CGContextTranslateCTM(context, 0, self.bounds.size.height);
  1143.             CGContextScaleCTM(context, 1.0, -1.0);
  1144.             // draw text
  1145.             CTFrameDraw(drawFrame, context);
  1146.         }
  1147.         // cleanup
  1148.         if (drawFrame) CFRelease(drawFrame);
  1149.         CGPathRelease(mainPath);
  1150.     }
  1151. }
  1152.  
  1153. - (void)drawImages
  1154. {
  1155.     CGMutablePathRef mainPath = CGPathCreateMutable();
  1156.     if (!_path) {
  1157.         CGPathAddRect(mainPath, NULL, CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height));
  1158.     }
  1159.     else {
  1160.         CGPathAddPath(mainPath, NULL, _path);
  1161.     }
  1162.    
  1163.     CTFrameRef ctframe = CTFramesetterCreateFrame(_framesetter, CFRangeMake(0, 0), mainPath, NULL);
  1164.     CGPathRelease(mainPath);
  1165.    
  1166.     NSArray *lines = (NSArray *)CTFrameGetLines(ctframe);
  1167.     NSInteger lineCount = [lines count];
  1168.     CGPoint origins[lineCount];
  1169.    
  1170.     CTFrameGetLineOrigins(ctframe, CFRangeMake(0, 0), origins);
  1171.    
  1172.     FTCoreTextNode *imageNode = [_images objectAtIndex:0];
  1173.    
  1174.     for (int i = 0; i < lineCount; i++) {
  1175.         CGPoint baselineOrigin = origins[i];
  1176.         //the view is inverted, the y origin of the baseline is upside down
  1177.         baselineOrigin.y = CGRectGetHeight(self.frame) - baselineOrigin.y;
  1178.        
  1179.         CTLineRef line = (CTLineRef)[lines objectAtIndex:i];
  1180.         CFRange cfrange = CTLineGetStringRange(line);
  1181.        
  1182.         if (cfrange.location > imageNode.styleRange.location) {
  1183.             CGFloat ascent, descent;
  1184.             CGFloat lineWidth = CTLineGetTypographicBounds(line, &ascent, &descent, NULL);
  1185.            
  1186.             CGRect lineFrame = CGRectMake(baselineOrigin.x, baselineOrigin.y - ascent, lineWidth, ascent + descent);
  1187.            
  1188.             CTTextAlignment alignment = imageNode.style.textAlignment;
  1189.            
  1190.             UIImage *img = [UIImage imageNamed:@"rien.gif"];
  1191.             //UIImage *img = [UIImage imageNamed:imageNode.imageName];
  1192.             if (img) {
  1193.                 __block int x = 0;
  1194.                 if (alignment == kCTRightTextAlignment) x = (self.frame.size.width - img.size.width);
  1195.                 if (alignment == kCTCenterTextAlignment) x = ((self.frame.size.width - img.size.width) / 2);
  1196.                
  1197.                 __block CGRect frame = CGRectMake(x, (lineFrame.origin.y - img.size.height), img.size.width, img.size.height);
  1198.                
  1199.                 // adjusting frame
  1200.                
  1201.                 __block UIEdgeInsets insets = imageNode.style.paragraphInset;
  1202.                 if (alignment != kCTCenterTextAlignment) frame.origin.x = (alignment == kCTLeftTextAlignment)? insets.left : (self.frame.size.width - img.size.width - insets.right);
  1203.                 frame.origin.y += insets.top;
  1204.                 frame.size.width = ((insets.left + insets.right + img.size.width ) > self.frame.size.width)? self.frame.size.width : img.size.width;
  1205.                
  1206.                
  1207.                 UIImageView *imgNode = [[UIImageView alloc] init];
  1208.                 NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageNode.imageName]];
  1209.                 [imgNode setImageWithURLRequest:request placeholderImage:[UIImage imageNamed:@"rien.gif"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
  1210.                     if (alignment == kCTRightTextAlignment) x = (self.frame.size.width - image.size.width);
  1211.                     if (alignment == kCTCenterTextAlignment) x = ((self.frame.size.width - image.size.width) / 2);
  1212.                    
  1213.                     frame = CGRectMake(x, (lineFrame.origin.y - image.size.height), image.size.width, image.size.height);
  1214.                     if (alignment != kCTCenterTextAlignment) frame.origin.x = (alignment == kCTLeftTextAlignment)? insets.left : (self.frame.size.width - image.size.width - insets.right);
  1215.                     frame.origin.y += insets.top;
  1216.                     frame.size.width = ((insets.left + insets.right + image.size.width ) > self.frame.size.width)? self.frame.size.width : image.size.width;
  1217.                    
  1218.                    
  1219.                     frame = CGRectMake((((320-2*20)-image.size.width)/2), frame.origin.y, image.size.width, image.size.height);
  1220.                     NSLog(@"Frame : %f %f", frame.size.width, frame.size.height);
  1221.                     NSLog(@"Image : %f %f", image.size.width, image.size.height);
  1222.                     CGContextRef context = UIGraphicsGetCurrentContext();                      
  1223.                     CGContextSaveGState(context);
  1224.  
  1225.                     [image drawInRect:CGRectIntegral(frame)];
  1226.                 } failure:nil];
  1227.                 [imgNode.image drawInRect:CGRectIntegral(frame)];
  1228.             }
  1229.            
  1230.             NSInteger imageNodeIndex = [_images indexOfObject:imageNode];
  1231.             if (imageNodeIndex < [_images count] - 1) {
  1232.                 imageNode = [_images objectAtIndex:imageNodeIndex + 1];
  1233.             }
  1234.             else {
  1235.                 break;
  1236.             }
  1237.         }
  1238.     }
  1239.     CFRelease(ctframe);
  1240. }
  1241.  
  1242. #pragma mark User Interaction
  1243.  
  1244. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
  1245. {
  1246.     [super touchesEnded:touches withEvent:event];
  1247.    
  1248.     if (![self isSystemUnder3_2]) {
  1249.         if (self.delegate && ([self.delegate respondsToSelector:@selector(touchedData:inCoreTextView:)] || [self.delegate respondsToSelector:@selector(coreTextView:receivedTouchOnData:)])) {
  1250.             CGPoint point = [(UITouch *)[touches anyObject] locationInView:self];
  1251.             NSDictionary *data = [self dataForPoint:point];
  1252.             if (data) {
  1253.                 if ([self.delegate respondsToSelector:@selector(coreTextView:receivedTouchOnData:)]) {
  1254.                     [self.delegate coreTextView:self receivedTouchOnData:data];
  1255.                 }
  1256.                 else {
  1257.                     if ([self.delegate respondsToSelector:@selector(touchedData:inCoreTextView:)]) {
  1258.                         [self.delegate touchedData:data inCoreTextView:self];
  1259.                     }
  1260.                 }
  1261.             }
  1262.         }
  1263.     }
  1264. }
  1265.  
  1266. @end
  1267.  
  1268. @implementation NSString (FTCoreText)
  1269. - (NSString *)stringByAppendingTagName:(NSString *)tagName
  1270. {
  1271.     return [NSString stringWithFormat:@"<%@>%@</%@>", tagName, self, tagName];
  1272. }
  1273. @end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement