diff --git a/Source/NSAttributedString.m b/Source/NSAttributedString.m index fd39ac4f3..0869771da 100644 --- a/Source/NSAttributedString.m +++ b/Source/NSAttributedString.m @@ -37,7 +37,9 @@ #include #include #include + #include "Parsers/rtfConsumer.h" +#include "Parsers/RTFProducer.h" /* * function to return a character set containing characters that @@ -82,32 +84,6 @@ wordCSet() return cset; } -/* - * function to return a character set containing paragraph separators - */ -static NSCharacterSet* -paraBreakCSet() -{ - static NSCharacterSet *cset = nil; - - if (cset == nil) - { - NSMutableCharacterSet *m = [NSMutableCharacterSet new]; - - /* - * Build set with characters specified in MacOS-X documentation for - * [NSAttributedString -fixParagraphStyleAttributeInRange:] - */ - [m addCharactersInRange: NSMakeRange(0x000A, 1)]; /* CR */ - [m addCharactersInRange: NSMakeRange(0x000D, 1)]; /* LF */ - [m addCharactersInRange: NSMakeRange(0x2028, 1)]; /* line sep */ - [m addCharactersInRange: NSMakeRange(0x2029, 1)]; /* para sep */ - cset = [m copy]; - RELEASE(m); - } - return cset; -} - @interface NSAttributedString(AttributedStringRTFDAdditions) - (NSString*) RTFHeaderStringWithContext: (NSMutableDictionary*) contextDict; @@ -146,7 +122,7 @@ paraBreakCSet() id keys[8]; int count = 0; - if (range.location < 0 || NSMaxRange(range) > [self length]) + if (NSMaxRange(range) > [self length]) { [NSException raise: NSRangeException format: @"RangeError in method -fontAttributesInRange:"]; @@ -205,7 +181,7 @@ paraBreakCSet() { id style; - if (range.location < 0 || NSMaxRange(range) > [self length]) + if (NSMaxRange(range) > [self length]) { [NSException raise: NSRangeException format: @"RangeError in method -rulerAttributesInRange:"]; @@ -231,8 +207,7 @@ paraBreakCSet() NSRange scanRange; NSRange startRange; - if (aRange.location < 0 || NSMaxRange(aRange) > length || - location < 0 || location > length) + if (NSMaxRange(aRange) > length || location > length) { [NSException raise: NSRangeException format: @"RangeError in method -lineBreakBeforeIndex:withinRange:"]; @@ -303,7 +278,7 @@ paraBreakCSet() unsigned length = [str length]; NSRange range; - if (location < 0 || location > length) + if (location > length) { [NSException raise: NSRangeException format: @"RangeError in method -nextWordFromIndex:forward:"]; @@ -354,13 +329,6 @@ paraBreakCSet() } } -/* - * This is where the fun begins with RTF/RTFD/HTML - * This is currently ALL FIXME. :-) - * With love from Michael, hehe. - */ - - - (id) initWithPath: (NSString*)path documentAttributes: (NSDictionary**)dict { @@ -446,8 +414,9 @@ documentAttributes: (NSDictionary**)dict - (NSData*) RTFDFromRange: (NSRange)range documentAttributes: (NSDictionary*)dict { - return [[self RTFDStringFromRange: range documentAttributes: dict] - dataUsingEncoding: NSASCIIStringEncoding]; + return [RTFProducer RTFDFromAttributedString: + [self attributedSubstringFromRange: range] + documentAttributes: dict]; } - (NSFileWrapper*) RTFDFileWrapperFromRange: (NSRange)range @@ -479,7 +448,7 @@ documentAttributes: (NSDictionary**)dict int sValue; NSRange effRange; - if (range.location < 0 || NSMaxRange(range) > [self length]) + if (NSMaxRange(range) > [self length]) { [NSException raise: NSRangeException format: @"RangeError in method -superscriptRange:"]; @@ -506,7 +475,7 @@ documentAttributes: (NSDictionary**)dict int sValue; NSRange effRange; - if (range.location < 0 || NSMaxRange(range) > [self length]) + if (NSMaxRange(range) > [self length]) { [NSException raise: NSRangeException format: @"RangeError in method -subscriptRange:"]; @@ -529,7 +498,7 @@ documentAttributes: (NSDictionary**)dict - (void) unscriptRange: (NSRange)range { - if (range.location < 0 || NSMaxRange(range) > [self length]) + if (NSMaxRange(range) > [self length]) { [NSException raise: NSRangeException format: @"RangeError in method -unscriptRange:"]; @@ -548,7 +517,7 @@ documentAttributes: (NSDictionary**)dict NSRange effRange; NSFontManager *fm = [NSFontManager sharedFontManager]; - if (range.location < 0 || NSMaxRange(range) > [self length]) + if (NSMaxRange(range) > [self length]) { [NSException raise: NSRangeException format: @"RangeError in method -applyFontTraits:range:"]; @@ -582,7 +551,7 @@ documentAttributes: (NSDictionary**)dict id value; unsigned loc = range.location; - if (range.location < 0 || NSMaxRange(range) > [self length]) + if (NSMaxRange(range) > [self length]) { [NSException raise: NSRangeException format: @"RangeError in method -setAlignment:range:"]; @@ -625,131 +594,67 @@ documentAttributes: (NSDictionary**)dict - (void) fixAttributesInRange: (NSRange)range { [self fixFontAttributeInRange: range]; -// [self fixParagraphStyleAttributeInRange: range]; + [self fixParagraphStyleAttributeInRange: range]; // [self fixAttachmentAttributeInRange: range]; } - (void) fixFontAttributeInRange: (NSRange)range { - if (range.location < 0 || NSMaxRange(range) > [self length]) + if (NSMaxRange(range) > [self length]) { [NSException raise: NSRangeException format: @"RangeError in method -fixFontAttributeInRange:"]; } + // FIXME: Should check for each character if it is supported by the + // assigned font } - (void) fixParagraphStyleAttributeInRange: (NSRange)range { - NSString *str = [self string]; - unsigned length = [str length]; - unsigned location; - NSRange r; + NSString *str = [self string]; + unsigned loc = range.location; + NSRange r; - if (range.location < 0 || NSMaxRange(range) > [self length]) + if (NSMaxRange(range) > [self length]) { [NSException raise: NSRangeException format: @"RangeError in method -fixParagraphStyleAttributeInRange:"]; } - if (range.location > 0) - { - /* - * Extend range backward to take in entire paragraph if necessary. - */ - r = NSMakeRange(0, range.location); - r = [str rangeOfCharacterFromSet: paraBreakCSet() - options: NSBackwardsSearch|NSLiteralSearch - range: r]; - if (r.length == 0) - { - /* - * No paragraph before this in range - so extend range right - * back to the start of the string. - */ - range.length += range.location; - range.location = 0; - } - else if (r.location + 1 < range.location) - { - range.length += (range.location - r.location - 1); - range.location = r.location + 1; - } - } - - /* - * Extend range forwards to take in entire paragraph if necessary. - */ - location = r.location + r.length; - if (location > 0) - location--; - r = NSMakeRange(location, length - location); - r = [str rangeOfCharacterFromSet: paraBreakCSet() - options: NSLiteralSearch - range: r]; - if (r.length > 0 && r.location > location) - range.length += (r.location - location); - - /* - * Now try to step through the range fixing up the paragraph styles in - * each paragraph so the entire paragraph has the same style as the - * first character in the paragraph. - */ - while (range.length > 0) + while (loc < NSMaxRange(range)) { NSParagraphStyle *style; NSRange found; unsigned end; /* - * Determine position of next paragraph end. + * Extend loc to take in entire paragraph if necessary. */ - r = [str rangeOfCharacterFromSet: paraBreakCSet() - options: NSLiteralSearch - range: range]; - if (r.length == 0) - end = NSMaxRange(range); - else - end = r.location + 1; + r = [str lineRangeForRange: NSMakeRange(loc, 1)]; + end = NSMaxRange(r); /* * get the style in effect at the paragraph start. */ style = [self attribute: NSParagraphStyleAttributeName - atIndex: location + atIndex: r.location effectiveRange: &found]; - /* - * Fix up this paragraph to have the starting style. - */ - while (NSMaxRange(found) < end) - { - NSParagraphStyle *nextStyle; - NSRange nextFound; - - nextStyle = [self attribute: NSParagraphStyleAttributeName - atIndex: NSMaxRange(found) - effectiveRange: &nextFound]; - if (nextStyle == style || [nextStyle isEqual: style] == YES) - { - found = nextFound; - } - else - { - /* - * Styles differ - add the old style to the remainder of the - * range. - */ - found.location = NSMaxRange(found); - found.length = end - found.location; - [self addAttribute: NSParagraphStyleAttributeName - value: style - range: found]; - } + + if (NSMaxRange(found) < end) + { + /* + * Styles differ - add the old style to the remainder of the + * range. + */ + found.location = NSMaxRange(found); + found.length = end - found.location; + [self addAttribute: NSParagraphStyleAttributeName + value: style + range: found]; + loc = end; } - /* - * Adjust the range to start at the beginning of the next paragraph. - */ - range.length = NSMaxRange(range) - end; - range.location = end; + else + loc = NSMaxRange(found); } } @@ -758,7 +663,7 @@ documentAttributes: (NSDictionary**)dict unsigned location = range.location; unsigned end = NSMaxRange(range); - if (range.location < 0 || NSMaxRange(range) > [self length]) + if (NSMaxRange(range) > [self length]) { [NSException raise: NSRangeException format: @"RangeError in method -fixAttachmentAttributeInRange:"]; @@ -797,483 +702,7 @@ documentAttributes: (NSDictionary**)dict - (void)updateAttachmentsFromPath:(NSString *)path { - // FIXME: Still missing -} -@end - - - - -/* AttributedStringRTFDAdditions.m created by daniel on Wed 24-Nov-1999 */ - -@interface NSString (Replacing) -- (NSString*) stringByReplacingEveryOccurrenceOfString: (NSString*)aString - withString: (NSString*)other; -@end - -@implementation NSString (Replacing) - -- (NSString*) stringByReplacingEveryOccurrenceOfString: (NSString*)aString - withString: (NSString*)other -{ - unsigned len = [self length]; - NSMutableString *erg = [NSMutableString string]; - NSRange currRange = [self rangeOfString: aString]; - unsigned prevLocation = 0; - - while (currRange.length > 0) - { - if (currRange.location > 0) - { - NSRange r; - - r = NSMakeRange(prevLocation, currRange.location - prevLocation); - [erg appendString: [self substringWithRange: r]]; - } - [erg appendString: other]; - currRange.location += currRange.length; - currRange.length = len - currRange.location; - prevLocation = currRange.location; - currRange = [self rangeOfString: aString - options: NSLiteralSearch - range: currRange]; - } - if (prevLocation < len) - { - NSRange r; - - r = NSMakeRange(prevLocation, len - prevLocation); - [erg appendString: [self substringWithRange: r]]; - } - - return erg; -} -@end - -@implementation NSAttributedString(AttributedStringRTFDAdditions) - -- (NSString*) RTFHeaderStringWithContext: (NSMutableDictionary*) contextDict -{ - NSMutableString *result; - NSDictionary *fontDict; - NSDictionary *colorDict; - NSDictionary *docDict; - - result = (NSMutableString*)[NSMutableString stringWithString: @"{\\rtf0\\ansi"]; - fontDict = [contextDict objectForKey: @"Fonts"]; - - // write Font Table - if (fontDict != nil) - { - NSMutableString *fontlistString = [NSMutableString string]; - NSString *table; - NSEnumerator *fontEnum; - id currFont; - NSArray *keyArray; - - keyArray = [fontDict allKeys]; - keyArray = [keyArray sortedArrayUsingSelector: @selector(compare:)]; - - fontEnum = [keyArray objectEnumerator]; - while ((currFont = [fontEnum nextObject]) != nil) - { - NSString *fontFamily; - NSString *detail; - - if ([currFont isEqualToString: @"Symbol"]) - fontFamily = @"tech"; - else if ([currFont isEqualToString: @"Helvetica"]) - fontFamily = @"swiss"; - else if ([currFont isEqualToString: @"Courier"]) - fontFamily = @"modern"; - else - fontFamily = @"nil"; - - detail = [NSString stringWithFormat: @"%@\\f%@ %@;", - [fontDict objectForKey: currFont], fontFamily, currFont]; - [fontlistString appendString: detail]; - } - table = [NSString stringWithFormat: @"{\\fonttbl%@}\n", fontlistString]; - [result appendString: table]; - } - - // write Colour table - colorDict = [contextDict objectForKey: @"Colors"]; - if (colorDict != nil) - { - unsigned int count = [colorDict count]; - NSMutableArray *list = [NSMutableArray arrayWithCapacity: count]; - NSEnumerator *keyEnum = [colorDict keyEnumerator]; - id next; - int i; - - while ((next = [keyEnum nextObject]) != nil) - { - NSNumber *cn = [colorDict objectForKey: next]; - [list insertObject: next atIndex: [cn intValue]-1]; - } - - [result appendString: @"{\\colortbl;"]; - for (i = 0; i < count; i++) - { - NSColor *color = [[list objectAtIndex: i] - colorUsingColorSpaceName: NSCalibratedRGBColorSpace]; - [result appendString: [NSString stringWithFormat: - @"\\red%d\\green%d\\blue%d;", - (int)([color redComponent]*255), - (int)([color greenComponent]*255), - (int)([color blueComponent]*255)]]; - } - - [result appendString: @"}\n"]; - } - // We should output the parameters for the document - docDict = [contextDict objectForKey: @"DocumentAttributes"]; - - return result; -} - -- (NSString*) RTFTrailerStringWithContext: (NSMutableDictionary*) contextDict -{ - return @"}"; -} - -- (NSString*) RTFBodyStringWithContext: (NSMutableDictionary*) contextDict -{ - NSRange completeRange; - NSRange currRange; - NSString *string = [self string]; - NSMutableString *result = [NSMutableString string]; - NSFont *currentFont = nil; - NSColor *fgColor = [NSColor textColor]; - NSColor *bgColor = [NSColor textBackgroundColor]; - - completeRange = NSRangeFromString([contextDict objectForKey: @"Range"]); - currRange = NSMakeRange(completeRange.location, 0); - - while (NSMaxRange(currRange) < NSMaxRange(completeRange)) // save all "runs" - { - NSDictionary *attributes; - NSString *substring; - BOOL useBraces = NO; - NSMutableString *headerString; - NSMutableString *trailerString; - NSEnumerator *attribEnum; - id currAttrib; - - attributes = [self attributesAtIndex: NSMaxRange(currRange) - longestEffectiveRange: &currRange - inRange: completeRange]; - substring = [string substringWithRange: currRange]; - headerString = (id)[NSMutableString string]; - trailerString = (id)[NSMutableString string]; - - /* - * analyze attributes of current run - */ - attribEnum = [attributes keyEnumerator]; - while ((currAttrib = [attribEnum nextObject]) != nil) - { - /* - * handle fonts - */ - if ([currAttrib isEqualToString: NSFontAttributeName]) - { - NSFont *font; - NSMutableDictionary *peekDict; - NSString *fontToken; - NSString *fontName; - NSFontTraitMask traits; - - font = [attributes objectForKey: NSFontAttributeName]; - peekDict = [contextDict objectForKey: @"Fonts"]; - fontName = [font familyName]; - traits = [[NSFontManager sharedFontManager] traitsOfFont: font]; - - /* - * maintain a dictionary for the used fonts - * (for rtf-header generation) - */ - if (peekDict == nil) - { - peekDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: - @"\\f0", fontName, nil]; - [contextDict setObject: peekDict forKey: @"Fonts"]; - } - else - { - if ([peekDict objectForKey: fontName] == nil) - { - unsigned count = [peekDict count]; - NSString *fCount; - - fCount = [NSString stringWithFormat: @"\\f%d", count]; - [peekDict setObject: fCount forKey: fontName]; - } - } - fontToken = [peekDict objectForKey: fontName]; - /* - * font name - */ - if (![fontName isEqualToString: [currentFont familyName]] - || currentFont == nil) - { - [headerString appendString: fontToken]; - } - /* - * font size - */ - if ([font pointSize] != [currentFont pointSize]) - { - int points = (int)[font pointSize]*2; - NSString *pString; - - pString = [NSString stringWithFormat: @"\\fs%d", points]; - [headerString appendString: pString]; - } - /* - * font attributes - */ - if (traits & NSItalicFontMask) - { - [headerString appendString: @"\\i"]; - [trailerString appendString: @"\\i0"]; - } - if (traits & NSBoldFontMask) - { - [headerString appendString: @"\\b"]; - [trailerString appendString: @"\\b0"]; - } - currentFont = font; - } - else if ([currAttrib isEqualToString: NSParagraphStyleAttributeName]) - { - float firstLineIndent; - float lineIndent; - NSParagraphStyle *paraStyle = [attributes objectForKey: - NSParagraphStyleAttributeName]; - NSTextAlignment alignment = [paraStyle alignment]; - - switch (alignment) - { - case NSRightTextAlignment: - [headerString appendString: @"\\qr"]; - break; - case NSCenterTextAlignment: - [headerString appendString: @"\\qc"]; - break; - case NSLeftTextAlignment: - [headerString appendString: @"\\ql"]; - break; - case NSJustifiedTextAlignment: - [headerString appendString: @"\\qj"]; - break; - default: break; - } - - // write first line indent and left indent - firstLineIndent = [paraStyle firstLineHeadIndent]; - if (firstLineIndent != 0.0) - { - // FIXME: How should the units be converted? - [headerString appendString: [NSString stringWithFormat: - @"\\fi%d", - (int)firstLineIndent]]; - [trailerString appendString: @"\\fi0"]; - } - lineIndent = [paraStyle headIndent]; - if (lineIndent != 0.0) - { - // FIXME: How should the units be converted? - [headerString appendString: [NSString stringWithFormat: - @"\\li%d", - (int)lineIndent]]; - [trailerString appendString: @"\\li0"]; - } - } - else if ([currAttrib isEqualToString: NSForegroundColorAttributeName]) - { - NSColor *color = [attributes objectForKey: NSForegroundColorAttributeName]; - if (![color isEqual: fgColor]) - { - NSMutableDictionary *peekDict; - unsigned int cn; - - peekDict = [contextDict objectForKey: @"Colors"]; - /* - * maintain a dictionary for the used colours - * (for rtf-header generation) - */ - if (peekDict == nil) - { - peekDict = [NSMutableDictionary - dictionaryWithObjectsAndKeys: - [NSNumber numberWithInt: 1], color, nil]; - [contextDict setObject: peekDict forKey: @"Colors"]; - cn = 1; - } - else - { - if ([peekDict objectForKey: color] == nil) - { - cn = [peekDict count] + 1; - - [peekDict setObject: [NSNumber numberWithInt: cn] - forKey: color]; - } - else - cn = [[peekDict objectForKey: color] intValue]; - } - [headerString appendString: [NSString stringWithFormat: - @"\\cf%d", cn]]; - [trailerString appendString: @"\\cf0"]; - } - } - else if ([currAttrib isEqualToString: NSUnderlineStyleAttributeName]) - { - [headerString appendString: @"\\ul"]; - [trailerString appendString: @"\\ulnone"]; - } - else if ([currAttrib isEqualToString: NSSuperscriptAttributeName]) - { - NSNumber *value = [attributes objectForKey: NSSuperscriptAttributeName]; - int svalue = [value intValue] * 6; - - if (svalue > 0) - { - [headerString appendString: [NSString stringWithFormat: - @"\\up%d", svalue]]; - [trailerString appendString: @"\\up0"]; - } - else if (svalue < 0) - { - [headerString appendString: [NSString stringWithFormat: - @"\\dn%d", -svalue]]; - [trailerString appendString: @"\\dn0"]; - } - } - else if ([currAttrib isEqualToString: NSBackgroundColorAttributeName]) - { - NSColor *color = [attributes objectForKey: NSForegroundColorAttributeName]; - if (![color isEqual: bgColor]) - { - NSMutableDictionary *peekDict; - unsigned int cn; - - peekDict = [contextDict objectForKey: @"Colors"]; - /* - * maintain a dictionary for the used colours - * (for rtf-header generation) - */ - if (peekDict == nil) - { - peekDict = [NSMutableDictionary - dictionaryWithObjectsAndKeys: - [NSNumber numberWithInt: 1], color, nil]; - [contextDict setObject: peekDict forKey: @"Colors"]; - cn = 1; - } - else - { - if ([peekDict objectForKey: color] == nil) - { - cn = [peekDict count] + 1; - - [peekDict setObject: [NSNumber numberWithInt: cn] - forKey: color]; - } - else - cn = [[peekDict objectForKey: color] intValue]; - } - [headerString appendString: [NSString stringWithFormat: - @"\\cb%d", cn]]; - [trailerString appendString: @"\\cb0"]; - } - } - else if ([currAttrib isEqualToString: NSAttachmentAttributeName]) - { - } - else if ([currAttrib isEqualToString: NSLigatureAttributeName]) - { - } - else if ([currAttrib isEqualToString: NSBaselineOffsetAttributeName]) - { - } - else if ([currAttrib isEqualToString: NSKernAttributeName]) - { - } - } - // write down current run - substring = [substring stringByReplacingString: @"\\" - withString: @"\\\\"]; - substring = [substring stringByReplacingString: @"\n" - withString: @"\\par\n"]; - substring = [substring stringByReplacingString: @"\t" - withString: @"\\tab"]; - substring = [substring stringByReplacingString: @"{" - withString: @"\\{"]; - substring = [substring stringByReplacingString: @"}" - withString: @"\\}"]; - // FIXME: All characters not in the standard encodeing must be - // replaced by \'xx - - if (useBraces) - { - NSString *braces; - - if ([headerString length]) - braces = [NSString stringWithFormat: @"{%@ %@%@}", - headerString, substring, trailerString]; - else - braces = [NSString stringWithFormat: @"{%@%@}", - substring, trailerString]; - - [result appendString: braces]; - } - else - { - NSString *nobraces; - - if ([headerString length]) - nobraces = [NSString stringWithFormat: @"%@ %@", - headerString, substring]; - else - nobraces = substring; - - if ([trailerString length]) - nobraces = [NSString stringWithFormat: @"%@%@ ", - nobraces, trailerString]; - - [result appendString: nobraces]; - } - } - return result; -} - - -- (NSString*) RTFDStringFromRange: (NSRange)range - documentAttributes: (NSDictionary*)dict -{ - NSMutableString *output = [NSMutableString string]; - NSString *headerString; - NSString *trailerString; - NSString *bodyString; - NSMutableDictionary *context = [NSMutableDictionary dictionary]; - - [context setObject: (dict ? dict : [NSMutableDictionary dictionary]) - forKey: @"DocumentAttributes"]; - [context setObject: NSStringFromRange(range) forKey: @"Range"]; - - /* - * do not change order! (esp. body has to be generated first; builds context) - */ - bodyString = [self RTFBodyStringWithContext: context]; - trailerString = [self RTFTrailerStringWithContext: context]; - headerString = [self RTFHeaderStringWithContext: context]; - - [output appendString: headerString]; - [output appendString: bodyString]; - [output appendString: trailerString]; - return (NSString*)output; + // FIXME: Still missing } + @end