Skip to content

Commit

Permalink
Merge pull request Cocoanetics#39 from kzaragoza/master
Browse files Browse the repository at this point in the history
Patch for Issue Cocoanetics#38: Nested lists not supported
  • Loading branch information
odrobnik committed Jun 20, 2011
2 parents bd441ee + 979b2c8 commit c11f0e4
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 21 deletions.
11 changes: 10 additions & 1 deletion Classes/DTHTMLElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ typedef enum

CGFloat textScale;
CGSize size;

NSInteger _listDepth;
NSInteger _listCounter;

NSMutableArray *children;
}

@property (nonatomic, assign) DTHTMLElement *parent;
Expand All @@ -116,7 +121,9 @@ typedef enum
@property (nonatomic, assign) DTHTMLElementListStyle listStyle;
@property (nonatomic, assign) CGFloat textScale;
@property (nonatomic, assign) CGSize size;

@property (nonatomic, readonly) NSArray *children;
@property (nonatomic, readonly) NSInteger listDepth;
@property (nonatomic) NSInteger listCounter;


- (NSAttributedString *)attributedString;
Expand All @@ -128,4 +135,6 @@ typedef enum

- (NSString *)path;

- (void)addChild:(DTHTMLElement *)child;

@end
81 changes: 81 additions & 0 deletions Classes/DTHTMLElement.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ - (id)init
{
_isInline = -1;
_isMeta = -1;
_listDepth = -1;
_listCounter = NSIntegerMin;
children = [NSMutableArray array];
}

return self;
Expand Down Expand Up @@ -674,6 +677,12 @@ - (void)addAdditionalAttribute:(id)attribute forKey:(id)key
[_additionalAttributes setObject:attribute forKey:key];
}

- (void)addChild:(DTHTMLElement *)child
{
child.parent = self;
[children addObject:child];
}

#pragma mark Copying

- (id)copyWithZone:(NSZone *)zone
Expand Down Expand Up @@ -801,6 +810,77 @@ - (NSString *)path
return @"root";
}

- (NSInteger)listDepth
{
if (_listDepth < 0)
{
// See if this is a list related element.
if ([tagName isEqualToString:@"ol"] || [tagName isEqualToString:@"ul"] || [tagName isEqualToString:@"li"])
{
// Walk up the tree to the root. Increment the count every time we hit an OL or UL tag
// so we have our nesting count correct.
DTHTMLElement *elem = self;
_listDepth = 0;
while (elem.parent) {
NSString *tag = elem.parent.tagName;
if ([tag isEqualToString:@"ol"] || [tag isEqualToString:@"ul"])
{
_listDepth++;
}
elem = elem.parent;
}
}
else {
// We're not a list element, so set the depth to zero.
_listDepth = 0;
}
}
return _listDepth;
}

- (NSInteger)listCounter
{
// If the counter is set to NSIntegerMin, it hasn't been calculated or manually set.
// Calculate it on demand.
if (_listCounter == NSIntegerMin)
{
// See if this is an LI. No other elements get a counter.
if ([tagName isEqualToString:@"li"])
{
// Count the number of LI elements in the parent until we reach self. That's our counter.
NSInteger counter = 1;
NSUInteger numChildren = [parent.children count];
for (NSInteger i = 0; i < numChildren; i++)
{
// We walk through the children and check for LI elements just in case someone
// slipped us some bad HTML.
DTHTMLElement *child = [parent.children objectAtIndex:i];
if (child != self && [child.tagName isEqualToString:@"li"])
{
// Add one to the last LI's value just in case its listCounter property got overridden and
// set to something other than its natural order in the elements list.
counter = child.listCounter + 1;
}
else
{
break;
}
}
_listCounter = counter;
}
else
{
_listCounter = 0;
}
}
return _listCounter;
}

- (void)setListCounter:(NSInteger)count
{
_listCounter = count;
}

@synthesize parent;
@synthesize fontDescriptor;
@synthesize paragraphStyle;
Expand All @@ -826,6 +906,7 @@ - (NSString *)path
@synthesize size;

@synthesize fontCache = _fontCache;
@synthesize children;



Expand Down
39 changes: 20 additions & 19 deletions Classes/NSAttributedString+HTML.m
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,9 @@ - (id)initWithHTML:(NSData *)data options:(NSDictionary *)options documentAttrib
#if ALLOW_IPHONE_SPECIAL_CASES
CGFloat nextParagraphAdditionalSpaceBefore = 0.0;
#endif
NSInteger listCounter = 0; // Unordered, set to 1 to get ordered list
BOOL needsListItemStart = NO;
BOOL needsNewLineBefore = NO;


// we cannot skip any characters, NLs turn into spaces and multi-spaces get compressed to singles
NSScanner *scanner = [NSScanner scannerWithString:htmlString];
scanner.charactersToBeSkipped = nil;
Expand Down Expand Up @@ -193,8 +191,8 @@ - (id)initWithHTML:(NSData *)data options:(NSDictionary *)options documentAttrib
DTHTMLElement *parent = currentTag;
currentTag = [[currentTag copy] autorelease];
currentTag.tagName = tagName;
currentTag.parent = parent;
currentTag.textScale = textScale;
[parent addChild:currentTag];

// convert CSS Styles into our own style
NSString *styleString = [tagAttributesDict objectForKey:@"style"];
Expand Down Expand Up @@ -442,24 +440,25 @@ - (id)initWithHTML:(NSData *)data options:(NSDictionary *)options documentAttrib
{
needsListItemStart = YES;
currentTag.paragraphStyle.paragraphSpacing = 0;

#if ALLOW_IPHONE_SPECIAL_CASES
currentTag.paragraphStyle.headIndent += 27.0 * textScale;
CGFloat indentSize = 27.0 * textScale;
#else
currentTag.paragraphStyle.headIndent += 36.0 * textScale;
CGFloat indentSize = 36.0 * textScale;
#endif

CGFloat indentHang = indentSize;

currentTag.paragraphStyle.headIndent += indentSize;
currentTag.paragraphStyle.firstLineIndent = currentTag.paragraphStyle.headIndent - indentHang;

[currentTag.paragraphStyle addTabStopAtPosition:currentTag.paragraphStyle.headIndent - 5.0*textScale alignment:kCTRightTextAlignment];

[currentTag.paragraphStyle addTabStopAtPosition:currentTag.paragraphStyle.headIndent alignment: kCTLeftTextAlignment];
}
else
{
needsListItemStart = NO;

if (listCounter)
{
listCounter++;
}
}

}
Expand Down Expand Up @@ -501,25 +500,27 @@ - (id)initWithHTML:(NSData *)data options:(NSDictionary *)options documentAttrib
{
if (tagOpen)
{
listCounter = 1;
needsNewLineBefore = YES;
}
else
{
#if ALLOW_IPHONE_SPECIAL_CASES
nextParagraphAdditionalSpaceBefore = defaultFontDescriptor.pointSize;
#if ALLOW_IPHONE_SPECIAL_CASES
if (currentTag.listDepth < 1)
nextParagraphAdditionalSpaceBefore = defaultFontDescriptor.pointSize;
#endif
}
}
else if ([tagName isEqualToString:@"ul"])
{
if (tagOpen)
{
listCounter = 0;
needsNewLineBefore = YES;
}
else
{
#if ALLOW_IPHONE_SPECIAL_CASES
nextParagraphAdditionalSpaceBefore = defaultFontDescriptor.pointSize;
#if ALLOW_IPHONE_SPECIAL_CASES
if (currentTag.listDepth < 1)
nextParagraphAdditionalSpaceBefore = defaultFontDescriptor.pointSize;
#endif
}
}
Expand Down Expand Up @@ -853,9 +854,9 @@ - (id)initWithHTML:(NSData *)data options:(NSDictionary *)options documentAttrib
}

