mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-26 22:30:56 +00:00
Moved RTF output code to Source/Parser/RTFProducer.m
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@6788 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
b29f76be2b
commit
15fb40d163
1 changed files with 45 additions and 616 deletions
|
@ -37,7 +37,9 @@
|
||||||
#include <AppKit/NSGraphics.h>
|
#include <AppKit/NSGraphics.h>
|
||||||
#include <Foundation/NSString.h>
|
#include <Foundation/NSString.h>
|
||||||
#include <Foundation/NSRange.h>
|
#include <Foundation/NSRange.h>
|
||||||
|
|
||||||
#include "Parsers/rtfConsumer.h"
|
#include "Parsers/rtfConsumer.h"
|
||||||
|
#include "Parsers/RTFProducer.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* function to return a character set containing characters that
|
* function to return a character set containing characters that
|
||||||
|
@ -82,32 +84,6 @@ wordCSet()
|
||||||
return cset;
|
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)
|
@interface NSAttributedString(AttributedStringRTFDAdditions)
|
||||||
|
|
||||||
- (NSString*) RTFHeaderStringWithContext: (NSMutableDictionary*) contextDict;
|
- (NSString*) RTFHeaderStringWithContext: (NSMutableDictionary*) contextDict;
|
||||||
|
@ -146,7 +122,7 @@ paraBreakCSet()
|
||||||
id keys[8];
|
id keys[8];
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
if (range.location < 0 || NSMaxRange(range) > [self length])
|
if (NSMaxRange(range) > [self length])
|
||||||
{
|
{
|
||||||
[NSException raise: NSRangeException
|
[NSException raise: NSRangeException
|
||||||
format: @"RangeError in method -fontAttributesInRange:"];
|
format: @"RangeError in method -fontAttributesInRange:"];
|
||||||
|
@ -205,7 +181,7 @@ paraBreakCSet()
|
||||||
{
|
{
|
||||||
id style;
|
id style;
|
||||||
|
|
||||||
if (range.location < 0 || NSMaxRange(range) > [self length])
|
if (NSMaxRange(range) > [self length])
|
||||||
{
|
{
|
||||||
[NSException raise: NSRangeException
|
[NSException raise: NSRangeException
|
||||||
format: @"RangeError in method -rulerAttributesInRange:"];
|
format: @"RangeError in method -rulerAttributesInRange:"];
|
||||||
|
@ -231,8 +207,7 @@ paraBreakCSet()
|
||||||
NSRange scanRange;
|
NSRange scanRange;
|
||||||
NSRange startRange;
|
NSRange startRange;
|
||||||
|
|
||||||
if (aRange.location < 0 || NSMaxRange(aRange) > length ||
|
if (NSMaxRange(aRange) > length || location > length)
|
||||||
location < 0 || location > length)
|
|
||||||
{
|
{
|
||||||
[NSException raise: NSRangeException
|
[NSException raise: NSRangeException
|
||||||
format: @"RangeError in method -lineBreakBeforeIndex:withinRange:"];
|
format: @"RangeError in method -lineBreakBeforeIndex:withinRange:"];
|
||||||
|
@ -303,7 +278,7 @@ paraBreakCSet()
|
||||||
unsigned length = [str length];
|
unsigned length = [str length];
|
||||||
NSRange range;
|
NSRange range;
|
||||||
|
|
||||||
if (location < 0 || location > length)
|
if (location > length)
|
||||||
{
|
{
|
||||||
[NSException raise: NSRangeException
|
[NSException raise: NSRangeException
|
||||||
format: @"RangeError in method -nextWordFromIndex:forward:"];
|
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
|
- (id) initWithPath: (NSString*)path
|
||||||
documentAttributes: (NSDictionary**)dict
|
documentAttributes: (NSDictionary**)dict
|
||||||
{
|
{
|
||||||
|
@ -446,8 +414,9 @@ documentAttributes: (NSDictionary**)dict
|
||||||
- (NSData*) RTFDFromRange: (NSRange)range
|
- (NSData*) RTFDFromRange: (NSRange)range
|
||||||
documentAttributes: (NSDictionary*)dict
|
documentAttributes: (NSDictionary*)dict
|
||||||
{
|
{
|
||||||
return [[self RTFDStringFromRange: range documentAttributes: dict]
|
return [RTFProducer RTFDFromAttributedString:
|
||||||
dataUsingEncoding: NSASCIIStringEncoding];
|
[self attributedSubstringFromRange: range]
|
||||||
|
documentAttributes: dict];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSFileWrapper*) RTFDFileWrapperFromRange: (NSRange)range
|
- (NSFileWrapper*) RTFDFileWrapperFromRange: (NSRange)range
|
||||||
|
@ -479,7 +448,7 @@ documentAttributes: (NSDictionary**)dict
|
||||||
int sValue;
|
int sValue;
|
||||||
NSRange effRange;
|
NSRange effRange;
|
||||||
|
|
||||||
if (range.location < 0 || NSMaxRange(range) > [self length])
|
if (NSMaxRange(range) > [self length])
|
||||||
{
|
{
|
||||||
[NSException raise: NSRangeException
|
[NSException raise: NSRangeException
|
||||||
format: @"RangeError in method -superscriptRange:"];
|
format: @"RangeError in method -superscriptRange:"];
|
||||||
|
@ -506,7 +475,7 @@ documentAttributes: (NSDictionary**)dict
|
||||||
int sValue;
|
int sValue;
|
||||||
NSRange effRange;
|
NSRange effRange;
|
||||||
|
|
||||||
if (range.location < 0 || NSMaxRange(range) > [self length])
|
if (NSMaxRange(range) > [self length])
|
||||||
{
|
{
|
||||||
[NSException raise: NSRangeException
|
[NSException raise: NSRangeException
|
||||||
format: @"RangeError in method -subscriptRange:"];
|
format: @"RangeError in method -subscriptRange:"];
|
||||||
|
@ -529,7 +498,7 @@ documentAttributes: (NSDictionary**)dict
|
||||||
|
|
||||||
- (void) unscriptRange: (NSRange)range
|
- (void) unscriptRange: (NSRange)range
|
||||||
{
|
{
|
||||||
if (range.location < 0 || NSMaxRange(range) > [self length])
|
if (NSMaxRange(range) > [self length])
|
||||||
{
|
{
|
||||||
[NSException raise: NSRangeException
|
[NSException raise: NSRangeException
|
||||||
format: @"RangeError in method -unscriptRange:"];
|
format: @"RangeError in method -unscriptRange:"];
|
||||||
|
@ -548,7 +517,7 @@ documentAttributes: (NSDictionary**)dict
|
||||||
NSRange effRange;
|
NSRange effRange;
|
||||||
NSFontManager *fm = [NSFontManager sharedFontManager];
|
NSFontManager *fm = [NSFontManager sharedFontManager];
|
||||||
|
|
||||||
if (range.location < 0 || NSMaxRange(range) > [self length])
|
if (NSMaxRange(range) > [self length])
|
||||||
{
|
{
|
||||||
[NSException raise: NSRangeException
|
[NSException raise: NSRangeException
|
||||||
format: @"RangeError in method -applyFontTraits:range:"];
|
format: @"RangeError in method -applyFontTraits:range:"];
|
||||||
|
@ -582,7 +551,7 @@ documentAttributes: (NSDictionary**)dict
|
||||||
id value;
|
id value;
|
||||||
unsigned loc = range.location;
|
unsigned loc = range.location;
|
||||||
|
|
||||||
if (range.location < 0 || NSMaxRange(range) > [self length])
|
if (NSMaxRange(range) > [self length])
|
||||||
{
|
{
|
||||||
[NSException raise: NSRangeException
|
[NSException raise: NSRangeException
|
||||||
format: @"RangeError in method -setAlignment:range:"];
|
format: @"RangeError in method -setAlignment:range:"];
|
||||||
|
@ -625,131 +594,67 @@ documentAttributes: (NSDictionary**)dict
|
||||||
- (void) fixAttributesInRange: (NSRange)range
|
- (void) fixAttributesInRange: (NSRange)range
|
||||||
{
|
{
|
||||||
[self fixFontAttributeInRange: range];
|
[self fixFontAttributeInRange: range];
|
||||||
// [self fixParagraphStyleAttributeInRange: range];
|
[self fixParagraphStyleAttributeInRange: range];
|
||||||
// [self fixAttachmentAttributeInRange: range];
|
// [self fixAttachmentAttributeInRange: range];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) fixFontAttributeInRange: (NSRange)range
|
- (void) fixFontAttributeInRange: (NSRange)range
|
||||||
{
|
{
|
||||||
if (range.location < 0 || NSMaxRange(range) > [self length])
|
if (NSMaxRange(range) > [self length])
|
||||||
{
|
{
|
||||||
[NSException raise: NSRangeException
|
[NSException raise: NSRangeException
|
||||||
format: @"RangeError in method -fixFontAttributeInRange:"];
|
format: @"RangeError in method -fixFontAttributeInRange:"];
|
||||||
}
|
}
|
||||||
|
// FIXME: Should check for each character if it is supported by the
|
||||||
|
// assigned font
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) fixParagraphStyleAttributeInRange: (NSRange)range
|
- (void) fixParagraphStyleAttributeInRange: (NSRange)range
|
||||||
{
|
{
|
||||||
NSString *str = [self string];
|
NSString *str = [self string];
|
||||||
unsigned length = [str length];
|
unsigned loc = range.location;
|
||||||
unsigned location;
|
NSRange r;
|
||||||
NSRange r;
|
|
||||||
|
|
||||||
if (range.location < 0 || NSMaxRange(range) > [self length])
|
if (NSMaxRange(range) > [self length])
|
||||||
{
|
{
|
||||||
[NSException raise: NSRangeException
|
[NSException raise: NSRangeException
|
||||||
format: @"RangeError in method -fixParagraphStyleAttributeInRange:"];
|
format: @"RangeError in method -fixParagraphStyleAttributeInRange:"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (range.location > 0)
|
while (loc < NSMaxRange(range))
|
||||||
{
|
|
||||||
/*
|
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
NSParagraphStyle *style;
|
NSParagraphStyle *style;
|
||||||
NSRange found;
|
NSRange found;
|
||||||
unsigned end;
|
unsigned end;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine position of next paragraph end.
|
* Extend loc to take in entire paragraph if necessary.
|
||||||
*/
|
*/
|
||||||
r = [str rangeOfCharacterFromSet: paraBreakCSet()
|
r = [str lineRangeForRange: NSMakeRange(loc, 1)];
|
||||||
options: NSLiteralSearch
|
end = NSMaxRange(r);
|
||||||
range: range];
|
|
||||||
if (r.length == 0)
|
|
||||||
end = NSMaxRange(range);
|
|
||||||
else
|
|
||||||
end = r.location + 1;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get the style in effect at the paragraph start.
|
* get the style in effect at the paragraph start.
|
||||||
*/
|
*/
|
||||||
style = [self attribute: NSParagraphStyleAttributeName
|
style = [self attribute: NSParagraphStyleAttributeName
|
||||||
atIndex: location
|
atIndex: r.location
|
||||||
effectiveRange: &found];
|
effectiveRange: &found];
|
||||||
/*
|
|
||||||
* Fix up this paragraph to have the starting style.
|
|
||||||
*/
|
|
||||||
while (NSMaxRange(found) < end)
|
|
||||||
{
|
|
||||||
NSParagraphStyle *nextStyle;
|
|
||||||
NSRange nextFound;
|
|
||||||
|
|
||||||
nextStyle = [self attribute: NSParagraphStyleAttributeName
|
if (NSMaxRange(found) < end)
|
||||||
atIndex: NSMaxRange(found)
|
{
|
||||||
effectiveRange: &nextFound];
|
/*
|
||||||
if (nextStyle == style || [nextStyle isEqual: style] == YES)
|
* Styles differ - add the old style to the remainder of the
|
||||||
{
|
* range.
|
||||||
found = nextFound;
|
*/
|
||||||
}
|
found.location = NSMaxRange(found);
|
||||||
else
|
found.length = end - found.location;
|
||||||
{
|
[self addAttribute: NSParagraphStyleAttributeName
|
||||||
/*
|
value: style
|
||||||
* Styles differ - add the old style to the remainder of the
|
range: found];
|
||||||
* range.
|
loc = end;
|
||||||
*/
|
|
||||||
found.location = NSMaxRange(found);
|
|
||||||
found.length = end - found.location;
|
|
||||||
[self addAttribute: NSParagraphStyleAttributeName
|
|
||||||
value: style
|
|
||||||
range: found];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/*
|
else
|
||||||
* Adjust the range to start at the beginning of the next paragraph.
|
loc = NSMaxRange(found);
|
||||||
*/
|
|
||||||
range.length = NSMaxRange(range) - end;
|
|
||||||
range.location = end;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,7 +663,7 @@ documentAttributes: (NSDictionary**)dict
|
||||||
unsigned location = range.location;
|
unsigned location = range.location;
|
||||||
unsigned end = NSMaxRange(range);
|
unsigned end = NSMaxRange(range);
|
||||||
|
|
||||||
if (range.location < 0 || NSMaxRange(range) > [self length])
|
if (NSMaxRange(range) > [self length])
|
||||||
{
|
{
|
||||||
[NSException raise: NSRangeException
|
[NSException raise: NSRangeException
|
||||||
format: @"RangeError in method -fixAttachmentAttributeInRange:"];
|
format: @"RangeError in method -fixAttachmentAttributeInRange:"];
|
||||||
|
@ -797,483 +702,7 @@ documentAttributes: (NSDictionary**)dict
|
||||||
|
|
||||||
- (void)updateAttachmentsFromPath:(NSString *)path
|
- (void)updateAttachmentsFromPath:(NSString *)path
|
||||||
{
|
{
|
||||||
// FIXME: Still missing
|
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
Loading…
Reference in a new issue