C'è un modo per salvare attributi personalizzati in formato RTF utilizzando cacao. Si basa sul fatto che RTF è un formato di testo, e quindi può essere manipolato come una stringa anche se non si conoscono tutte le regole di RTF e non si dispone di un lettore/scrittore RTF personalizzato. La procedura che ho delineato di seguito post-processa l'RTF sia in scrittura che in lettura, e ho usato personalmente questa tecnica. Una cosa a cui prestare molta attenzione è che il testo inserito nell'RTF utilizza solo ASCII a 7 bit e nessun carattere di controllo senza caratteri di escape, che includono "\ {}".
Ecco come si potrebbe codificare i dati:
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);
}
Questa tecnica funziona perché tutti i lettori RTF compatibili ignoreranno la cui parola chiave non riconoscono "gruppi recitato". Pertanto, vuoi essere sicuro che la tua parola di controllo non verrà riconosciuta da nessun altro lettore, quindi utilizza qualcosa che potrebbe essere univoco, ad esempio un prefisso con la tua azienda o il nome del prodotto. Se i tuoi dati sono complessi, o binari, o potrebbero contenere caratteri RTF illegali che non vuoi sfuggire, codificali in base64. Assicurati di inserire uno spazio dopo la parola chiave.
Analogamente, durante la lettura dell'RTF, si cerca la parola di controllo, si estraggono i dati e si ripristina l'attributo. Questa routine prende come argomenti la stringa attribuita e l'RTF da cui è stata creata.
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)];
}
Ovviamente, questi dati non saranno nel formato RTF, quindi non si dovrebbe dare al file l'estensione .rtf. Meglio creare una nuova estensione e UTI e chiamarla un nuovo formato. –
Questa è un'ottima risposta! Grazie Rob e Peter. Posso vivere senza che l'output sia rtf o rtfd. Immagino che non stavo pensando subito dopo aver lavorato al nuovo schema ... Avrei dovuto pensare a NSCoder me stesso. – regulus6633
Questo non funziona in iOS per me. Ottengo un arresto anomalo su un NSCFType encodeWithCoder :, dove l'oggetto tipo è simile a questo: [ (kCGColorSpaceDeviceRGB)] (0.576471 0.576471 0.560784 1) - sarebbe di grande aiuto se sapessi come Posso codificarli? –