// if we start a list, then we wait until we have actual text
if (needsListItemStart && ![tagContents isEqualToString:@" "])
if (needsListItemStart && [tagContents length] > 0 && ![tagContents isEqualToString:@" "])
{
NSAttributedString *prefixString = [currentTag prefixForListItemWithCounter:listCounter];
NSAttributedString *prefixString = [currentTag prefixForListItemWithCounter:currentTag.listCounter];

if (prefixString)
{
Expand Down
6 changes: 6 additions & 0 deletions CoreTextExtensions.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; };
288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765A40DF7441C002DB57D /* CoreGraphics.framework */; };
28D7ACF80DDB3853001CB0EB /* DemoTextViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28D7ACF70DDB3853001CB0EB /* DemoTextViewController.m */; };
4F02B9BF13AA998200ED7AB1 /* ListTest.html in Resources */ = {isa = PBXBuildFile; fileRef = 4F02B9BE13AA998200ED7AB1 /* ListTest.html */; };
4F02B9C113AA9ACC00ED7AB1 /* ListTest.html in Resources */ = {isa = PBXBuildFile; fileRef = 4F02B9BE13AA998200ED7AB1 /* ListTest.html */; };
A704C93A13901FDB0045CFC6 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A704C93913901FDB0045CFC6 /* ImageIO.framework */; };
A70F5511138A746200D642C5 /* DTCoreTextFontCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = A70F5510138A746200D642C5 /* DTCoreTextFontCollection.m */; };
A70F5512138A746200D642C5 /* DTCoreTextFontCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = A70F5510138A746200D642C5 /* DTCoreTextFontCollection.m */; };
Expand Down Expand Up @@ -119,6 +121,7 @@
288765A40DF7441C002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
28D7ACF60DDB3853001CB0EB /* DemoTextViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoTextViewController.h; sourceTree = "<group>"; };
28D7ACF70DDB3853001CB0EB /* DemoTextViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoTextViewController.m; sourceTree = "<group>"; };
4F02B9BE13AA998200ED7AB1 /* ListTest.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = ListTest.html; sourceTree = "<group>"; };
6A5A32C512DD10BB0019AAF1 /* UIColorHTMLTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIColorHTMLTest.h; sourceTree = "<group>"; };
6A5A32C612DD10BB0019AAF1 /* UIColorHTMLTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIColorHTMLTest.m; sourceTree = "<group>"; };
6A5A32CB12DD11740019AAF1 /* NSAttributedStringHTMLTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSAttributedStringHTMLTest.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -409,6 +412,7 @@
A7EA5FAF12E1AC7F0045411B /* CurrentTest.html */,
A7E5D0F4135EBCD0004B6C67 /* Subviews.html */,
A7BF4B6C13970D9800491C6F /* DTCoreTextFontOverrides.plist */,
4F02B9BE13AA998200ED7AB1 /* ListTest.html */,
);
path = Resources;
sourceTree = "<group>";
Expand Down Expand Up @@ -484,6 +488,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4F02B9C113AA9ACC00ED7AB1 /* ListTest.html in Resources */,
A7F2A73C1356F4F30097BF31 /* CustomFont.html in Resources */,
B23558F712E04C8E005EA62F /* README.html in Resources */,
B235594412E05090005EA62F /* Snippets.plist in Resources */,
Expand Down Expand Up @@ -522,6 +527,7 @@
A7F2A73B1356F3950097BF31 /* CustomFont.html in Resources */,
A7E5D0F5135EBCD0004B6C67 /* Subviews.html in Resources */,
A7BF4B6D13970D9800491C6F /* DTCoreTextFontOverrides.plist in Resources */,
4F02B9BF13AA998200ED7AB1 /* ListTest.html in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
77 changes: 77 additions & 0 deletions Resources/ListTest.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<p>This page demonstrates support for unordered, ordered, and nested lists.</p>

<h2>Unordered Lists</h2>
<ul>
<li>First item</li>
<li>Second item
<ul>
<li>Nested unordered lists</li>
<li>Nested line 2</li>
<li>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed pulvinar sollicitudin tellus, vitae congue arcu condimentum ut. Fusce sagittis iaculis sem ut bibendum. Sed a ipsum purus, sit amet elementum diam. Fusce sapien sapien, cursus sit amet auctor sit amet, ultricies et lorem. Pellentesque dignissim gravida nunc, id porta felis dapibus nec. Proin ultrices, dui vel tempor imperdiet, mi libero congue orci, at auctor ante quam et lorem. Mauris imperdiet molestie leo at pellentesque. Maecenas nec nulla sed felis tristique sagittis. Suspendisse sit amet lectus vitae felis ultrices interdum vel nec ipsum. Proin elementum lectus sed nisi varius id euismod mauris lobortis. Phasellus eget purus ut nunc vulputate tincidunt sed ac purus. Aenean iaculis eros sed odio faucibus commodo. Morbi eu metus risus, ut convallis urna. Cras aliquet urna non diam rhoncus posuere. Aliquam est risus, ullamcorper ac malesuada eget, molestie vitae mauris. Nam volutpat, eros at scelerisque gravida, neque lorem interdum sapien, sed sollicitudin odio neque condimentum lacus. Curabitur congue tortor quis quam mattis lobortis. Quisque a ligula ipsum, eget hendrerit elit. Ut nec nibh nisi. Donec sit amet tortor massa.</li>
<li>
<ul>
<li>Nested list the third.</li>
<li>Nested list the third.</li>
</ul>
</li>
</ul>
</li>
<li>Last item</li>
</ul>


<h2>Ordered Lists</h2>
<ol>
<li>First item</li>
<li>Second item
<ol>
<li>Nested unordered lists</li>
<li>Nested line 2</li>
<li>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed pulvinar sollicitudin tellus, vitae congue arcu condimentum ut. Fusce sagittis iaculis sem ut bibendum. Sed a ipsum purus, sit amet elementum diam. Fusce sapien sapien, cursus sit amet auctor sit amet, ultricies et lorem. Pellentesque dignissim gravida nunc, id porta felis dapibus nec. Proin ultrices, dui vel tempor imperdiet, mi libero congue orci, at auctor ante quam et lorem. Mauris imperdiet molestie leo at pellentesque. Maecenas nec nulla sed felis tristique sagittis. Suspendisse sit amet lectus vitae felis ultrices interdum vel nec ipsum. Proin elementum lectus sed nisi varius id euismod mauris lobortis. Phasellus eget purus ut nunc vulputate tincidunt sed ac purus. Aenean iaculis eros sed odio faucibus commodo. Morbi eu metus risus, ut convallis urna. Cras aliquet urna non diam rhoncus posuere. Aliquam est risus, ullamcorper ac malesuada eget, molestie vitae mauris. Nam volutpat, eros at scelerisque gravida, neque lorem interdum sapien, sed sollicitudin odio neque condimentum lacus. Curabitur congue tortor quis quam mattis lobortis. Quisque a ligula ipsum, eget hendrerit elit. Ut nec nibh nisi. Donec sit amet tortor massa.</li>
<li>
<ol>
<li>Nested list the third.</li>
<li>Nested list the third.</li>
</ol>
</li>
</ol>
</li>
<li>Last item</li>
</ol>

<h2>Mixed Lists</h2>
<ul>
<li>First item</li>
<li>Second item
<ol>
<li>Nested unordered lists</li>
<li>Nested line 2</li>
<li>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed pulvinar sollicitudin tellus, vitae congue arcu condimentum ut. Fusce sagittis iaculis sem ut bibendum. Sed a ipsum purus, sit amet elementum diam. Fusce sapien sapien, cursus sit amet auctor sit amet, ultricies et lorem. Pellentesque dignissim gravida nunc, id porta felis dapibus nec. Proin ultrices, dui vel tempor imperdiet, mi libero congue orci, at auctor ante quam et lorem. Mauris imperdiet molestie leo at pellentesque. Maecenas nec nulla sed felis tristique sagittis. Suspendisse sit amet lectus vitae felis ultrices interdum vel nec ipsum. Proin elementum lectus sed nisi varius id euismod mauris lobortis. Phasellus eget purus ut nunc vulputate tincidunt sed ac purus. Aenean iaculis eros sed odio faucibus commodo. Morbi eu metus risus, ut convallis urna. Cras aliquet urna non diam rhoncus posuere. Aliquam est risus, ullamcorper ac malesuada eget, molestie vitae mauris. Nam volutpat, eros at scelerisque gravida, neque lorem interdum sapien, sed sollicitudin odio neque condimentum lacus. Curabitur congue tortor quis quam mattis lobortis. Quisque a ligula ipsum, eget hendrerit elit. Ut nec nibh nisi. Donec sit amet tortor massa.</li>
<li>
<ul>
<li>Nested list the third.</li>
<li>Nested list the third.</li>
</ul>
</li>
</ol>
</li>
<li>Last item</li>
</ul>

<ol>
<li>First item</li>
<li>Second item
<ul>
<li>Nested unordered lists</li>
<li>Nested line 2</li>
<li>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed pulvinar sollicitudin tellus, vitae congue arcu condimentum ut. Fusce sagittis iaculis sem ut bibendum. Sed a ipsum purus, sit amet elementum diam. Fusce sapien sapien, cursus sit amet auctor sit amet, ultricies et lorem. Pellentesque dignissim gravida nunc, id porta felis dapibus nec. Proin ultrices, dui vel tempor imperdiet, mi libero congue orci, at auctor ante quam et lorem. Mauris imperdiet molestie leo at pellentesque. Maecenas nec nulla sed felis tristique sagittis. Suspendisse sit amet lectus vitae felis ultrices interdum vel nec ipsum. Proin elementum lectus sed nisi varius id euismod mauris lobortis. Phasellus eget purus ut nunc vulputate tincidunt sed ac purus. Aenean iaculis eros sed odio faucibus commodo. Morbi eu metus risus, ut convallis urna. Cras aliquet urna non diam rhoncus posuere. Aliquam est risus, ullamcorper ac malesuada eget, molestie vitae mauris. Nam volutpat, eros at scelerisque gravida, neque lorem interdum sapien, sed sollicitudin odio neque condimentum lacus. Curabitur congue tortor quis quam mattis lobortis. Quisque a ligula ipsum, eget hendrerit elit. Ut nec nibh nisi. Donec sit amet tortor massa.</li>
<li>
<ol>
<li>Nested list the third.</li>
<li>Nested list the third.</li>
</ol>
</li>
</ul>
</li>
<li>Last item</li>
</ol>
10 changes: 9 additions & 1 deletion Resources/Snippets.plist
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,18 @@
<dict>
<key>Description</key>
<string>Custom subviews can be affixed over any glyph run.</string>
<key>File</key>
<string>Subviews.html</string>
<key>Title</key>
<string>Custom Subviews</string>
</dict>
<dict>
<key>Description</key>
<string>Demo of nested HTML lists</string>
<key>File</key>
<string>Subviews.html</string>
<string>ListTest.html</string>
<key>Title</key>
<string>Nested Lists</string>
</dict>
<dict>
<key>Description</key>
Expand Down

0 comments on commit c11f0e4

Please sign in to comment.