I have a NSAttributedString
object as a property of a custom object. I need to save this custom object to the disk in JSON format. Later I need to
You could try starting with RTFFromRange:
From the documentation: For information about the OS X methods supporting RTF, ..., see NSAttributedString Application Kit Additions Reference.
RTF should be self contained. RTFFromRange: returns NSData; I would think its probably character data in some encoding so should be easy to convert to NSString.
(Sorry, just read that method is MacOS X only).
NSAttributedString has two properties:
Each "run" has:
It would be very easy to represent that as JSON, using enumerateAttributesInRange:options:usingBlock:
.
Something like:
{
"string" : "Hello World",
"runs" : [
{
"range" : [0,3],
"attributes" : {
"font" : {
"name" : "Arial",
"size" : 12
}
}
},
{
"range" : [3,6],
"attributes" : {
"font" : {
"name" : "Arial",
"size" : 12
},
"color" : [255,0,0]
}
},
{
"range" : [9,2],
"attributes" : {
"font" : {
"name" : "Arial",
"size" : 12
}
}
}
]
}
EDIT: here's an example implementation:
// create a basic attributed string
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:@"Hello World" attributes:@{NSFontAttributeName: [NSFont fontWithName:@"Arial" size:12]}];
[attStr addAttribute:NSForegroundColorAttributeName value:[NSColor redColor] range:NSMakeRange(3, 6)];
// build array of attribute runs
NSMutableArray *attributeRuns = [NSMutableArray array];
[attStr enumerateAttributesInRange:NSMakeRange(0, attStr.length) options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
NSArray *rangeArray = @[[NSNumber numberWithUnsignedInteger:range.location],
[NSNumber numberWithUnsignedInteger:range.length]];
NSMutableDictionary *runAttributes = [NSMutableDictionary dictionary];
[attrs enumerateKeysAndObjectsUsingBlock:^(id attributeName, id attributeValue, BOOL *stop) {
if ([attributeName isEqual:NSFontAttributeName]) { // convert font values into a dictionary with the name and size
attributeName = @"font";
attributeValue = @{@"name": [(NSFont *)attributeValue displayName],
@"size": [NSNumber numberWithFloat:[(NSFont *)attributeValue pointSize]]};
} else if ([attributeName isEqualToString:NSForegroundColorAttributeName]) { // convert foreground colour values into an array with red/green/blue as a number from 0 to 255
attributeName = @"color";
attributeValue = @[[NSNumber numberWithInteger:([(NSColor *)attributeValue redComponent] * 255)],
[NSNumber numberWithInteger:([(NSColor *)attributeValue greenComponent] * 255)],
[NSNumber numberWithInteger:([(NSColor *)attributeValue blueComponent] * 255)]];
} else { // skip unknown attributes
NSLog(@"skipping unknown attribute %@", attributeName);
return;
}
[runAttributes setObject:attributeValue forKey:attributeName];
}];
// save the attributes (if there are any)
if (runAttributes.count == 0)
return;
[attributeRuns addObject:@{@"range": rangeArray,
@"attributes": runAttributes}];
}];
// build JSON output
NSDictionary *jsonOutput = @{@"string": attStr.string,
@"runs": attributeRuns};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonOutput options:NSJSONWritingPrettyPrinted error:NULL];
NSLog(@"%@", [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]);
exit(0);
You could use this simple code snippet to convert NSAttributedString
to XML without actually parsing NSAttributedString
. This can be a human-readable alternative to JSON if you can afford verbose text output.
It can be also used for decoding back to NSAttributedString
.
let data = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWithMutableData: data)
archiver.outputFormat = .XMLFormat_v1_0
textView.attributedText.encodeWithCoder(archiver)
archiver.finishEncoding()
let textAsString = NSString(data: data, encoding: NSUTF8StringEncoding)'