Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Committing files.

  • Loading branch information...
commit 335ca7f3be036d4c1a35c35edac3664a50065a57 1 parent a4d16d9
Brandon Williams authored
25 NSAttributedString+Encoding.h
... ... @@ -0,0 +1,25 @@
  1 +//
  2 +// NSAttributedString+Opetopic.h
  3 +// NSAttributedString+Opetopic
  4 +//
  5 +// Created by Brandon Williams on 4/6/12.
  6 +// Copyright (c) 2012 Opetopic. All rights reserved.
  7 +//
  8 +
  9 +#import <Foundation/Foundation.h>
  10 +#import <CoreText/CoreText.h>
  11 +
  12 +@interface NSAttributedString (Encoding)
  13 +
  14 +/**
  15 + Create an NSAttributedString from an NSData object (which must have been created by the `-convertToData` method).
  16 + */
  17 ++(id) attributedStringWithData:(NSData*)data;
  18 +
  19 +/**
  20 + Convert an NSAttributedString to an NSData object. I would have loved to just call this method `-data`, but alas
  21 + that may conflict with future methods on this class.
  22 + */
  23 +-(NSData*) convertToData;
  24 +
  25 +@end
225 NSAttributedString+Encoding.m
... ... @@ -0,0 +1,225 @@
  1 +//
  2 +// NSAttributedString+Opetopic.m
  3 +// NSAttributedString+Opetopic
  4 +//
  5 +// Created by Brandon Williams on 4/6/12.
  6 +// Copyright (c) 2012 Opetopic. All rights reserved.
  7 +//
  8 +
  9 +#import "NSAttributedString+Encoding.h"
  10 +
  11 +const struct NSAttributedStringArchiveKeys {
  12 + __unsafe_unretained NSString *rootString;
  13 + __unsafe_unretained NSString *attributes;
  14 + __unsafe_unretained NSString *attributeDictionary;
  15 + __unsafe_unretained NSString *attributeRange;
  16 +} NSAttributedStringArchiveKeys;
  17 +
  18 +const struct NSAttributedStringArchiveKeys NSAttributedStringArchiveKeys = {
  19 + .rootString = @"rootString",
  20 + .attributes = @"attributes",
  21 + .attributeDictionary = @"attributeDictionary",
  22 + .attributeRange = @"attributeRange",
  23 +};
  24 +
  25 +@interface NSAttributedString (Encoding_Private)
  26 +-(NSDictionary*) dictionaryRepresentation;
  27 ++(id) attributedStringWithDictionaryRepresentation:(NSDictionary*)dictionary;
  28 ++(NSDictionary*) dictionaryRepresentationOfFont:(CTFontRef)fontRef;
  29 ++(CTFontRef) fontFromDictionaryRepresentation:(NSDictionary*)dictionary;
  30 +@end
  31 +
  32 +@implementation NSAttributedString (Encoding)
  33 +
  34 ++(id) attributedStringWithData:(NSData*)data {
  35 + return [self attributedStringWithDictionaryRepresentation:[NSKeyedUnarchiver unarchiveObjectWithData:data]];
  36 +}
  37 +
  38 +-(NSData*) convertToData {
  39 + return [NSKeyedArchiver archivedDataWithRootObject:[self dictionaryRepresentation]];
  40 +}
  41 +
  42 +@end
  43 +
  44 +
  45 +@implementation NSAttributedString (Encoding_Private)
  46 +
  47 ++(id) attributedStringWithDictionaryRepresentation:(NSDictionary*)dictionary {
  48 +
  49 + NSString *string = [dictionary objectForKey:NSAttributedStringArchiveKeys.rootString];
  50 + NSMutableAttributedString *retVal = [[NSMutableAttributedString alloc] initWithString:string];
  51 +
  52 + [[dictionary arrayForKey:NSAttributedStringArchiveKeys.attributes] enumerateObjectsUsingBlock:^(NSDictionary *attribute, NSUInteger idx, BOOL *stop) {
  53 +
  54 + NSDictionary *attributeDictionary = [attribute dictionaryForKey:NSAttributedStringArchiveKeys.attributeDictionary];
  55 + NSRange range = NSRangeFromString([attribute objectForKey:NSAttributedStringArchiveKeys.attributeRange]);
  56 +
  57 + [attributeDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id attr, BOOL *stop) {
  58 +
  59 + if ([key isEqual:(NSString*)kCTFontAttributeName])
  60 + {
  61 + CTFontRef fontRef = [[self class] fontFromDictionaryRepresentation:attr];
  62 + [retVal addAttribute:key value:(__bridge id)fontRef range:range];
  63 + }
  64 + else if([key isEqualToString:(NSString*)kCTForegroundColorFromContextAttributeName] ||
  65 + [key isEqualToString:(NSString*)kCTKernAttributeName] ||
  66 + [key isEqualToString:(NSString*)kCTStrokeWidthAttributeName] ||
  67 + [key isEqualToString:(NSString*)kCTLigatureAttributeName] ||
  68 + [key isEqualToString:(NSString*)kCTSuperscriptAttributeName] ||
  69 + [key isEqualToString:(NSString*)kCTUnderlineStyleAttributeName] ||
  70 + [key isEqualToString:(NSString*)kCTCharacterShapeAttributeName])
  71 + {
  72 + [retVal addAttribute:key value:attr range:range];
  73 + }
  74 + else if([key isEqualToString:(NSString*)kCTForegroundColorAttributeName] ||
  75 + [key isEqualToString:(NSString*)kCTStrokeColorAttributeName] ||
  76 + [key isEqualToString:(NSString*)kCTUnderlineColorAttributeName])
  77 + {
  78 + [retVal addAttribute:key value:(id)[attr CGColor] range:range];
  79 + }
  80 + else if([key isEqualToString:(NSString*)kCTParagraphStyleAttributeName])
  81 + {
  82 + CTParagraphStyleSetting settings[[attr count]];
  83 + int settingIndex = 0;
  84 +
  85 +#define PARAGRAPH_SETTING(datatype, specifier, container) \
  86 + datatype container = sizeof(datatype) == sizeof(CGFloat) ? [[attr objectForKey:[NSNumber numberWithInt:specifier]] floatValue] : [[attr objectForKey:[NSNumber numberWithInt:specifier]] intValue]; \
  87 + settings[settingIndex].spec = specifier; \
  88 + settings[settingIndex].valueSize = sizeof(datatype); \
  89 + settings[settingIndex].value = &container; \
  90 + settingIndex++; \
  91 +
  92 + PARAGRAPH_SETTING(uint8_t, kCTParagraphStyleSpecifierAlignment, alignment);
  93 + PARAGRAPH_SETTING(CGFloat, kCTParagraphStyleSpecifierFirstLineHeadIndent, firstLineHeadIndent);
  94 + PARAGRAPH_SETTING(CGFloat, kCTParagraphStyleSpecifierHeadIndent, headIndent);
  95 + PARAGRAPH_SETTING(CGFloat, kCTParagraphStyleSpecifierTailIndent, tailIndent);
  96 + PARAGRAPH_SETTING(CGFloat, kCTParagraphStyleSpecifierDefaultTabInterval, defaultTabInterval);
  97 + PARAGRAPH_SETTING(uint8_t, kCTParagraphStyleSpecifierLineBreakMode, linebreakMode);
  98 + PARAGRAPH_SETTING(CGFloat, kCTParagraphStyleSpecifierLineHeightMultiple, lineHeightMultiple);
  99 + PARAGRAPH_SETTING(CGFloat, kCTParagraphStyleSpecifierMaximumLineHeight, maximumLineHeight);
  100 + PARAGRAPH_SETTING(CGFloat, kCTParagraphStyleSpecifierMinimumLineHeight, minimumLineHeight);
  101 + PARAGRAPH_SETTING(CGFloat, kCTParagraphStyleSpecifierLineSpacing, lineSpacing);
  102 + PARAGRAPH_SETTING(CGFloat, kCTParagraphStyleSpecifierParagraphSpacing, paragraphSpacing);
  103 + PARAGRAPH_SETTING(CGFloat, kCTParagraphStyleSpecifierParagraphSpacingBefore, paragraphSpacingBefore);
  104 + PARAGRAPH_SETTING(int8_t, kCTParagraphStyleSpecifierBaseWritingDirection, baseWritingDirection);
  105 +
  106 + CTParagraphStyleRef paragraphStyleRef = CTParagraphStyleCreate(settings, [attr count]);
  107 +
  108 + [retVal addAttribute:key value:(__bridge id)paragraphStyleRef range:range];
  109 + }
  110 + else if([key isEqualToString:(NSString*)kCTGlyphInfoAttributeName])
  111 + {
  112 + // TODO
  113 + }
  114 + else if([key isEqualToString:(NSString*)kCTRunDelegateAttributeName])
  115 + {
  116 + // TODO
  117 + }
  118 +
  119 + }];
  120 +
  121 + }];
  122 +
  123 + return retVal;
  124 +}
  125 +
  126 +-(NSDictionary*) dictionaryRepresentation {
  127 +
  128 + NSMutableDictionary *retVal = [NSMutableDictionary new];
  129 +
  130 + [retVal setObject:[self string] forKey:NSAttributedStringArchiveKeys.rootString];
  131 +
  132 + NSMutableArray *attributes = [NSMutableArray new];
  133 + [retVal setObject:attributes forKey:NSAttributedStringArchiveKeys.attributes];
  134 +
  135 + [self enumerateAttributesInRange:NSMakeRange(0, [self length]) options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
  136 +
  137 + NSMutableDictionary *attribute = [NSMutableDictionary new];
  138 + [attributes addObject:attribute];
  139 +
  140 + [attribute setObject:NSStringFromRange(range) forKey:NSAttributedStringArchiveKeys.attributeRange];
  141 + NSMutableDictionary *attributeDictionary = [NSMutableDictionary new];
  142 + [attribute setObject:attributeDictionary forKey:NSAttributedStringArchiveKeys.attributeDictionary];
  143 +
  144 + [attrs enumerateKeysAndObjectsUsingBlock:^(id key, id attr, BOOL *stop) {
  145 +
  146 + if ([key isEqual:(NSString*)kCTFontAttributeName])
  147 + {
  148 + [attributeDictionary setObject:[[self class] dictionaryRepresentationOfFont:(CTFontRef)attr] forKey:key];
  149 + }
  150 + else if([key isEqualToString:(NSString*)kCTForegroundColorFromContextAttributeName] ||
  151 + [key isEqualToString:(NSString*)kCTKernAttributeName] ||
  152 + [key isEqualToString:(NSString*)kCTStrokeWidthAttributeName] ||
  153 + [key isEqualToString:(NSString*)kCTLigatureAttributeName] ||
  154 + [key isEqualToString:(NSString*)kCTSuperscriptAttributeName] ||
  155 + [key isEqualToString:(NSString*)kCTUnderlineStyleAttributeName] ||
  156 + [key isEqualToString:(NSString*)kCTCharacterShapeAttributeName])
  157 + {
  158 + [attributeDictionary setObject:attr forKey:key];
  159 + }
  160 + else if([key isEqualToString:(NSString*)kCTForegroundColorAttributeName] ||
  161 + [key isEqualToString:(NSString*)kCTStrokeColorAttributeName] ||
  162 + [key isEqualToString:(NSString*)kCTUnderlineColorAttributeName])
  163 + {
  164 + [attributeDictionary setObject:[UIColor colorWithCGColor:(CGColorRef)attr] forKey:key];
  165 + }
  166 + else if([key isEqualToString:(NSString*)kCTParagraphStyleAttributeName])
  167 + {
  168 + NSMutableDictionary *paragraphDictionary = [NSMutableDictionary new];
  169 +
  170 +#define SPECIFIER_VALUE(datatype, specifier, container) {\
  171 + datatype container; \
  172 + CTParagraphStyleGetValueForSpecifier((__bridge CTParagraphStyleRef)attr, specifier, sizeof(datatype), &container); \
  173 + [paragraphDictionary setObject:sizeof(datatype)==sizeof(CGFloat) ? [NSNumber numberWithFloat:container] : [NSNumber numberWithInt:container] forKey:[NSNumber numberWithInt:specifier]]; \
  174 +}
  175 + SPECIFIER_VALUE(uint8_t, kCTParagraphStyleSpecifierAlignment, alignment);
  176 + SPECIFIER_VALUE(CGFloat, kCTParagraphStyleSpecifierFirstLineHeadIndent, firstLineHeadIndent);
  177 + SPECIFIER_VALUE(CGFloat, kCTParagraphStyleSpecifierHeadIndent, headIndent);
  178 + SPECIFIER_VALUE(CGFloat, kCTParagraphStyleSpecifierTailIndent, tailIndent);
  179 + SPECIFIER_VALUE(CGFloat, kCTParagraphStyleSpecifierDefaultTabInterval, defaultTabInterval);
  180 + SPECIFIER_VALUE(uint8_t, kCTParagraphStyleSpecifierLineBreakMode, linebreakMode);
  181 + SPECIFIER_VALUE(CGFloat, kCTParagraphStyleSpecifierLineHeightMultiple, lineHeightMultiple);
  182 + SPECIFIER_VALUE(CGFloat, kCTParagraphStyleSpecifierMaximumLineHeight, maximumLineHeight);
  183 + SPECIFIER_VALUE(CGFloat, kCTParagraphStyleSpecifierMinimumLineHeight, minimumLineHeight);
  184 + SPECIFIER_VALUE(CGFloat, kCTParagraphStyleSpecifierLineSpacing, lineSpacing);
  185 + SPECIFIER_VALUE(CGFloat, kCTParagraphStyleSpecifierParagraphSpacing, paragraphSpacing);
  186 + SPECIFIER_VALUE(CGFloat, kCTParagraphStyleSpecifierParagraphSpacingBefore, paragraphSpacingBefore);
  187 + SPECIFIER_VALUE(int8_t, kCTParagraphStyleSpecifierBaseWritingDirection, baseWritingDirection);
  188 + [attributeDictionary setObject:paragraphDictionary forKey:key];
  189 + }
  190 + else if([key isEqualToString:(NSString*)kCTGlyphInfoAttributeName])
  191 + {
  192 + // TODO
  193 + }
  194 + else if([key isEqualToString:(NSString*)kCTRunDelegateAttributeName])
  195 + {
  196 + // TODO
  197 + }
  198 +
  199 + }];
  200 +
  201 + }];
  202 +
  203 + return retVal;
  204 +}
  205 +
  206 ++(NSDictionary*) dictionaryRepresentationOfFont:(CTFontRef)fontRef {
  207 +
  208 + NSDictionary *retVal = nil;
  209 + CTFontDescriptorRef descriptorRef = CTFontCopyFontDescriptor(fontRef);
  210 + CFDictionaryRef attributesRef = CTFontDescriptorCopyAttributes(descriptorRef);
  211 + retVal = (__bridge_transfer NSDictionary*)attributesRef;
  212 + CFRelease(descriptorRef);
  213 + return retVal;
  214 +}
  215 +
  216 ++(CTFontRef) fontFromDictionaryRepresentation:(NSDictionary*)dictionary {
  217 +
  218 + CTFontRef retVal = NULL;
  219 + CTFontDescriptorRef descriptorRef = CTFontDescriptorCreateWithAttributes((__bridge_retained CFDictionaryRef)dictionary);
  220 + retVal = CTFontCreateWithFontDescriptor(descriptorRef, 0.0f, NULL);
  221 + CFRelease(descriptorRef);
  222 + return retVal;
  223 +}
  224 +
  225 +@end
17 NSAttributedString+Encoding.podspec
... ... @@ -0,0 +1,17 @@
  1 +Pod::Spec.new do |s|
  2 + s.name = 'NSAttributedString+Encoding'
  3 + s.version = '0.1'
  4 + s.license = 'MIT'
  5 +
  6 + s.summary = 'NSAttributedString+Encoding'
  7 + s.homepage = 'https://github.com/mbrandonw/NSAttributedString-Encoding'
  8 + s.author = { 'Brandon Williams' => 'brandon@opetopic.com' }
  9 + s.source = { :git => 'git@github.com:mbrandonw/NSAttributedString-Encoding.git' }
  10 + s.requires_arc = true
  11 +
  12 + s.source_files = '*.{h,m}'
  13 + s.requires_arc = true
  14 +
  15 + s.frameworks = 'CoreText'
  16 +
  17 +end

0 comments on commit 335ca7f

Please sign in to comment.
Something went wrong with that request. Please try again.