Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>There is a way to save custom attributes to RTF using Cocoa. It relies on the fact that RTF is a text format, and so can be manipulated as a string even if you don't know all the rules of RTF and don't have a custom RTF reader/writer. The procedure I outline below post-processes the RTF both when writing and reading, and I have used this technique personally. One thing to be very careful of is that the text you insert into the RTF uses only 7-bit ASCII and no unescaped control characters, which include "\ { }".</p> <p>Here's how you would encode your data:</p> <pre><code>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:&amp;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); } </code></pre> <p>This technique works because all compliant RTF readers will ignore "starred groups" whose keyword they don't recognize. Therefore you want to be sure your control word will not be recognized by any other reader, so use something likely to be unique, such as a prefix with your company or product name. If your data is complex, or binary, or may contain illegal RTF characters that you don't want to escape, encode it in base64. Be sure you put a space after your keyword.</p> <p>Similarly, when reading the RTF, you search for your control word, extract the data, and restore the attribute. This routine takes as arguments the attributed string and the RTF it was created from.</p> <pre><code>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)]; } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload