Существует способ сохранить пользовательские атрибуты RTF с использованием какао. Он полагается на то, что RTF является текстовым форматом, поэтому его можно манипулировать как строку, даже если вы не знаете все правила RTF и не имеете пользовательского RTF-ридера/записи. Процедура, описанная ниже, обрабатывает RTF как при записи, так и при чтении, и я использовал эту технику лично. Одно очень важно, так это то, что текст, который вы вставляете в RTF, использует только 7-битный ASCII и не имеет неэкранированных управляющих символов, которые включают «\ {}».
Вот как вы бы кодировать данные:
NSData *GetRtfFromAttributedString(NSAttributedString *text)
{
NSData *rtfData = nil;
NSMutableString *rtfString = nil;
NSString *customData = nil, *encodedData = nil;
NSRange range;
NSUInteger dataLocation;
// Convert the attributed string to RTF
if ((rtfData = [text RTFFromRange:NSMakeRange(0, [text length]) documentAttributes:nil]) == nil)
return(nil);
// Find and encode your custom attributes here. In this example the data is a string and there's at most one of them
if ((customData = [text attribute:@"MyCustomData" atIndex:0 effectiveRange:&range]) == nil)
return(rtfData); // No custom data, return RTF as is
dataLocation = range.location;
// Get a string representation of the RTF
rtfString = [[NSMutableString alloc] initWithData:rtfData encoding:NSASCIIStringEncoding];
// Find the anchor where we'll put our data, namely just before the first paragraph property reset
range = [rtfString rangeOfString:@"\\pard" options:NSLiteralSearch];
if (range.location == NSNotFound)
{
NSLog(@"Custom data dropped; RTF has no paragraph properties");
[rtfString release];
return(rtfData);
}
// Insert the starred group containing the custom data and its location
encodedData = [NSString stringWithFormat:@"{\\*\\my_custom_keyword %d,%@}\n", dataLocation, customData];
[rtfString insertString:encodedData atIndex:range.location];
// Convert the amended RTF back to a data object
rtfData = [rtfString dataUsingEncoding:NSASCIIStringEncoding];
[rtfString release];
return(rtfData);
}
Этот метод работает, потому что все совместимые читатели RTF будут игнорировать «избранные группы», чьи ключевые слова они не признают. Поэтому вы хотите быть уверенным, что ваше контрольное слово не будет распознано каким-либо другим читателем, поэтому используйте что-то, что может быть уникальным, например префикс вашей компании или название продукта. Если ваши данные сложны или бинарны или могут содержать незаконные символы RTF, которые вы не хотите убегать, закодируйте его в base64. Убедитесь, что вы поместили пробел после своего ключевого слова.
Аналогично, при чтении RTF вы ищете свое управляющее слово, извлекаете данные и восстанавливаете атрибут. Эта процедура принимает в качестве аргументов атрибутивную строку и RTF, из которых она была создана.
void RestoreCustomAttributes(NSMutableAttributedString *text, NSData *rtfData)
{
NSString *rtfString = [[NSString alloc] initWithData:rtfData encoding:NSASCIIStringEncoding];
NSArray *components = nil;
NSRange range, endRange;
// Find the custom data and its end
range = [rtfString rangeOfString:@"{\\*\\my_custom_keyword " options:NSLiteralSearch];
if (range.location == NSNotFound)
{
[rtfString release];
return;
}
range.location += range.length;
endRange = [rtfString rangeOfString:@"}" options:NSLiteralSearch
range:NSMakeRange(range.location, [rtfString length] - endRange.location)];
if (endRange.location == NSNotFound)
{
[rtfString release];
return;
}
// Get the location and the string data, which are separated by a comma
range.length = endRange.location - range.location;
components = [[rtfString substringWithRange:range] componentsSeparatedByString:@","];
[rtfString release];
// Assign the custom data back to the attributed string. You should do range checking here (omitted for clarity)
[text addAttribute:@"MyCustomData" value:[components objectAtIndex:1]
range:NSMakeRange([[components objectAtIndex:0] integerValue], 1)];
}
Конечно, эти данные не будут в формате RTF, поэтому вы не должны указывать в файле расширение .rtf. Лучше всего создать новое расширение и UTI и назвать его новым форматом. –
Это отличный ответ! Спасибо, Роб и Питер. Я могу жить без вывода rtf или rtfd. Наверное, я не думал сразу после разработки новой схемы ... Мне следовало подумать о NSCoder. – regulus6633
Это не работает в iOS для меня. Я получаю сбой на NSCFType encodeWithCoder :, где объект типа выглядит следующим образом: [ (kCGColorSpaceDeviceRGB)] (0.576471 0.576471 0.560784 1) - было бы очень полезно, если бы вы знали, как Я могу кодировать их? –