├── .gitignore ├── HTMLToAttributedString.xcodeproj └── project.pbxproj ├── HTMLToAttributedString ├── AppDelegate.h ├── AppDelegate.m ├── Default-568h@2x.png ├── Default.png ├── Default@2x.png ├── HTMLToAttributedString-Info.plist ├── HTMLToAttributedString-Prefix.pch ├── ViewController.h ├── ViewController.m ├── car.png ├── en.lproj │ ├── InfoPlist.strings │ └── MainStoryboard.storyboard └── main.m ├── NSAttributedString+DDHTML ├── NSAttributedString+DDHTML.h └── NSAttributedString+DDHTML.m ├── NSAttributedString-DDHTML.podspec └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /HTMLToAttributedString.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | AA9A4B81166FE7A80006F4EE /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA9A4B80166FE7A80006F4EE /* UIKit.framework */; }; 11 | AA9A4B83166FE7A80006F4EE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA9A4B82166FE7A80006F4EE /* Foundation.framework */; }; 12 | AA9A4B85166FE7A80006F4EE /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA9A4B84166FE7A80006F4EE /* CoreGraphics.framework */; }; 13 | AA9A4B8B166FE7A80006F4EE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = AA9A4B89166FE7A80006F4EE /* InfoPlist.strings */; }; 14 | AA9A4B8D166FE7A80006F4EE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AA9A4B8C166FE7A80006F4EE /* main.m */; }; 15 | AA9A4B91166FE7A80006F4EE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AA9A4B90166FE7A80006F4EE /* AppDelegate.m */; }; 16 | AA9A4B93166FE7A80006F4EE /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = AA9A4B92166FE7A80006F4EE /* Default.png */; }; 17 | AA9A4B95166FE7A80006F4EE /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA9A4B94166FE7A80006F4EE /* Default@2x.png */; }; 18 | AA9A4B97166FE7A80006F4EE /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA9A4B96166FE7A80006F4EE /* Default-568h@2x.png */; }; 19 | AA9A4B9A166FE7A80006F4EE /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA9A4B98166FE7A80006F4EE /* MainStoryboard.storyboard */; }; 20 | AA9A4B9D166FE7A80006F4EE /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AA9A4B9C166FE7A80006F4EE /* ViewController.m */; }; 21 | AA9A4BA7166FF1AA0006F4EE /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = AA9A4BA6166FF1AA0006F4EE /* libxml2.dylib */; }; 22 | AAA378A816701A280092D30F /* NSAttributedString+DDHTML.m in Sources */ = {isa = PBXBuildFile; fileRef = AAA378A716701A280092D30F /* NSAttributedString+DDHTML.m */; }; 23 | D11933B11BD0883F00E34F3A /* car.png in Resources */ = {isa = PBXBuildFile; fileRef = D11933B01BD0883F00E34F3A /* car.png */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | AA9A4B7C166FE7A80006F4EE /* HTMLToAttributedString.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HTMLToAttributedString.app; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | AA9A4B80166FE7A80006F4EE /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 29 | AA9A4B82166FE7A80006F4EE /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 30 | AA9A4B84166FE7A80006F4EE /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 31 | AA9A4B88166FE7A80006F4EE /* HTMLToAttributedString-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HTMLToAttributedString-Info.plist"; sourceTree = ""; }; 32 | AA9A4B8A166FE7A80006F4EE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 33 | AA9A4B8C166FE7A80006F4EE /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 34 | AA9A4B8E166FE7A80006F4EE /* HTMLToAttributedString-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "HTMLToAttributedString-Prefix.pch"; sourceTree = ""; }; 35 | AA9A4B8F166FE7A80006F4EE /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 36 | AA9A4B90166FE7A80006F4EE /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 37 | AA9A4B92166FE7A80006F4EE /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 38 | AA9A4B94166FE7A80006F4EE /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 39 | AA9A4B96166FE7A80006F4EE /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 40 | AA9A4B99166FE7A80006F4EE /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard.storyboard; sourceTree = ""; }; 41 | AA9A4B9B166FE7A80006F4EE /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 42 | AA9A4B9C166FE7A80006F4EE /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 43 | AA9A4BA6166FF1AA0006F4EE /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = usr/lib/libxml2.dylib; sourceTree = SDKROOT; }; 44 | AAA378A616701A280092D30F /* NSAttributedString+DDHTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSAttributedString+DDHTML.h"; sourceTree = ""; }; 45 | AAA378A716701A280092D30F /* NSAttributedString+DDHTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSAttributedString+DDHTML.m"; sourceTree = ""; }; 46 | D11933B01BD0883F00E34F3A /* car.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = car.png; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | AA9A4B79166FE7A80006F4EE /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | AA9A4BA7166FF1AA0006F4EE /* libxml2.dylib in Frameworks */, 55 | AA9A4B81166FE7A80006F4EE /* UIKit.framework in Frameworks */, 56 | AA9A4B83166FE7A80006F4EE /* Foundation.framework in Frameworks */, 57 | AA9A4B85166FE7A80006F4EE /* CoreGraphics.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | AA9A4B71166FE7A80006F4EE = { 65 | isa = PBXGroup; 66 | children = ( 67 | AA9A4BA6166FF1AA0006F4EE /* libxml2.dylib */, 68 | AA9A4B86166FE7A80006F4EE /* HTMLToAttributedString */, 69 | AA9A4B7F166FE7A80006F4EE /* Frameworks */, 70 | AA9A4B7D166FE7A80006F4EE /* Products */, 71 | ); 72 | sourceTree = ""; 73 | }; 74 | AA9A4B7D166FE7A80006F4EE /* Products */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | AA9A4B7C166FE7A80006F4EE /* HTMLToAttributedString.app */, 78 | ); 79 | name = Products; 80 | sourceTree = ""; 81 | }; 82 | AA9A4B7F166FE7A80006F4EE /* Frameworks */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | AA9A4B80166FE7A80006F4EE /* UIKit.framework */, 86 | AA9A4B82166FE7A80006F4EE /* Foundation.framework */, 87 | AA9A4B84166FE7A80006F4EE /* CoreGraphics.framework */, 88 | ); 89 | name = Frameworks; 90 | sourceTree = ""; 91 | }; 92 | AA9A4B86166FE7A80006F4EE /* HTMLToAttributedString */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | AAA378A516701A280092D30F /* NSAttributedString+DDHTML */, 96 | AA9A4B8F166FE7A80006F4EE /* AppDelegate.h */, 97 | AA9A4B90166FE7A80006F4EE /* AppDelegate.m */, 98 | AA9A4B98166FE7A80006F4EE /* MainStoryboard.storyboard */, 99 | AA9A4B9B166FE7A80006F4EE /* ViewController.h */, 100 | AA9A4B9C166FE7A80006F4EE /* ViewController.m */, 101 | AA9A4B87166FE7A80006F4EE /* Supporting Files */, 102 | ); 103 | path = HTMLToAttributedString; 104 | sourceTree = ""; 105 | }; 106 | AA9A4B87166FE7A80006F4EE /* Supporting Files */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | D11933B01BD0883F00E34F3A /* car.png */, 110 | AA9A4B88166FE7A80006F4EE /* HTMLToAttributedString-Info.plist */, 111 | AA9A4B89166FE7A80006F4EE /* InfoPlist.strings */, 112 | AA9A4B8C166FE7A80006F4EE /* main.m */, 113 | AA9A4B8E166FE7A80006F4EE /* HTMLToAttributedString-Prefix.pch */, 114 | AA9A4B92166FE7A80006F4EE /* Default.png */, 115 | AA9A4B94166FE7A80006F4EE /* Default@2x.png */, 116 | AA9A4B96166FE7A80006F4EE /* Default-568h@2x.png */, 117 | ); 118 | name = "Supporting Files"; 119 | sourceTree = ""; 120 | }; 121 | AAA378A516701A280092D30F /* NSAttributedString+DDHTML */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | AAA378A616701A280092D30F /* NSAttributedString+DDHTML.h */, 125 | AAA378A716701A280092D30F /* NSAttributedString+DDHTML.m */, 126 | ); 127 | path = "NSAttributedString+DDHTML"; 128 | sourceTree = SOURCE_ROOT; 129 | }; 130 | /* End PBXGroup section */ 131 | 132 | /* Begin PBXNativeTarget section */ 133 | AA9A4B7B166FE7A80006F4EE /* HTMLToAttributedString */ = { 134 | isa = PBXNativeTarget; 135 | buildConfigurationList = AA9A4BA0166FE7A80006F4EE /* Build configuration list for PBXNativeTarget "HTMLToAttributedString" */; 136 | buildPhases = ( 137 | AA9A4B78166FE7A80006F4EE /* Sources */, 138 | AA9A4B79166FE7A80006F4EE /* Frameworks */, 139 | AA9A4B7A166FE7A80006F4EE /* Resources */, 140 | ); 141 | buildRules = ( 142 | ); 143 | dependencies = ( 144 | ); 145 | name = HTMLToAttributedString; 146 | productName = HTMLToAttributedString; 147 | productReference = AA9A4B7C166FE7A80006F4EE /* HTMLToAttributedString.app */; 148 | productType = "com.apple.product-type.application"; 149 | }; 150 | /* End PBXNativeTarget section */ 151 | 152 | /* Begin PBXProject section */ 153 | AA9A4B73166FE7A80006F4EE /* Project object */ = { 154 | isa = PBXProject; 155 | attributes = { 156 | LastUpgradeCheck = 0450; 157 | ORGANIZATIONNAME = "Deloitte Digital"; 158 | }; 159 | buildConfigurationList = AA9A4B76166FE7A80006F4EE /* Build configuration list for PBXProject "HTMLToAttributedString" */; 160 | compatibilityVersion = "Xcode 3.2"; 161 | developmentRegion = English; 162 | hasScannedForEncodings = 0; 163 | knownRegions = ( 164 | en, 165 | ); 166 | mainGroup = AA9A4B71166FE7A80006F4EE; 167 | productRefGroup = AA9A4B7D166FE7A80006F4EE /* Products */; 168 | projectDirPath = ""; 169 | projectRoot = ""; 170 | targets = ( 171 | AA9A4B7B166FE7A80006F4EE /* HTMLToAttributedString */, 172 | ); 173 | }; 174 | /* End PBXProject section */ 175 | 176 | /* Begin PBXResourcesBuildPhase section */ 177 | AA9A4B7A166FE7A80006F4EE /* Resources */ = { 178 | isa = PBXResourcesBuildPhase; 179 | buildActionMask = 2147483647; 180 | files = ( 181 | AA9A4B8B166FE7A80006F4EE /* InfoPlist.strings in Resources */, 182 | AA9A4B93166FE7A80006F4EE /* Default.png in Resources */, 183 | AA9A4B95166FE7A80006F4EE /* Default@2x.png in Resources */, 184 | D11933B11BD0883F00E34F3A /* car.png in Resources */, 185 | AA9A4B97166FE7A80006F4EE /* Default-568h@2x.png in Resources */, 186 | AA9A4B9A166FE7A80006F4EE /* MainStoryboard.storyboard in Resources */, 187 | ); 188 | runOnlyForDeploymentPostprocessing = 0; 189 | }; 190 | /* End PBXResourcesBuildPhase section */ 191 | 192 | /* Begin PBXSourcesBuildPhase section */ 193 | AA9A4B78166FE7A80006F4EE /* Sources */ = { 194 | isa = PBXSourcesBuildPhase; 195 | buildActionMask = 2147483647; 196 | files = ( 197 | AA9A4B8D166FE7A80006F4EE /* main.m in Sources */, 198 | AA9A4B91166FE7A80006F4EE /* AppDelegate.m in Sources */, 199 | AA9A4B9D166FE7A80006F4EE /* ViewController.m in Sources */, 200 | AAA378A816701A280092D30F /* NSAttributedString+DDHTML.m in Sources */, 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | }; 204 | /* End PBXSourcesBuildPhase section */ 205 | 206 | /* Begin PBXVariantGroup section */ 207 | AA9A4B89166FE7A80006F4EE /* InfoPlist.strings */ = { 208 | isa = PBXVariantGroup; 209 | children = ( 210 | AA9A4B8A166FE7A80006F4EE /* en */, 211 | ); 212 | name = InfoPlist.strings; 213 | sourceTree = ""; 214 | }; 215 | AA9A4B98166FE7A80006F4EE /* MainStoryboard.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | AA9A4B99166FE7A80006F4EE /* en */, 219 | ); 220 | name = MainStoryboard.storyboard; 221 | sourceTree = ""; 222 | }; 223 | /* End PBXVariantGroup section */ 224 | 225 | /* Begin XCBuildConfiguration section */ 226 | AA9A4B9E166FE7A80006F4EE /* Debug */ = { 227 | isa = XCBuildConfiguration; 228 | buildSettings = { 229 | ALWAYS_SEARCH_USER_PATHS = NO; 230 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 231 | CLANG_CXX_LIBRARY = "libc++"; 232 | CLANG_ENABLE_OBJC_ARC = YES; 233 | CLANG_WARN_EMPTY_BODY = YES; 234 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 235 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 236 | COPY_PHASE_STRIP = NO; 237 | GCC_C_LANGUAGE_STANDARD = gnu99; 238 | GCC_DYNAMIC_NO_PIC = NO; 239 | GCC_OPTIMIZATION_LEVEL = 0; 240 | GCC_PREPROCESSOR_DEFINITIONS = ( 241 | "DEBUG=1", 242 | "$(inherited)", 243 | ); 244 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 245 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 246 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 247 | GCC_WARN_UNUSED_VARIABLE = YES; 248 | HEADER_SEARCH_PATHS = "${SDKROOT}/usr/include/libxml2"; 249 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 250 | ONLY_ACTIVE_ARCH = YES; 251 | SDKROOT = iphoneos; 252 | USER_HEADER_SEARCH_PATHS = ""; 253 | }; 254 | name = Debug; 255 | }; 256 | AA9A4B9F166FE7A80006F4EE /* Release */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | ALWAYS_SEARCH_USER_PATHS = NO; 260 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 261 | CLANG_CXX_LIBRARY = "libc++"; 262 | CLANG_ENABLE_OBJC_ARC = YES; 263 | CLANG_WARN_EMPTY_BODY = YES; 264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 265 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 266 | COPY_PHASE_STRIP = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 269 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 270 | GCC_WARN_UNUSED_VARIABLE = YES; 271 | HEADER_SEARCH_PATHS = "${SDKROOT}/usr/include/libxml2"; 272 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 273 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 274 | SDKROOT = iphoneos; 275 | USER_HEADER_SEARCH_PATHS = ""; 276 | VALIDATE_PRODUCT = YES; 277 | }; 278 | name = Release; 279 | }; 280 | AA9A4BA1166FE7A80006F4EE /* Debug */ = { 281 | isa = XCBuildConfiguration; 282 | buildSettings = { 283 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 284 | GCC_PREFIX_HEADER = "HTMLToAttributedString/HTMLToAttributedString-Prefix.pch"; 285 | INFOPLIST_FILE = "HTMLToAttributedString/HTMLToAttributedString-Info.plist"; 286 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 287 | OTHER_LDFLAGS = ""; 288 | PRODUCT_NAME = "$(TARGET_NAME)"; 289 | USER_HEADER_SEARCH_PATHS = ""; 290 | WRAPPER_EXTENSION = app; 291 | }; 292 | name = Debug; 293 | }; 294 | AA9A4BA2166FE7A80006F4EE /* Release */ = { 295 | isa = XCBuildConfiguration; 296 | buildSettings = { 297 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 298 | GCC_PREFIX_HEADER = "HTMLToAttributedString/HTMLToAttributedString-Prefix.pch"; 299 | INFOPLIST_FILE = "HTMLToAttributedString/HTMLToAttributedString-Info.plist"; 300 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 301 | OTHER_LDFLAGS = ""; 302 | PRODUCT_NAME = "$(TARGET_NAME)"; 303 | USER_HEADER_SEARCH_PATHS = ""; 304 | WRAPPER_EXTENSION = app; 305 | }; 306 | name = Release; 307 | }; 308 | /* End XCBuildConfiguration section */ 309 | 310 | /* Begin XCConfigurationList section */ 311 | AA9A4B76166FE7A80006F4EE /* Build configuration list for PBXProject "HTMLToAttributedString" */ = { 312 | isa = XCConfigurationList; 313 | buildConfigurations = ( 314 | AA9A4B9E166FE7A80006F4EE /* Debug */, 315 | AA9A4B9F166FE7A80006F4EE /* Release */, 316 | ); 317 | defaultConfigurationIsVisible = 0; 318 | defaultConfigurationName = Release; 319 | }; 320 | AA9A4BA0166FE7A80006F4EE /* Build configuration list for PBXNativeTarget "HTMLToAttributedString" */ = { 321 | isa = XCConfigurationList; 322 | buildConfigurations = ( 323 | AA9A4BA1166FE7A80006F4EE /* Debug */, 324 | AA9A4BA2166FE7A80006F4EE /* Release */, 325 | ); 326 | defaultConfigurationIsVisible = 0; 327 | defaultConfigurationName = Release; 328 | }; 329 | /* End XCConfigurationList section */ 330 | }; 331 | rootObject = AA9A4B73166FE7A80006F4EE /* Project object */; 332 | } 333 | -------------------------------------------------------------------------------- /HTMLToAttributedString/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // HTMLToAttributedString 4 | // 5 | // Created by Derek Bowen (Work) on 12/5/12. 6 | // Copyright (c) 2012 Deloitte Digital. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /HTMLToAttributedString/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // HTMLToAttributedString 4 | // 5 | // Created by Derek Bowen (Work) on 12/5/12. 6 | // Copyright (c) 2012 Deloitte Digital. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /HTMLToAttributedString/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbowen/NSAttributedString-DDHTML/9684d4e9c9ba3ea4c9cc14475888a3991193e5cd/HTMLToAttributedString/Default-568h@2x.png -------------------------------------------------------------------------------- /HTMLToAttributedString/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbowen/NSAttributedString-DDHTML/9684d4e9c9ba3ea4c9cc14475888a3991193e5cd/HTMLToAttributedString/Default.png -------------------------------------------------------------------------------- /HTMLToAttributedString/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbowen/NSAttributedString-DDHTML/9684d4e9c9ba3ea4c9cc14475888a3991193e5cd/HTMLToAttributedString/Default@2x.png -------------------------------------------------------------------------------- /HTMLToAttributedString/HTMLToAttributedString-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | whocares.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | MainStoryboard 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /HTMLToAttributedString/HTMLToAttributedString-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'HTMLToAttributedString' target in the 'HTMLToAttributedString' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_5_0 8 | #warning "This project uses features only available in iOS SDK 5.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /HTMLToAttributedString/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // HTMLToAttributedString 4 | // 5 | // Created by Derek Bowen (Work) on 12/5/12. 6 | // Copyright (c) 2012 Deloitte Digital. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /HTMLToAttributedString/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // HTMLToAttributedString 4 | // 5 | // Created by Derek Bowen (Work) on 12/5/12. 6 | // Copyright (c) 2012 Deloitte Digital. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "NSAttributedString+DDHTML.h" 11 | 12 | @interface ViewController () 13 | 14 | @property (weak, nonatomic) IBOutlet UILabel *label; 15 | 16 | @end 17 | 18 | @implementation ViewController 19 | 20 | - (void)viewDidLoad 21 | { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view, typically from a nib. 24 | 25 | 26 | NSAttributedString *attrString = [NSAttributedString attributedStringFromHTML:@"This is Happy bold, underlined, awesomeness link!
And some italic on the next line." 27 | normalFont:[UIFont systemFontOfSize:12] 28 | boldFont:[UIFont boldSystemFontOfSize:12] 29 | italicFont:[UIFont italicSystemFontOfSize:12.0] 30 | ]; 31 | self.label.attributedText = attrString; 32 | } 33 | 34 | - (void)didReceiveMemoryWarning 35 | { 36 | [super didReceiveMemoryWarning]; 37 | // Dispose of any resources that can be recreated. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /HTMLToAttributedString/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbowen/NSAttributedString-DDHTML/9684d4e9c9ba3ea4c9cc14475888a3991193e5cd/HTMLToAttributedString/car.png -------------------------------------------------------------------------------- /HTMLToAttributedString/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /HTMLToAttributedString/en.lproj/MainStoryboard.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /HTMLToAttributedString/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // HTMLToAttributedString 4 | // 5 | // Created by Derek Bowen (Work) on 12/5/12. 6 | // Copyright (c) 2012 Deloitte Digital. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /NSAttributedString+DDHTML/NSAttributedString+DDHTML.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSAttributedString+HTML.m 3 | // 4 | // Created by Derek Bowen 5 | // Copyright (c) 2012-2015, Deloitte Digital 6 | // All rights reserved. 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of the nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | // DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | 31 | #import 32 | #import 33 | 34 | NS_ASSUME_NONNULL_BEGIN 35 | 36 | @interface NSAttributedString (DDHTML) 37 | 38 | /** 39 | * Generates an attributed string from HTML. 40 | * 41 | * @param htmlString HTML String 42 | * 43 | * @return Attributed string 44 | */ 45 | + (NSAttributedString *)attributedStringFromHTML:(NSString *)htmlString; 46 | 47 | /** 48 | * Generates an attributed string from HTML. 49 | * 50 | * @param htmlString HTML String 51 | * @param boldFont Font to use for \ and \ tags 52 | * @param italicFont Font to use for \ and \ tags 53 | * 54 | * @return Attributed string 55 | */ 56 | + (NSAttributedString *)attributedStringFromHTML:(NSString *)htmlString boldFont:(UIFont *)boldFont italicFont:(UIFont *)italicFont; 57 | 58 | /** 59 | * Generates an attributed string from HTML. 60 | * 61 | * @param htmlString HTML String 62 | * @param normalFont Font to use for general text 63 | * @param boldFont Font to use for \ and \ tags 64 | * @param italicFont Font to use for \ and \ tags 65 | * 66 | * @return Attributed string 67 | */ 68 | + (NSAttributedString *)attributedStringFromHTML:(NSString *)htmlString normalFont:(UIFont *)normalFont boldFont:(UIFont *)boldFont italicFont:(UIFont *)italicFont; 69 | 70 | /** 71 | * Generates an attributed string from HTML. 72 | * 73 | * @param htmlString HTML String 74 | * @param normalFont Font to use for general text 75 | * @param boldFont Font to use for \ and \ tags 76 | * @param italicFont Font to use for \ and \ tags 77 | * @param imageMap Images to use in place of standard bundle images. 78 | * 79 | * @return Attributed string 80 | */ 81 | + (NSAttributedString *)attributedStringFromHTML:(NSString *)htmlString normalFont:(UIFont *)normalFont boldFont:(UIFont *)boldFont italicFont:(UIFont *)italicFont imageMap:(NSDictionary *)imageMap; 82 | 83 | @end 84 | 85 | NS_ASSUME_NONNULL_END 86 | -------------------------------------------------------------------------------- /NSAttributedString+DDHTML/NSAttributedString+DDHTML.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSAttributedString+HTML.m 3 | // 4 | // Created by Derek Bowen 5 | // Copyright (c) 2012-2015, Deloitte Digital 6 | // All rights reserved. 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of the nor the 16 | // names of its contributors may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | // DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 23 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | 31 | #import "NSAttributedString+DDHTML.h" 32 | #include 33 | 34 | @implementation NSAttributedString (DDHTML) 35 | 36 | + (NSAttributedString *)attributedStringFromHTML:(NSString *)htmlString 37 | { 38 | UIFont *preferredBodyFont = [UIFont preferredFontForTextStyle:UIFontTextStyleBody]; 39 | 40 | return [self attributedStringFromHTML:htmlString 41 | normalFont:preferredBodyFont 42 | boldFont:[UIFont boldSystemFontOfSize:preferredBodyFont.pointSize] 43 | italicFont:[UIFont italicSystemFontOfSize:preferredBodyFont.pointSize]]; 44 | } 45 | 46 | + (NSAttributedString *)attributedStringFromHTML:(NSString *)htmlString boldFont:(UIFont *)boldFont italicFont:(UIFont *)italicFont 47 | { 48 | return [self attributedStringFromHTML:htmlString 49 | normalFont:[UIFont preferredFontForTextStyle:UIFontTextStyleBody] 50 | boldFont:boldFont 51 | italicFont:italicFont]; 52 | } 53 | + (NSAttributedString *)attributedStringFromHTML:(NSString *)htmlString normalFont:(UIFont *)normalFont boldFont:(UIFont *)boldFont italicFont:(UIFont *)italicFont 54 | { 55 | return [self attributedStringFromHTML:htmlString 56 | normalFont:normalFont 57 | boldFont:boldFont 58 | italicFont:italicFont 59 | imageMap:@{}]; 60 | } 61 | 62 | + (NSAttributedString *)attributedStringFromHTML:(NSString *)htmlString normalFont:(UIFont *)normalFont boldFont:(UIFont *)boldFont italicFont:(UIFont *)italicFont imageMap:(NSDictionary *)imageMap 63 | { 64 | // Parse HTML string as XML document using UTF-8 encoding 65 | NSData *documentData = [htmlString dataUsingEncoding:NSUTF8StringEncoding]; 66 | xmlDoc *document = htmlReadMemory(documentData.bytes, (int)documentData.length, nil, "UTF-8", HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR); 67 | 68 | if (document == NULL) { 69 | return [[NSAttributedString alloc] initWithString:htmlString attributes:nil]; 70 | } 71 | 72 | NSMutableAttributedString *finalAttributedString = [[NSMutableAttributedString alloc] init]; 73 | 74 | xmlNodePtr currentNode = document->children; 75 | while (currentNode != NULL) { 76 | NSAttributedString *childString = [self attributedStringFromNode:currentNode normalFont:normalFont boldFont:boldFont italicFont:italicFont imageMap:imageMap]; 77 | [finalAttributedString appendAttributedString:childString]; 78 | 79 | currentNode = currentNode->next; 80 | } 81 | 82 | xmlFreeDoc(document); 83 | 84 | return finalAttributedString; 85 | } 86 | 87 | + (NSAttributedString *)attributedStringFromNode:(xmlNodePtr)xmlNode normalFont:(UIFont *)normalFont boldFont:(UIFont *)boldFont italicFont:(UIFont *)italicFont imageMap:(NSDictionary *)imageMap 88 | { 89 | NSMutableAttributedString *nodeAttributedString = [[NSMutableAttributedString alloc] init]; 90 | 91 | if ((xmlNode->type != XML_ENTITY_REF_NODE) && ((xmlNode->type != XML_ELEMENT_NODE) && xmlNode->content != NULL)) { 92 | NSAttributedString *normalAttributedString = [[NSAttributedString alloc] initWithString:[NSString stringWithCString:(const char *)xmlNode->content encoding:NSUTF8StringEncoding] attributes:@{NSFontAttributeName : normalFont}]; 93 | [nodeAttributedString appendAttributedString:normalAttributedString]; 94 | } 95 | 96 | // Handle children 97 | xmlNodePtr currentNode = xmlNode->children; 98 | while (currentNode != NULL) { 99 | NSAttributedString *childString = [self attributedStringFromNode:currentNode normalFont:normalFont boldFont:boldFont italicFont:italicFont imageMap:imageMap]; 100 | [nodeAttributedString appendAttributedString:childString]; 101 | 102 | currentNode = currentNode->next; 103 | } 104 | 105 | if (xmlNode->type == XML_ELEMENT_NODE) { 106 | 107 | NSRange nodeAttributedStringRange = NSMakeRange(0, nodeAttributedString.length); 108 | 109 | // Build dictionary to store attributes 110 | NSMutableDictionary *attributeDictionary = [NSMutableDictionary dictionary]; 111 | if (xmlNode->properties != NULL) { 112 | xmlAttrPtr attribute = xmlNode->properties; 113 | 114 | while (attribute != NULL) { 115 | NSString *attributeValue = @""; 116 | 117 | if (attribute->children != NULL) { 118 | attributeValue = [NSString stringWithCString:(const char *)attribute->children->content encoding:NSUTF8StringEncoding]; 119 | } 120 | NSString *attributeName = [[NSString stringWithCString:(const char*)attribute->name encoding:NSUTF8StringEncoding] lowercaseString]; 121 | [attributeDictionary setObject:attributeValue forKey:attributeName]; 122 | 123 | attribute = attribute->next; 124 | } 125 | } 126 | 127 | // Bold Tag 128 | if (strncmp("b", (const char *)xmlNode->name, strlen((const char *)xmlNode->name)) == 0 || 129 | strncmp("strong", (const char *)xmlNode->name, strlen((const char *)xmlNode->name)) == 0) { 130 | if (boldFont) { 131 | [nodeAttributedString addAttribute:NSFontAttributeName value:boldFont range:nodeAttributedStringRange]; 132 | } 133 | } 134 | 135 | // Italic Tag 136 | else if (strncmp("i", (const char *)xmlNode->name, strlen((const char *)xmlNode->name)) == 0 || 137 | strncmp("em", (const char *)xmlNode->name, strlen((const char *)xmlNode->name)) == 0) { 138 | if (italicFont) { 139 | [nodeAttributedString addAttribute:NSFontAttributeName value:italicFont range:nodeAttributedStringRange]; 140 | } 141 | } 142 | 143 | // Underline Tag 144 | else if (strncmp("u", (const char *)xmlNode->name, strlen((const char *)xmlNode->name)) == 0) { 145 | [nodeAttributedString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:nodeAttributedStringRange]; 146 | } 147 | 148 | // Stike Tag 149 | else if (strncmp("strike", (const char *)xmlNode->name, strlen((const char *)xmlNode->name)) == 0) { 150 | [nodeAttributedString addAttribute:NSStrikethroughStyleAttributeName value:@(YES) range:nodeAttributedStringRange]; 151 | } 152 | 153 | // Stoke Tag 154 | else if (strncmp("stroke", (const char *)xmlNode->name, strlen((const char *)xmlNode->name)) == 0) { 155 | UIColor *strokeColor = [UIColor purpleColor]; 156 | NSNumber *strokeWidth = @(1.0); 157 | 158 | if (attributeDictionary[@"color"]) { 159 | strokeColor = [self colorFromHexString:attributeDictionary[@"color"]]; 160 | } 161 | if (attributeDictionary[@"width"]) { 162 | strokeWidth = @(fabs([attributeDictionary[@"width"] doubleValue])); 163 | } 164 | if (!attributeDictionary[@"nofill"]) { 165 | strokeWidth = @(-fabs([strokeWidth doubleValue])); 166 | } 167 | 168 | [nodeAttributedString addAttribute:NSStrokeColorAttributeName value:strokeColor range:nodeAttributedStringRange]; 169 | [nodeAttributedString addAttribute:NSStrokeWidthAttributeName value:strokeWidth range:nodeAttributedStringRange]; 170 | } 171 | 172 | // Shadow Tag 173 | else if (strncmp("shadow", (const char *)xmlNode->name, strlen((const char *)xmlNode->name)) == 0) { 174 | #if __has_include() 175 | NSShadow *shadow = [[NSShadow alloc] init]; 176 | shadow.shadowOffset = CGSizeMake(0, 0); 177 | shadow.shadowBlurRadius = 2.0; 178 | shadow.shadowColor = [UIColor blackColor]; 179 | 180 | if (attributeDictionary[@"offset"]) { 181 | shadow.shadowOffset = CGSizeFromString(attributeDictionary[@"offset"]); 182 | } 183 | if (attributeDictionary[@"blurradius"]) { 184 | shadow.shadowBlurRadius = [attributeDictionary[@"blurradius"] doubleValue]; 185 | } 186 | if (attributeDictionary[@"color"]) { 187 | shadow.shadowColor = [self colorFromHexString:attributeDictionary[@"color"]]; 188 | } 189 | 190 | [nodeAttributedString addAttribute:NSShadowAttributeName value:shadow range:nodeAttributedStringRange]; 191 | #endif 192 | } 193 | 194 | // Font Tag 195 | else if (strncmp("font", (const char *)xmlNode->name, strlen((const char *)xmlNode->name)) == 0) { 196 | NSString *fontName = nil; 197 | NSNumber *fontSize = nil; 198 | UIColor *foregroundColor = nil; 199 | UIColor *backgroundColor = nil; 200 | 201 | if (attributeDictionary[@"face"]) { 202 | fontName = attributeDictionary[@"face"]; 203 | } 204 | if (attributeDictionary[@"size"]) { 205 | fontSize = @([attributeDictionary[@"size"] doubleValue]); 206 | } 207 | if (attributeDictionary[@"color"]) { 208 | foregroundColor = [self colorFromHexString:attributeDictionary[@"color"]]; 209 | } 210 | if (attributeDictionary[@"backgroundcolor"]) { 211 | backgroundColor = [self colorFromHexString:attributeDictionary[@"backgroundcolor"]]; 212 | } 213 | 214 | if (fontName == nil && fontSize != nil) { 215 | [nodeAttributedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:[fontSize doubleValue]] range:nodeAttributedStringRange]; 216 | } 217 | else if (fontName != nil && fontSize == nil) { 218 | [nodeAttributedString addAttribute:NSFontAttributeName value:[self fontOrSystemFontForName:fontName size:12.0] range:nodeAttributedStringRange]; 219 | } 220 | else if (fontName != nil && fontSize != nil) { 221 | [nodeAttributedString addAttribute:NSFontAttributeName value:[self fontOrSystemFontForName:fontName size:fontSize.floatValue] range:nodeAttributedStringRange]; 222 | } 223 | 224 | if (foregroundColor) { 225 | [nodeAttributedString addAttribute:NSForegroundColorAttributeName value:foregroundColor range:nodeAttributedStringRange]; 226 | } 227 | if (backgroundColor) { 228 | [nodeAttributedString addAttribute:NSBackgroundColorAttributeName value:backgroundColor range:nodeAttributedStringRange]; 229 | } 230 | } 231 | 232 | // Paragraph Tag 233 | else if (strncmp("p", (const char *)xmlNode->name, strlen((const char *)xmlNode->name)) == 0) { 234 | NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 235 | 236 | if ([attributeDictionary objectForKey:@"align"]) { 237 | NSString *alignString = [attributeDictionary[@"align"] lowercaseString]; 238 | 239 | if ([alignString isEqualToString:@"left"]) { 240 | paragraphStyle.alignment = NSTextAlignmentLeft; 241 | } 242 | else if ([alignString isEqualToString:@"center"]) { 243 | paragraphStyle.alignment = NSTextAlignmentCenter; 244 | } 245 | else if ([alignString isEqualToString:@"right"]) { 246 | paragraphStyle.alignment = NSTextAlignmentRight; 247 | } 248 | else if ([alignString isEqualToString:@"justify"]) { 249 | paragraphStyle.alignment = NSTextAlignmentJustified; 250 | } 251 | } 252 | if ([attributeDictionary objectForKey:@"linebreakmode"]) { 253 | NSString *lineBreakModeString = [attributeDictionary[@"linebreakmode"] lowercaseString]; 254 | 255 | if ([lineBreakModeString isEqualToString:@"wordwrapping"]) { 256 | paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping; 257 | } 258 | else if ([lineBreakModeString isEqualToString:@"charwrapping"]) { 259 | paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping; 260 | } 261 | else if ([lineBreakModeString isEqualToString:@"clipping"]) { 262 | paragraphStyle.lineBreakMode = NSLineBreakByClipping; 263 | } 264 | else if ([lineBreakModeString isEqualToString:@"truncatinghead"]) { 265 | paragraphStyle.lineBreakMode = NSLineBreakByTruncatingHead; 266 | } 267 | else if ([lineBreakModeString isEqualToString:@"truncatingtail"]) { 268 | paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail; 269 | } 270 | else if ([lineBreakModeString isEqualToString:@"truncatingmiddle"]) { 271 | paragraphStyle.lineBreakMode = NSLineBreakByTruncatingMiddle; 272 | } 273 | } 274 | 275 | if ([attributeDictionary objectForKey:@"firstlineheadindent"]) { 276 | paragraphStyle.firstLineHeadIndent = [attributeDictionary[@"firstlineheadindent"] doubleValue]; 277 | } 278 | if ([attributeDictionary objectForKey:@"headindent"]) { 279 | paragraphStyle.headIndent = [attributeDictionary[@"headindent"] doubleValue]; 280 | } 281 | if ([attributeDictionary objectForKey:@"hyphenationfactor"]) { 282 | paragraphStyle.hyphenationFactor = [attributeDictionary[@"hyphenationfactor"] doubleValue]; 283 | } 284 | if ([attributeDictionary objectForKey:@"lineheightmultiple"]) { 285 | paragraphStyle.lineHeightMultiple = [attributeDictionary[@"lineheightmultiple"] doubleValue]; 286 | } 287 | if ([attributeDictionary objectForKey:@"linespacing"]) { 288 | paragraphStyle.lineSpacing = [attributeDictionary[@"linespacing"] doubleValue]; 289 | } 290 | if ([attributeDictionary objectForKey:@"maximumlineheight"]) { 291 | paragraphStyle.maximumLineHeight = [attributeDictionary[@"maximumlineheight"] doubleValue]; 292 | } 293 | if ([attributeDictionary objectForKey:@"minimumlineheight"]) { 294 | paragraphStyle.minimumLineHeight = [attributeDictionary[@"minimumlineheight"] doubleValue]; 295 | } 296 | if ([attributeDictionary objectForKey:@"paragraphspacing"]) { 297 | paragraphStyle.paragraphSpacing = [attributeDictionary[@"paragraphspacing"] doubleValue]; 298 | } 299 | if ([attributeDictionary objectForKey:@"paragraphspacingbefore"]) { 300 | paragraphStyle.paragraphSpacingBefore = [attributeDictionary[@"paragraphspacingbefore"] doubleValue]; 301 | } 302 | if ([attributeDictionary objectForKey:@"tailindent"]) { 303 | paragraphStyle.tailIndent = [attributeDictionary[@"tailindent"] doubleValue]; 304 | } 305 | 306 | [nodeAttributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:nodeAttributedStringRange]; 307 | 308 | // MR - For some reason they are not adding the paragraph space when parsing the

