Last active
May 29, 2019 04:56
-
-
Save robnadin/bc780da7edb3aaaa5443 to your computer and use it in GitHub Desktop.
NSAttributedString-HTMLStyle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// NSAttributedString+HTMLStyle.h | |
// QRContentMobilizer | |
// | |
// Created by Wojciech Czekalski on 22.03.2014. | |
// Copyright (c) 2014 Wojciech Czekalski. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
typedef NSString QRHTMLAttribute; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeParagraph; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeLink; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeHeader1; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeHeader2; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeHeader3; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeHeader4; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeHeader5; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeHeader6; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeEmphasis; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeStrong; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeBold; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeAlternateVoice; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeSmall; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeSubscripted; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeSuperscripted; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeInserted; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeDeleted; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeHighlighted; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeCode; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeKeyboardText; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeSampleCode; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeVariable; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributePreformatted; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeAbbreviation; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeAddress; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeBlockQuote; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeInlineQuote; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeTitle; | |
FOUNDATION_EXPORT QRHTMLAttribute * const QRHTMLAttributeDefinition; | |
/** | |
* Creation of NSAttributedString out of HTML with per-tag attributes made easy. | |
*/ | |
@interface NSAttributedString (HTMLStyle) | |
/** | |
* Creates an attributed string from HTML data. Assumes `NSUTF8StringEncoding`. | |
* | |
* @param data Data to be processed. | |
* | |
* @return Returns a new instance of `NSAttributedString` or nil if given data was invalid. | |
*/ | |
+ (instancetype)attributedStringFromHTMLData:(NSData *)data; | |
/** | |
* Creates an attributed string from HTML string and CSS string with attributes. | |
* | |
* @param html A string containing HTML. | |
* @param css A CSS string. If nil, use attributedStringFromHTMLData: instead. | |
* | |
* @abstract Don't convert your HTML and CSS into NSStrings from NSData objects. Use `attributedStringFromHTMLData:CSSData:` instead. | |
* | |
* @return Returns a new instance of `NSAttributedString` or nil if given data was invalid. | |
*/ | |
+ (instancetype)attributedStringFromHTMLString:(NSString *)html CSSString:(NSString *)css; | |
/** | |
* Creates an attributed string from merged HTML and CSS data. | |
* | |
* @param html HTML data to be processed | |
* @param css CSS data to be processed | |
* | |
* @return Returns a new instance of NSAttributedString or nil if given data was invalid. | |
*/ | |
+ (instancetype)attributedStringFromHTMLData:(NSData *)html CSSData:(NSData *)css; | |
/** | |
* Creates an attributed string from merged HTML and CSS files. | |
* | |
* @param html URL of a CSS file. | |
* @param css URL of a CSS file. | |
* | |
* @return Returns a new instance of NSAttributedString or nil if given data was invalid. | |
*/ | |
+ (instancetype)attributedStringFromHTMLFileAtURL:(NSURL *)html CSSURL:(NSURL *)css; | |
/** | |
* Creates an attributed string from HTML data and attributes parsed from attributes dict. | |
* | |
* @param data HTML data to be processed | |
* @param attributes Key-Value pairs of NSAttributedString attributes keyed under HTML tags. | |
* | |
* @return Returns a new instance of NSAttributedString or nil if given data was invalid. | |
*/ | |
+ (instancetype)attributedStringFromHTMLData:(NSData *)data attributes:(NSDictionary *)attributes; | |
@end | |
/** | |
* Convinience methods for dealing with adding attributes to `NSAttributedStrings` from HTML. | |
*/ | |
@interface NSMutableDictionary (CSS) | |
/** | |
* Sets attributes for a given HTML tag. | |
* | |
* @param attributes NSDictionary containing key-value pairs of `NSAttributedString` attributes. | |
* @param tag `HTML` tag to be assigned to the attributes | |
* @param flatten if flatten set to yes, the dictionary is converted into CSS string before being added to the receiver. | |
*/ | |
- (void)addAttributes:(NSDictionary *)attributes forHTMLAttribute:(QRHTMLAttribute *)tag flatten:(BOOL)flatten; | |
@end | |
@interface NSDictionary (CSS) | |
/** | |
* Parses receiver and creates a `NSString` with `CSS` attributes. | |
* | |
* @return Returns a `NSString` with `CSS` attributes. | |
*/ | |
- (NSString *)CSSStringFromAttributes; | |
@end | |
/** | |
* NSData category which adds method allowing to replace occurencies of given data with another data. | |
*/ | |
@interface NSData (HTMLAdditions) | |
/** | |
* Creates `NSData` instance with replaced occurencies. | |
* | |
* @param data `NSData` to be replaced. | |
* @param replacementData `NSData` to be inserted in place of `data`. | |
* | |
* This method replaces all occurencies of `data` with `replacementData`. | |
* | |
* @return Returns `NSData` with replaced contents. | |
*/ | |
- (NSData *)dataByReplacingOccurrencesOfData:(NSData *)data withData:(NSData *)replacementData; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// NSAttributedString+HTMLStyle.m | |
// QRContentMobilizer | |
// | |
// Created by Wojciech Czekalski on 22.03.2014. | |
// Copyright (c) 2014 Wojciech Czekalski. All rights reserved. | |
// | |
#import "NSAttributedString+HTMLStyle.h" | |
#import "regex.h" | |
QRHTMLAttribute * const QRHTMLAttributeParagraph = @"p"; | |
QRHTMLAttribute * const QRHTMLAttributeLink = @"a"; | |
QRHTMLAttribute * const QRHTMLAttributeHeader1 = @"h1"; | |
QRHTMLAttribute * const QRHTMLAttributeHeader2 = @"h2"; | |
QRHTMLAttribute * const QRHTMLAttributeHeader3 = @"h3"; | |
QRHTMLAttribute * const QRHTMLAttributeHeader4 = @"h4"; | |
QRHTMLAttribute * const QRHTMLAttributeHeader5 = @"h5"; | |
QRHTMLAttribute * const QRHTMLAttributeHeader6 = @"h6"; | |
QRHTMLAttribute * const QRHTMLAttributeEmphasis = @"em"; | |
QRHTMLAttribute * const QRHTMLAttributeStrong = @"strong"; | |
QRHTMLAttribute * const QRHTMLAttributeBold = @"b"; | |
QRHTMLAttribute * const QRHTMLAttributeAlternateVoice = @"i"; | |
QRHTMLAttribute * const QRHTMLAttributeSmall = @"small"; | |
QRHTMLAttribute * const QRHTMLAttributeSubscripted = @"sub"; | |
QRHTMLAttribute * const QRHTMLAttributeSuperscripted = @"sup"; | |
QRHTMLAttribute * const QRHTMLAttributeInserted = @"ins"; | |
QRHTMLAttribute * const QRHTMLAttributeDeleted = @"del"; | |
QRHTMLAttribute * const QRHTMLAttributeHighlighted = @"mark"; | |
QRHTMLAttribute * const QRHTMLAttributeCode = @"code"; | |
QRHTMLAttribute * const QRHTMLAttributeKeyboardText = @"kbd"; | |
QRHTMLAttribute * const QRHTMLAttributeSampleCode = @"samp"; | |
QRHTMLAttribute * const QRHTMLAttributeVariable = @"var"; | |
QRHTMLAttribute * const QRHTMLAttributePreformatted = @"pre"; | |
QRHTMLAttribute * const QRHTMLAttributeAbbreviation = @"abbr"; | |
QRHTMLAttribute * const QRHTMLAttributeAddress = @"address"; | |
QRHTMLAttribute * const QRHTMLAttributeBlockQuote = @"blockquote"; | |
QRHTMLAttribute * const QRHTMLAttributeInlineQuote = @"q"; | |
QRHTMLAttribute * const QRHTMLAttributeTitle = @"cite"; | |
QRHTMLAttribute * const QRHTMLAttributeDefinition = @"dfn"; | |
@implementation NSAttributedString (HTMLStyle) | |
+ (instancetype)attributedStringFromHTMLFileAtURL:(NSURL *)html CSSURL:(NSURL *)css { | |
return [self attributedStringFromHTMLData:[NSData dataWithContentsOfURL:html] CSSData:[NSData dataWithContentsOfURL:css]]; | |
} | |
+ (instancetype)attributedStringFromHTMLString:(NSString *)html CSSString:(NSString *)css { | |
return [self attributedStringFromHTMLData:[html dataUsingEncoding:NSUTF8StringEncoding] CSSData:[css dataUsingEncoding:NSUTF8StringEncoding]]; | |
} | |
+ (instancetype)attributedStringFromHTMLData:(NSData *)data { | |
return [[self alloc] initWithData:data options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)} documentAttributes:NULL error:nil]; | |
} | |
+ (instancetype)attributedStringFromHTMLData:(NSData *)html CSSData:(NSData *)css { | |
if (css) { | |
NSMutableData *newHTMLData = [css mutableCopy]; | |
[newHTMLData appendData:html]; | |
return [self attributedStringFromHTMLData:newHTMLData]; | |
} | |
return [self attributedStringFromHTMLData:html]; | |
} | |
+ (instancetype)attributedStringFromHTMLData:(NSData *)data attributes:(NSDictionary *)attributes { | |
return [self attributedStringFromHTMLData:data CSSData:[[attributes CSSStringFromAttributes] dataUsingEncoding:NSUTF8StringEncoding]]; | |
} | |
@end | |
@interface UIColor (HTMLRGBString) | |
- (NSString *)HTMLRGBString; | |
@end | |
@implementation UIColor (HTMLRGBString) | |
- (NSString *)HTMLRGBString { | |
UIColor *color = self; | |
CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0; | |
if (CGColorGetNumberOfComponents(color.CGColor) == 2) { | |
CGFloat white = 0.f, alpha = 0.f; | |
[color getWhite:&white alpha:&alpha]; | |
red = white; | |
green = white; | |
blue = white; | |
} else { | |
[color getRed:&red green:&green blue:&blue alpha:&alpha]; | |
} | |
return [NSString stringWithFormat:@"rgb(%li, %li, %li)", lroundf(red*255.f), lroundf(green*255.f), lroundf(blue*255.f)]; | |
} | |
@end | |
@implementation NSDictionary (CSS) | |
+ (NSString *)cssStringWithValues:(NSDictionary *)attributes HTMLTag:(QRHTMLAttribute *)tag { | |
NSMutableString *cssString = [NSMutableString stringWithFormat:@"%@ {", tag]; | |
for (NSString *key in attributes) { | |
#if DEBUG | |
NSSet *set = [NSSet setWithObjects:NSLigatureAttributeName, NSKernAttributeName, NSStrikethroughStyleAttributeName, NSStrikethroughColorAttributeName, NSUnderlineStyleAttributeName, NSStrokeColorAttributeName, NSStrokeWidthAttributeName, NSTextEffectAttributeName, NSObliquenessAttributeName, NSExpansionAttributeName, NSWritingDirectionAttributeName, NSVerticalGlyphFormAttributeName, NSUnderlineColorAttributeName, NSAttachmentAttributeName, NSLinkAttributeName, NSBaselineOffsetAttributeName, nil]; | |
NSAssert(![set containsObject:key], @"Invalid Key. This library doesn't support %@ yet.", key); | |
#endif | |
if (key == NSFontAttributeName) { | |
UIFont *font = attributes[NSFontAttributeName]; | |
[cssString appendFormat:@"font-family:'%@'; font-size: %0.fpx; ", font.fontName, roundf(font.pointSize)];; | |
} else if (key == NSParagraphStyleAttributeName) { | |
NSParagraphStyle *style = attributes[key]; | |
NSString *alignment; | |
switch (style.alignment) { | |
case NSTextAlignmentCenter: | |
alignment = @"center"; | |
break; | |
case NSTextAlignmentJustified: | |
alignment = @"justify"; | |
break; | |
case NSTextAlignmentLeft: | |
alignment = @"left"; | |
break; | |
case NSTextAlignmentNatural: | |
alignment = @"initial"; | |
break; | |
case NSTextAlignmentRight: | |
alignment = @"right"; | |
break; | |
default: | |
alignment = @"left"; | |
break; | |
} | |
[cssString appendFormat:@"line-height:%0.1fem; text-align:%@;", style.lineHeightMultiple, alignment]; | |
} else if (key == NSForegroundColorAttributeName) { | |
[cssString appendFormat:@"color:%@; ", [attributes[key] HTMLRGBString]]; | |
} else if (key == NSBackgroundColorAttributeName ) { | |
[cssString appendFormat:@"background-color:%@; ", [attributes[key] HTMLRGBString]];; | |
} else if (key == NSShadowAttributeName) { | |
NSShadow *shadow = attributes[NSShadowAttributeName]; | |
[cssString appendFormat:@"text-shadow: %fpx %fpx %fpx %@; ", shadow.shadowOffset.width, shadow.shadowOffset.height, shadow.shadowBlurRadius, [shadow.shadowColor HTMLRGBString]]; | |
} | |
} | |
[cssString appendString:@"}"]; | |
return cssString; | |
} | |
- (NSString *)CSSStringFromAttributes { | |
NSMutableString *string = [NSMutableString stringWithString:@"<style>"]; | |
for (NSString *key in self) { | |
id object = self[key]; | |
if ([object isKindOfClass:[NSDictionary class]]) { | |
[string appendString:[NSDictionary cssStringWithValues:object HTMLTag:key]]; | |
} else if ([object isKindOfClass:[NSString class]]) { | |
[string appendString:object]; | |
} | |
} | |
[string appendString:@"</style>"]; | |
return string; | |
} | |
@end | |
@implementation NSMutableDictionary (CSS) | |
- (void)addAttributes:(NSDictionary *)attributes forHTMLAttribute:(QRHTMLAttribute *)tag flatten:(BOOL)flatten { | |
if (!flatten) { | |
[self setObject:attributes forKey:tag]; | |
} else { | |
[self setObject:[NSDictionary cssStringWithValues:attributes HTMLTag:tag] forKey:tag]; | |
} | |
} | |
@end | |
@implementation NSData (HTMLAdditions) | |
- (NSData *)dataByReplacingOccurrencesOfData:(NSData *)data withData:(NSData *)replacementData { | |
NSMutableData *mutableSelf = [self mutableCopy]; | |
const void *replacementBytes = [replacementData bytes]; | |
NSUInteger replacementBytesLength = [replacementData length]; | |
NSRange rangeOfCharacters = [mutableSelf rangeOfData:data options:0 range:NSMakeRange(0, [mutableSelf length])]; | |
while (rangeOfCharacters.location != NSNotFound) { | |
[mutableSelf replaceBytesInRange:rangeOfCharacters withBytes:replacementBytes]; | |
NSUInteger searchLocation = replacementBytesLength + rangeOfCharacters.location; | |
rangeOfCharacters = [mutableSelf rangeOfData:data options:0 range:NSMakeRange(searchLocation, [mutableSelf length]-searchLocation)]; | |
} | |
return [NSData dataWithData:mutableSelf]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment