diff --git a/Core/Source/DTCoreTextLayoutFrameAccessibilityElementGenerator.m b/Core/Source/DTCoreTextLayoutFrameAccessibilityElementGenerator.m index f30428b74..87e1a292b 100644 --- a/Core/Source/DTCoreTextLayoutFrameAccessibilityElementGenerator.m +++ b/Core/Source/DTCoreTextLayoutFrameAccessibilityElementGenerator.m @@ -77,7 +77,7 @@ - (DTAccessibilityElement *)accessibilityElementForTextInAttributedString:(NSAtt element.accessibilityLabel = text; element.localCoordinateAccessibilityFrame = [self frameForRuns:runs]; - // We're trying to keep the accessibility frame behavior consistent with UIWebView, which seems to do a union of the rects for all the runs composing a single accessibility group, + // We're trying to keep the accessibility frame behavior consistent with web view, which seems to do a union of the rects for all the runs composing a single accessibility group, // even if that spans across multiple lines. Set the local coordinate activation point to support multi-line links. A link that is at the end of one line and // wraps to the beginning of the next would have a rect that's the size of both lines combined. The center of that rect would be outside the hit areas for either of the // runs individually, so we set the accessibility activation point to be the origin of the first run. diff --git a/Core/Source/DTHTMLAttributedStringBuilder.m b/Core/Source/DTHTMLAttributedStringBuilder.m index 9e3ce097b..9632a1078 100644 --- a/Core/Source/DTHTMLAttributedStringBuilder.m +++ b/Core/Source/DTHTMLAttributedStringBuilder.m @@ -422,8 +422,11 @@ - (void)_registerTagStartHandlers } NSURL *link = [NSURL URLWithString:cleanString]; - if (link == nil) { - link = [NSURL URLWithString:[cleanString stringByURLEncoding]]; + + if (link == nil) + { + cleanString = [cleanString stringByEncodingNonASCIICharacters]; + link = [NSURL URLWithString:cleanString]; } // deal with relative URL diff --git a/Core/Source/DTHTMLElement.m b/Core/Source/DTHTMLElement.m index 1a1bcdfd8..156decd11 100644 --- a/Core/Source/DTHTMLElement.m +++ b/Core/Source/DTHTMLElement.m @@ -1572,15 +1572,15 @@ - (void)interpretAttributes if ([directionStr isEqualToString:@"rtl"]) { - _paragraphStyle.baseWritingDirection = NSWritingDirectionRightToLeft; + _paragraphStyle.baseWritingDirection = kCTWritingDirectionRightToLeft; } else if ([directionStr isEqualToString:@"ltr"]) { - _paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + _paragraphStyle.baseWritingDirection = kCTWritingDirectionLeftToRight; } else if ([directionStr isEqualToString:@"auto"]) { - _paragraphStyle.baseWritingDirection = NSWritingDirectionNatural; // that's also default + _paragraphStyle.baseWritingDirection = kCTWritingDirectionNatural; // that's also default } else { diff --git a/Core/Source/NSCharacterSet+HTML.h b/Core/Source/NSCharacterSet+HTML.h index 282ab327f..17a354e1b 100644 --- a/Core/Source/NSCharacterSet+HTML.h +++ b/Core/Source/NSCharacterSet+HTML.h @@ -76,4 +76,11 @@ */ + (NSCharacterSet *)cssLengthUnitCharacterSet; +/** +Characterset of ASCII Characters +@returns An NSCharacterSet +*/ + ++ (NSCharacterSet *)ASCIICharacterSet; + @end diff --git a/Core/Source/NSCharacterSet+HTML.m b/Core/Source/NSCharacterSet+HTML.m index 65c19a5a3..a3f5d1472 100644 --- a/Core/Source/NSCharacterSet+HTML.m +++ b/Core/Source/NSCharacterSet+HTML.m @@ -17,7 +17,7 @@ static NSCharacterSet *_cssStyleAttributeNameCharacterSet = nil; static NSCharacterSet *_cssLengthValueCharacterSet = nil; static NSCharacterSet *_cssLengthUnitCharacterSet = nil; - +static NSCharacterSet *_asciiCharacterSet = nil; @implementation NSCharacterSet (HTML) @@ -136,4 +136,17 @@ + (NSCharacterSet *)cssLengthUnitCharacterSet return _cssLengthUnitCharacterSet; } ++ (NSCharacterSet *)ASCIICharacterSet +{ + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + NSMutableCharacterSet *tmpSet = [NSMutableCharacterSet new]; + [tmpSet addCharactersInRange:NSMakeRange(32, 96)]; + _asciiCharacterSet = [tmpSet copy]; + }); + + return _asciiCharacterSet; +} + @end diff --git a/Core/Source/NSScanner+HTML.m b/Core/Source/NSScanner+HTML.m index e8f1a48e4..79f9ab761 100644 --- a/Core/Source/NSScanner+HTML.m +++ b/Core/Source/NSScanner+HTML.m @@ -57,7 +57,7 @@ - (BOOL)scanCSSAttribute:(NSString * __autoreleasing*)name value:(id __autorelea NSMutableArray *results = [NSMutableArray array]; BOOL nextIterationAddsNewEntry = YES; - while (![self isAtEnd] && ![self scanString:@";" intoString:NULL]) + while (![self isAtEnd] && ![self scanString:@";" intoString:NULL] && ![self scanString:@"'';" intoString:NULL] && ![self scanString:@"\"\";" intoString:NULL]) { // skip whitespace [self scanCharactersFromSet:whiteCharacterSet intoString:NULL]; diff --git a/Core/Source/NSString+HTML.h b/Core/Source/NSString+HTML.h index 6f0263cf8..65ae324df 100644 --- a/Core/Source/NSString+HTML.h +++ b/Core/Source/NSString+HTML.h @@ -79,4 +79,9 @@ */ - (NSString *)stringByAddingAppleConvertedSpace; +/** +Percent-encodes all characters outside the normal ASCII range +*/ +- (NSString *)stringByEncodingNonASCIICharacters; + @end diff --git a/Core/Source/NSString+HTML.m b/Core/Source/NSString+HTML.m index 0216ddaf0..3ad08d0c8 100644 --- a/Core/Source/NSString+HTML.m +++ b/Core/Source/NSString+HTML.m @@ -9,6 +9,7 @@ #import "NSString+HTML.h" #import "NSCharacterSet+HTML.h" #import "DTCoreTextConstants.h" +#import static NSDictionary *entityLookup = nil; static NSDictionary *entityReverseLookup = nil; @@ -798,4 +799,13 @@ - (NSString *)stringByAddingAppleConvertedSpace return output; } +- (NSString *)stringByEncodingNonASCIICharacters +{ +#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_7_0 + return [self stringByURLEncoding]; +#else + return [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet ASCIICharacterSet]]; +#endif +} + @end diff --git a/DTCoreText.podspec b/DTCoreText.podspec index 2eb6e72fd..d2dcc967c 100644 --- a/DTCoreText.podspec +++ b/DTCoreText.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'DTCoreText' - spec.version = '1.6.23' + spec.version = '1.6.24' spec.platforms = {:ios => '4.3', :tvos => '9.0' } spec.license = 'BSD' spec.source = { :git => 'https://github.com/Cocoanetics/DTCoreText.git', :tag => spec.version.to_s } diff --git a/DTCoreText.xcodeproj/project.pbxproj b/DTCoreText.xcodeproj/project.pbxproj index f6ca1cdae..0fdad9724 100644 --- a/DTCoreText.xcodeproj/project.pbxproj +++ b/DTCoreText.xcodeproj/project.pbxproj @@ -216,6 +216,7 @@ A76E5B4912DD9AF500711782 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A76E5B4812DD9AF500711782 /* QuartzCore.framework */; }; A776DBE81716A8EE00E71F36 /* NSStringParagraphTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A776DBE71716A8EE00E71F36 /* NSStringParagraphTest.m */; }; A77A3E421779BF04000B290B /* NSMutableAttributedStringHTMLTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A77A3E411779BF03000B290B /* NSMutableAttributedStringHTMLTest.m */; }; + A786FBAB247BFC1D00890B3D /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A786FB91247BFC1D00890B3D /* WebKit.framework */; }; A788CA3A14863EF100E1AFD9 /* Alignment.html in Resources */ = {isa = PBXBuildFile; fileRef = A788CA1B14863EF100E1AFD9 /* Alignment.html */; }; A788CA3B14863EF100E1AFD9 /* APOD.html in Resources */ = {isa = PBXBuildFile; fileRef = A788CA1C14863EF100E1AFD9 /* APOD.html */; }; A788CA3C14863EF100E1AFD9 /* ArabicTest.html in Resources */ = {isa = PBXBuildFile; fileRef = A788CA1D14863EF100E1AFD9 /* ArabicTest.html */; }; @@ -882,6 +883,7 @@ A77A3E411779BF03000B290B /* NSMutableAttributedStringHTMLTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSMutableAttributedStringHTMLTest.m; sourceTree = ""; }; A783CE6717D11D0100C84C28 /* CSSOOMCrash.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = CSSOOMCrash.plist; sourceTree = ""; }; A785701C17FAA69D0080AB0A /* DTFoundation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = DTFoundation.xcodeproj; path = DTFoundation/DTFoundation.xcodeproj; sourceTree = ""; }; + A786FB91247BFC1D00890B3D /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.5.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; }; A788C91014863E8700E1AFD9 /* DTAttributedTextCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTAttributedTextCell.h; sourceTree = ""; }; A788C91114863E8700E1AFD9 /* DTAttributedTextCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = DTAttributedTextCell.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; A788C91214863E8700E1AFD9 /* DTAttributedTextContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTAttributedTextContentView.h; sourceTree = ""; }; @@ -1082,6 +1084,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + A786FBAB247BFC1D00890B3D /* WebKit.framework in Frameworks */, A7949A0014C6256B00A8CCDE /* libxml2.dylib in Frameworks */, A75C6C72141798CE00AEE350 /* MobileCoreServices.framework in Frameworks */, A7D29D661CB68D470068F043 /* DTCoreText.framework in Frameworks */, @@ -1194,6 +1197,7 @@ 29B97323FDCFA39411CA2CEA /* Frameworks */ = { isa = PBXGroup; children = ( + A786FB91247BFC1D00890B3D /* WebKit.framework */, A74A7AAC1B617250004163BE /* QuartzCore.framework */, A74A7AAA1B617227004163BE /* CoreGraphics.framework */, A74A7AA81B617222004163BE /* UIKit.framework */, @@ -3258,7 +3262,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1.6.23; + CURRENT_PROJECT_VERSION = 1.6.24; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; GCC_C_LANGUAGE_STANDARD = c99; @@ -3400,7 +3404,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1.6.23; + CURRENT_PROJECT_VERSION = 1.6.24; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; @@ -3456,7 +3460,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1.6.23; + CURRENT_PROJECT_VERSION = 1.6.24; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; GCC_C_LANGUAGE_STANDARD = c99; diff --git a/Demo/Resources/README.html b/Demo/Resources/README.html index 07951297f..e84351e59 100644 --- a/Demo/Resources/README.html +++ b/Demo/Resources/README.html @@ -1,7 +1,7 @@

NSAttributedString HTML Additions

Introduction

This project aims to duplicate the methods present on Mac OSX which allow creation of NSAttributedString from HTML code. -This is useful for drawing simple rich text - like this document - without having to use a UIWebView.

+This is useful for drawing simple rich text - like this document - without having to use a web view.

Hi! I'm Oliver and I appreciate your help!

Features

@@ -30,4 +30,4 @@

Please Help!

If you find brief test cases where the created NSAttributedString differs from the version on OSX please send them to us!

Also there are many small things that you could help this project with. You can either implement these yourself or sponsor their development.

Follow @cocoanetics on Twitter

-

This code is covered by a BSD License. © 2011 Oliver Drobnik

\ No newline at end of file +

This code is covered by a BSD License. © 2011 Oliver Drobnik

diff --git a/Demo/Source/DemoTextViewController.m b/Demo/Source/DemoTextViewController.m index f4b0be741..849024d03 100644 --- a/Demo/Source/DemoTextViewController.m +++ b/Demo/Source/DemoTextViewController.m @@ -630,7 +630,14 @@ - (void)linkPushed:(DTLinkButton *)button if ([[UIApplication sharedApplication] canOpenURL:[URL absoluteURL]]) { - [[UIApplication sharedApplication] openURL:[URL absoluteURL]]; + if (@available(iOS 10.0, *)) { + [[UIApplication sharedApplication] openURL:[URL absoluteURL] options:@{} completionHandler:nil]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [[UIApplication sharedApplication] openURL:[URL absoluteURL]]; +#pragma clang diagnostic pop + } } else { @@ -648,11 +655,22 @@ - (void)linkPushed:(DTLinkButton *)button } } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex +#pragma clang diagnostic pop { if (buttonIndex != actionSheet.cancelButtonIndex) { - [[UIApplication sharedApplication] openURL:[self.lastActionLink absoluteURL]]; + if (@available(iOS 10.0, *)) { + [[UIApplication sharedApplication] openURL:[self.lastActionLink absoluteURL] options:@{} completionHandler:nil]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [[UIApplication sharedApplication] openURL:[self.lastActionLink absoluteURL]]; +#pragma clang diagnostic pop + } } } @@ -666,8 +684,28 @@ - (void)linkLongPressed:(UILongPressGestureRecognizer *)gesture if ([[UIApplication sharedApplication] canOpenURL:[button.URL absoluteURL]]) { - UIActionSheet *action = [[UIActionSheet alloc] initWithTitle:[[button.URL absoluteURL] description] delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Open in Safari", nil]; - [action showFromRect:button.frame inView:button.superview animated:YES]; + if (@available(iOS 8.0, *)) { + UIAlertController *ac = [UIAlertController alertControllerWithTitle:[[button.URL absoluteURL] description] + message:nil + preferredStyle:UIAlertControllerStyleActionSheet]; + [ac addAction:[UIAlertAction actionWithTitle:@"Open in Safari" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + [[UIApplication sharedApplication] openURL:[self.lastActionLink absoluteURL] options:@{} completionHandler:nil]; + }]]; + + [ac addAction:[UIAlertAction actionWithTitle:@"Cancel" + style:UIAlertActionStyleCancel + handler:nil]]; + + [self presentViewController:ac animated:YES completion:nil]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + UIActionSheet *action = [[UIActionSheet alloc] initWithTitle:[[button.URL absoluteURL] description] delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Open in Safari", nil]; + [action showFromRect:button.frame inView:button.superview animated:YES]; +#pragma clang diagnostic pop + } } } } diff --git a/Demo/Source/DemoWebVideoView.h b/Demo/Source/DemoWebVideoView.h index 0408e7397..03d0a6540 100644 --- a/Demo/Source/DemoWebVideoView.h +++ b/Demo/Source/DemoWebVideoView.h @@ -7,6 +7,7 @@ // #import +#import @class DemoWebVideoView; @class DTTextAttachment; @@ -33,11 +34,11 @@ /** The class represents a custom subview for use in to represent an embedded video. - Embedded videos work by loading the video URL in a UIWebView which iOS then replaces with the built-in media player view. The URL of the embed script depends on the service and needs to be added to the webView:shouldStartLoadWithRequest:navigationType:. You want to allow the URL for the embed script, but disallow all other requests which for example occur if a user taps on the YouTube logo. If you were to allow this type of navigation then the YouTube website would be loaded in the video view. For these scenarios there is the videoView:shouldOpenExternalURL: method in DemoWebVideoViewDelegate. If you respond with `YES` (which is default if the method is not implemented) then the URL will be opened in Safari. + Embedded videos work by loading the video URL in a web view which iOS then replaces with the built-in media player view. The URL of the embed script depends on the service and needs to be added to the webView:shouldStartLoadWithRequest:navigationType:. You want to allow the URL for the embed script, but disallow all other requests which for example occur if a user taps on the YouTube logo. If you were to allow this type of navigation then the YouTube website would be loaded in the video view. For these scenarios there is the videoView:shouldOpenExternalURL: method in DemoWebVideoViewDelegate. If you respond with `YES` (which is default if the method is not implemented) then the URL will be opened in Safari. To add additional video services please add them in the mentioned location and submit a pull request for the addition. */ -@interface DemoWebVideoView : UIView +@interface DemoWebVideoView : UIView /** The delegate of the video view diff --git a/Demo/Source/DemoWebVideoView.m b/Demo/Source/DemoWebVideoView.m index b74ef5acc..498beb5a5 100644 --- a/Demo/Source/DemoWebVideoView.m +++ b/Demo/Source/DemoWebVideoView.m @@ -22,7 +22,7 @@ @implementation DemoWebVideoView DT_WEAK_VARIABLE id _delegate; - UIWebView *_webView; + WKWebView *_webView; } - (id)initWithFrame:(CGRect)frame @@ -32,25 +32,20 @@ - (id)initWithFrame:(CGRect)frame { self.userInteractionEnabled = YES; - _webView = [[UIWebView alloc] initWithFrame:self.bounds]; + _webView = [[WKWebView alloc] initWithFrame:self.bounds]; _webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self addSubview:_webView]; [self disableScrolling]; - _webView.delegate = self; - - if ([_webView respondsToSelector:@selector(setAllowsInlineMediaPlayback:)]) - { - _webView.allowsInlineMediaPlayback = YES; - } + _webView.navigationDelegate = self; } return self; } - (void)dealloc { - _webView.delegate = nil; + _webView.navigationDelegate = nil; } @@ -72,20 +67,24 @@ - (void)disableScrolling } } -#pragma mark UIWebViewDelegate +#pragma mark WKWebViewDelegate -- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { + NSURLRequest *request = navigationAction.request; + // allow the embed request for YouTube if (NSNotFound != [[[request URL] absoluteString] rangeOfString:@"www.youtube.com/embed/"].location) { - return YES; + decisionHandler(WKNavigationActionPolicyAllow); + return; } // allow the embed request for DailyMotion Cloud if (NSNotFound != [[[request URL] absoluteString] rangeOfString:@"api.dmcloud.net/player/embed/"].location) { - return YES; + decisionHandler(WKNavigationActionPolicyAllow); + return; } BOOL shouldOpenExternalURL = YES; @@ -98,11 +97,18 @@ - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *) #if !defined(DT_APP_EXTENSIONS) if (shouldOpenExternalURL) { - [[UIApplication sharedApplication] openURL:[request URL]]; + if (@available(iOS 10.0, *)) { + [[UIApplication sharedApplication] openURL:[request URL] options:@{} completionHandler:nil]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [[UIApplication sharedApplication] openURL:[request URL]]; +#pragma clang diagnostic pop + } } #endif - return NO; + decisionHandler(WKNavigationActionPolicyCancel); } diff --git a/Readme.markdown b/Readme.markdown index 55c9f3391..7ffbb16d6 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -8,7 +8,7 @@ The project covers two broad areas: 1. **Layouting** - Interfacing with CoreText, generating attributed strings from HTML code 2. **User Interface** - UI-related classes render these objects, specifically `DTAttributedTextView`, `DTAttributedLabel` and `DTAttributedTextCell`. -This is useful for drawing simple rich text like any HTML document without having to use a `UIWebView`. For text selection and highlighting (as you might need for an Editor or Reader) there is the commercial **DTRichTextEditor** component which can be purchased in the [Cocoanetics Parts Store](http://www.cocoanetics.com/parts/dtrichtexteditor/). +This is useful for drawing simple rich text like any HTML document without having to use a web view. For text selection and highlighting (as you might need for an Editor or Reader) there is the commercial **DTRichTextEditor** component which can be purchased in the [Cocoanetics Parts Store](http://www.cocoanetics.com/parts/dtrichtexteditor/). Documentation ------------- diff --git a/Test/Source/DTCSSStylesheetTest.m b/Test/Source/DTCSSStylesheetTest.m index f36a3abcc..1be494474 100644 --- a/Test/Source/DTCSSStylesheetTest.m +++ b/Test/Source/DTCSSStylesheetTest.m @@ -6,7 +6,7 @@ // Copyright (c) 2012 Drobnik.com. All rights reserved. // -#import "DTCSSStyleSheetTest.h" +#import "DTCSSStylesheetTest.h" #import "DTCSSStylesheet.h" #import "DTHTMLElement.h" @@ -47,6 +47,35 @@ - (void)testAttributeWithWhitespace XCTAssertEqualObjects(empty2, @"", @"empty2 should match"); } +// Issue 1183 + +- (void)testEmptyFontFamily +{ + NSString *string = @"span { font-family: ''; empty: ; empty2:; font-size: 16px; line-height: 20 px; font-style: italic }"; + + DTCSSStylesheet *stylesheet = [[DTCSSStylesheet alloc] initWithStyleBlock:string]; + + NSDictionary *styles = [stylesheet.styles objectForKey:@"span"]; + + NSString *fontFamily = [styles objectForKey:@""]; + XCTAssertNil(fontFamily, @"font-family should be nil"); + + NSString *fontSize = [styles objectForKey:@"font-size"]; + XCTAssertEqualObjects(fontSize, @"16px", @"font-size should match"); + + NSString *lineHeight = [styles objectForKey:@"line-height"]; + XCTAssertEqualObjects(lineHeight, @"20 px", @"line-height should match"); + + NSString *fontStyle = [styles objectForKey:@"font-style"]; + XCTAssertEqualObjects(fontStyle, @"italic", @"font-style should match"); + + NSString *empty = [styles objectForKey:@"empty"]; + XCTAssertEqualObjects(empty, @"", @"empty should match"); + + NSString *empty2 = [styles objectForKey:@"empty2"]; + XCTAssertEqualObjects(empty2, @"", @"empty2 should match"); +} + // the !important CSS tag should be ignored - (void)testImportant { diff --git a/Test/Source/DTHTMLAttributedStringBuilderTest.m b/Test/Source/DTHTMLAttributedStringBuilderTest.m index 07ee4fa9f..bdb26e3d2 100644 --- a/Test/Source/DTHTMLAttributedStringBuilderTest.m +++ b/Test/Source/DTHTMLAttributedStringBuilderTest.m @@ -316,6 +316,19 @@ - (void)testTransferOfHyperlinkURLToAttachment XCTAssertEqualObjects(URL, attachment.hyperLinkURL, @"Attachment URL and element URL should match!"); } +- (void)testURLwithCJKCharacters +{ + NSAttributedString *string = [self attributedStringFromHTMLString:@"hello" options:nil]; + + NSRange effectiveRange; + NSURL *link = [string attribute:NSLinkAttributeName atIndex:0 effectiveRange:&effectiveRange]; + + NSString *linkStr = link.absoluteString; + NSString *expected = @"http://www.example.com/%E4%BD%A0%E5%A5%BD"; + + XCTAssertTrue(effectiveRange.length==5, @"There should be 5 characters with the URL"); + XCTAssertTrue([linkStr isEqualToString: expected], @"Output incorrect for CJK URL"); +} // setting ordered list starting number - (void)testOrderedListStartingNumber diff --git a/Test/Source/NSAttributedStringHTMLTest.m b/Test/Source/NSAttributedStringHTMLTest.m index d8b72b623..7940c9e03 100644 --- a/Test/Source/NSAttributedStringHTMLTest.m +++ b/Test/Source/NSAttributedStringHTMLTest.m @@ -64,13 +64,13 @@ - (void)testParagraphs NSAttributedString *string = [self attributedStringFromHTML:html]; NSData *dump = [[string string] dataUsingEncoding:NSUTF8StringEncoding]; - NSString *resultOnIOS = [dump description]; + NSString *resultOnIOS = [self hexStringForData:dump]; - NSString *resultOnMac = @"<50726566 69780a4f 6e652074 776f20e2 80a87468 7265650a 4e657720 50617261 67726170 680a5375 66666978>"; + NSString *resultOnMac = @"5072656669780a4f6e652074776f20e280a874687265650a4e6577205061726167726170680a537566666978"; //[self dumpOneResult:resultOnIOS versusOtherResult:resultOnMac]; - XCTAssertEqualObjects(resultOnIOS, resultOnMac, @"Output on Paragraph Test differs"); + XCTAssertTrue([resultOnIOS isEqualToString:resultOnMac], @"Output on Paragraph Test differs"); } @@ -80,11 +80,11 @@ - (void)testHeaderParagraphs NSAttributedString *string = [self attributedStringFromHTML:html]; NSData *dump = [[string string] dataUsingEncoding:NSUTF8StringEncoding]; - NSString *resultOnIOS = [dump description]; + NSString *resultOnIOS = [self hexStringForData:dump]; - NSString *resultOnMac = @"<50726566 69780a4f 6e650a4f 6e650a4f 6e650a4f 6e650a4f 6e650a4e 65772050 61726167 72617068 0a537566 666978>"; + NSString *resultOnMac = @"5072656669780a4f6e650a4f6e650a4f6e650a4f6e650a4f6e650a4e6577205061726167726170680a537566666978"; - XCTAssertEqualObjects(resultOnIOS, resultOnMac, @"Output on Paragraph Test differs"); + XCTAssertTrue([resultOnIOS isEqualToString:resultOnMac], @"Output on Paragraph Test differs"); } @@ -94,11 +94,11 @@ - (void)testListParagraphs NSAttributedString *string = [self attributedStringFromHTML:html]; NSData *dump = [[string string] dataUsingEncoding:NSUTF8StringEncoding]; - NSString *resultOnIOS = [dump description]; + NSString *resultOnIOS = [self hexStringForData:dump]; - NSString *resultOnMac = @"<4265666f 72650a09 e280a209 4f6e650a 09e280a2 0954776f 0a416674 65720a>"; + NSString *resultOnMac = @"4265666f72650a09e280a2094f6e650a09e280a20954776f0a41667465720a"; - XCTAssertEqualObjects(resultOnIOS, resultOnMac, @"Output on List Test differs"); + XCTAssertTrue([resultOnIOS isEqualToString:resultOnMac], @"Output on List Test differs"); } - (void)testImageParagraphs @@ -108,11 +108,11 @@ - (void)testImageParagraphs NSAttributedString *string = [self attributedStringFromHTML:html]; NSData *dump = [[string string] dataUsingEncoding:NSUTF8StringEncoding]; - NSString *resultOnIOS = [dump description]; + NSString *resultOnIOS = [self hexStringForData:dump]; - NSString *resultOnMac = @"<4265666f 72650aef bfbc0a48 65616465 720a6166 7465720a 536f6d65 20696e6c 696e6520 efbfbc20 74657874 2e0a>"; + NSString *resultOnMac = @"4265666f72650aefbfbc0a4865616465720a61667465720a536f6d6520696e6c696e6520efbfbc20746578742e0a"; - XCTAssertEqualObjects(resultOnIOS, resultOnMac, @"Output on List Test differs"); + XCTAssertTrue([resultOnIOS isEqualToString:resultOnMac], @"Output on List Test differs"); } - (void)testSpaceNormalization @@ -121,11 +121,11 @@ - (void)testSpaceNormalization NSAttributedString *string = [self attributedStringFromHTML:html]; NSData *dump = [[string string] dataUsingEncoding:NSUTF8StringEncoding]; - NSString *resultOnIOS = [dump description]; + NSString *resultOnIOS = [self hexStringForData:dump]; - NSString *resultOnMac = @"<4e6f7720 74686572 65206973 20736f6d 6520626f 6c642074 65787420 616e6420 73706163 65732073 686f756c 64206265 206e6f72 6d616c69 7a65642e 0a>"; + NSString *resultOnMac = @"4e6f7720746865726520697320736f6d6520626f6c64207465787420616e64207370616365732073686f756c64206265206e6f726d616c697a65642e0a"; - XCTAssertEqualObjects(resultOnIOS, resultOnMac, @"Output on List Test differs"); + XCTAssertTrue([resultOnIOS isEqualToString:resultOnMac], @"Output on List Test differs"); } - (void)testSpaceAndNewlines @@ -134,11 +134,11 @@ - (void)testSpaceAndNewlines NSAttributedString *string = [self attributedStringFromHTML:html]; NSData *dump = [[string string] dataUsingEncoding:NSUTF8StringEncoding]; - NSString *resultOnIOS = [dump description]; + NSString *resultOnIOS = [self hexStringForData:dump]; - NSString *resultOnMac = @"<626c6120 666f6c6c 6f777320 4e535374 72696e67 202a7374 72203d20 40225468 65205175 69636b20 42726f77 6e20466f 78204272 6f776e22 3b>"; + NSString *resultOnMac = @"626c6120666f6c6c6f7773204e53537472696e67202a737472203d20402254686520517569636b2042726f776e20466f782042726f776e223b"; - XCTAssertEqualObjects(resultOnIOS, resultOnMac, @"Output on List Test differs"); + XCTAssertTrue([resultOnIOS isEqualToString:resultOnMac], @"Output on List Test differs"); } - (void)testMissingClosingTagAndSpacing @@ -147,28 +147,12 @@ - (void)testMissingClosingTagAndSpacing NSAttributedString *string = [self attributedStringFromHTML:html]; NSData *dump = [[string string] dataUsingEncoding:NSUTF8StringEncoding]; - NSString *resultOnIOS = [dump description]; + NSString *resultOnIOS = [self hexStringForData:dump]; - NSString *resultOnMac = @"<696d6167 65206c61 7374>"; + NSString *resultOnMac = @"696d616765206c617374"; - XCTAssertEqualObjects(resultOnIOS, resultOnMac, @"Output on Invalid Tag Test differs"); - -} - -/* -- (void)testAttributedStringColorToHTML -{ - NSMutableAttributedString *string = [[NSMutableAttributedString alloc]initWithString: @"test"]; - - UIColor *color = [ UIColor colorWithRed: 1.0 green: 0.0 blue: 0.0 alpha: 1.0 ]; - - [ string setAttributes: [ NSDictionary dictionaryWithObject: (id)color.CGColor forKey: (id)kCTForegroundColorAttributeName ] range: NSMakeRange(0, 2) ]; - - NSString *expected = @"test\n"; - - STAssertEqualObjects([ string htmlString ], expected, @"Output on HTML string color test differs"); + XCTAssertTrue([resultOnIOS isEqualToString:resultOnMac], @"Output on Invalid Tag Test differs"); } - */ - (void)testCrashAtEmptyNodeBeforeDivWithiOS6Attributes { @@ -182,4 +166,17 @@ - (void)testCrashAtEmptyNodeBeforeDivWithiOS6Attributes XCTAssert(string != nil); } + +- (NSString *)hexStringForData:(NSData *)data +{ + const unsigned char *bytes = (const unsigned char *)data.bytes; + NSMutableString *hex = [NSMutableString new]; + + for (NSInteger i = 0; i < data.length; i++) { + [hex appendFormat:@"%02x", bytes[i]]; + } + + return [hex copy]; +} + @end