tag 309 | [nodeAttributedString appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]]; 310 | } 311 | 312 | 313 | // Links 314 | else if (strncmp("a href", (const char *)xmlNode->name, strlen((const char *)xmlNode->name)) == 0) { 315 | 316 | xmlChar *value = xmlNodeListGetString(xmlNode->doc, xmlNode->xmlChildrenNode, 1); 317 | if (value) 318 | { 319 | NSString *title = [NSString stringWithCString:(const char *)value encoding:NSUTF8StringEncoding]; 320 | NSString *link = attributeDictionary[@"href"]; 321 | // Sometimes, an a tag may not have a corresponding href attribute. 322 | // This should not be added as an attribute. 323 | if (link) 324 | { 325 | [nodeAttributedString addAttribute:NSLinkAttributeName value:link range:NSMakeRange(0, title.length)]; 326 | } 327 | } 328 | } 329 | 330 | // New Lines 331 | else if (strncmp("br", (const char *)xmlNode->name, strlen((const char *)xmlNode->name)) == 0) { 332 | [nodeAttributedString appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]]; 333 | } 334 | 335 | // Images 336 | else if (strncmp("img", (const char *)xmlNode->name, strlen((const char *)xmlNode->name)) == 0) { 337 | #if __has_include() 338 | NSString *src = attributeDictionary[@"src"]; 339 | NSString *width = attributeDictionary[@"width"]; 340 | NSString *height = attributeDictionary[@"height"]; 341 | 342 | if (src != nil) { 343 | UIImage *image = imageMap[src]; 344 | if (image == nil) { 345 | image = [UIImage imageNamed:src]; 346 | } 347 | 348 | if (image != nil) { 349 | NSTextAttachment *imageAttachment = [[NSTextAttachment alloc] init]; 350 | imageAttachment.image = image; 351 | if (width != nil && height != nil) { 352 | imageAttachment.bounds = CGRectMake(0, 0, [width integerValue] / 2, [height integerValue] / 2); 353 | } 354 | NSAttributedString *imageAttributeString = [NSAttributedString attributedStringWithAttachment:imageAttachment]; 355 | [nodeAttributedString appendAttributedString:imageAttributeString]; 356 | } 357 | } 358 | #endif 359 | } 360 | } 361 | 362 | return nodeAttributedString; 363 | } 364 | 365 | + (UIFont *)fontOrSystemFontForName:(NSString *)fontName size:(CGFloat)fontSize { 366 | UIFont * font = [UIFont fontWithName:fontName size:fontSize]; 367 | if(font) { 368 | return font; 369 | } 370 | return [UIFont systemFontOfSize:fontSize]; 371 | } 372 | 373 | + (UIColor *)colorFromHexString:(NSString *)hexString 374 | { 375 | if (hexString == nil) 376 | return nil; 377 | 378 | hexString = [hexString stringByReplacingOccurrencesOfString:@"#" withString:@""]; 379 | char *p; 380 | NSUInteger hexValue = strtoul([hexString cStringUsingEncoding:NSUTF8StringEncoding], &p, 16); 381 | 382 | return [UIColor colorWithRed:((hexValue & 0xff0000) >> 16) / 255.0 green:((hexValue & 0xff00) >> 8) / 255.0 blue:(hexValue & 0xff) / 255.0 alpha:1.0]; 383 | } 384 | 385 | @end 386 | -------------------------------------------------------------------------------- /NSAttributedString-DDHTML.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'NSAttributedString-DDHTML' 3 | s.version = '1.2.0' 4 | s.license = { 5 | :type => 'BSD', 6 | :text => <<-LICENSE 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | * Neither the name of the nor the 15 | names of its contributors may be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | LICENSE 29 | } 30 | s.summary = 'Easily create NSAttributedStrings using HTML.' 31 | s.homepage = 'https://github.com/dbowen/NSAttributedString-DDHTML' 32 | s.author = { 'Derek Bowen' => 'dbowen@demiurgic.co' } 33 | s.source = { :git => 'https://github.com/dbowen/NSAttributedString-DDHTML.git', :tag => "v#{s.version}" } 34 | s.description = 'Simplifies working with NSAttributedString by allowing you to use HTML to describe formatting behaviors.' 35 | s.requires_arc = true 36 | s.ios.deployment_target = '7.0' 37 | s.tvos.deployment_target = '9.0' 38 | s.watchos.deployment_target = '2.0' 39 | 40 | s.source_files = 'NSAttributedString+DDHTML' 41 | s.libraries = 'xml2' 42 | s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' } 43 | end 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NSAttributedString+DDHTML 2 | ========================= 3 | 4 | Simplifies working with attributed strings by allowing you to use HTML to describe formatting behaviors. 5 | 6 | NSAttributedString+DDHTML isn't intended to support full HTML rendering. Instead it provides a quick, effient and light-weight implementation for leveraging attributed strings when utilizing native UIKit interface elements. 7 | 8 | ## License 9 | ----- 10 | 11 | It is open source and covered by a standard BSD license. That means you have to mention *Derek Bowen @ Deloite Digital* as the original author of this code. 12 | 13 | ## Requirements 14 | ----- 15 | NSAttributedString+DDHTML requires a minimum iOS deployment target of iOS 7.0 because of: 16 | 17 | * NSTextAttachment 18 | 19 | ## Setup 20 | ----- 21 | 22 | ### Using CocoaPods 23 | 24 | One of the easiest ways to integrate NSAttributedString+DDHTML in your project is to use [CocoaPods](http://cocoapods.org/): 25 | 26 | 1. Add the following line to your `Podfile`: 27 | 28 | ````ruby 29 | pod "NSAttributedString-DDHTML" 30 | ```` 31 | 32 | 2. In your project directory, run `pod update` 33 | 3. You should now be able to add `#import ` to any of your target's source files to use the library! 34 | 35 | ### Manual 36 | 37 | 1. Add NSAttributedString+DDHTML.m/h to your project. 38 | 2. Add *libxml2.dylib* to the *"Link Binary With Libraries"* section of your target's build phase. 39 | 3. Add *${SDKROOT}/usr/include/libxml2* to your project's *Header Search Paths* under *Build Settings*. 40 | 4. Start using it! 41 | 42 | ## Usage 43 | ----- 44 | #import "NSAttributedString+DDHTML.h" 45 | 46 | ... 47 | 48 | NSAttributedString *attributedString = [NSAttributedString attributedStringFromHTML:@"My formatted string."]; 49 | 50 | ... 51 | 52 | 53 | ## Supported Tags 54 | ----- 55 | 56 | ### b, strong - Bold (iOS, watchOS, tvOS) 57 | 58 | ### i - Italics (iOS, watchOS, tvOS) 59 | 60 | ### u - Underline (iOS, watchOS, tvOS) 61 | 62 | ### strike - Strikethrough (iOS, watchOS, tvOS) 63 | 64 | ### stroke - Stroke (iOS, watchOS, tvOS) 65 | * **color**: Color of stroke, e.g. stroke="#ff0000" 66 | * **width**: Width of stroke, e.g. stroke="2.0" 67 | * **nofill**: If present text color will be transparent 68 | 69 | ### shadow - Shadow (iOS, tvOS) 70 | * **offset**: Amount to offset shadow from center of text, e.g. offset="{1.0, 1.0}" 71 | * **blurRadius**: Radius/thickness of the shadow 72 | * **color**: Color of the shadow 73 | 74 | ### font - Font (iOS, watchOS, tvOS) 75 | * **face**: Name of font to use, e.g. face="Avenir-Heavy" 76 | * **size**: Size of the text, e.g. size="12.0" 77 | * **color**: Color of the text, e.g. color="#fafafa" 78 | * **backgroundColor**: Color of the text background, e.g. backgroundColor="#333333" 79 | 80 | ### br - Line Break (iOS, watchOS, tvOS) 81 | 82 | ### p - Paragraph (iOS, watchOS, tvOS) 83 | * **align**: Alignment of text, e.g. align="center" 84 | * Available values: left, center, right, justify 85 | * **lineBreakMode**: How to handle text which doesn't fit horizontally in the view 86 | * Available values: WordWrapping, CharWrapping, Clipping, TruncatingHead, TruncatingTail, TruncatingMiddle 87 | * **firstLineHeadIndent** 88 | * **headIndent** 89 | * **hyphenationFactor** 90 | * **lineHeightMultiple** 91 | * **lineSpacing** 92 | * **maximumLineHeight** 93 | * **minimumLineHeight** 94 | * **paragraphSpacing** 95 | * **paragraphSpacingBefore** 96 | * **tailIndent** 97 | 98 | ### img - Image (iOS, tvOS) 99 | * src : key in imagerMapper parameter 100 | * width : px 101 | * height : px 102 | 103 | --------------------------------------------------------------------------------