From ec9f1d87b29cabd37fc673092267db525d932a7a Mon Sep 17 00:00:00 2001 From: FredKiefer Date: Fri, 16 Jun 2000 17:00:17 +0000 Subject: [PATCH] Extended support for RTF git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@6697 72102866-910b-0410-8b05-ffd578937521 --- Source/Parsers/attributedStringConsumer.m | 458 ++++++++++++++++------ 1 file changed, 329 insertions(+), 129 deletions(-) diff --git a/Source/Parsers/attributedStringConsumer.m b/Source/Parsers/attributedStringConsumer.m index 1dfc4cd38..f4226cc88 100644 --- a/Source/Parsers/attributedStringConsumer.m +++ b/Source/Parsers/attributedStringConsumer.m @@ -4,6 +4,8 @@ Author: Stefan Bðhringer (stefan.boehringer@uni-bochum.de) Date: Dec 1999 + Author: Fred Kiefer + Date: June 2000 This file is part of the GNUstep GUI Library. @@ -23,9 +25,10 @@ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#import -#import -#import "Parsers/rtfConsumer.h" +#include +#include +#include "Parsers/rtfConsumer.h" +#include "Parsers/rtfConsumerFunctions.h" /* we have to satisfy the scanner with a stream reading function */ typedef struct { @@ -53,54 +56,77 @@ readNSString(StringContext *ctxt) we must implement from the rtfConsumerSkeleton.h file (Supporting files) this includes the yacc error handling and output */ -#define GSfontDictName @"fonts" -#define GScurrentTextPosition @"textPosition" -#define GSresultName @"result" -#define GSchanged @"changed" -#define GSparagraph @"paragraph" -#define GSfontName @"fontName" -#define GSfontSize @"fontSize" -#define GSbold @"bold" -#define GSitalic @"italic" -#define GSunderline @"underline" -#define GSscript @"script" - -#define GSdocumentAttributes @"documentAttributes" - -#define CTXT ((NSMutableDictionary *)ctxt) -#define FONTS [CTXT objectForKey: GSfontDictName] -#define RESULT [CTXT objectForKey: GSresultName] -#define CHANGED [[CTXT objectForKey: GSchanged] boolValue] -#define SETCHANGED(flag) [CTXT setObject: [NSNumber numberWithBool: flag] forKey: GSchanged] -#define PARAGRAPH [CTXT objectForKey: GSparagraph] - -#define halfpoints2points(a) ((a)/2.0) -// FIXME: How to convert twips to points??? -#define twips2points(a) ((a)/500.0) - - -static int textPosition(void *ctxt) +// Hold the attributs of the current run +@interface RTFAttribute: NSObject { - return [[CTXT objectForKey: GScurrentTextPosition] intValue]; +@public + BOOL changed; + NSParagraphStyle *paragraph; + NSColor *fgColour; + NSColor *bgColour; + NSString *fontName; + float fontSize; + BOOL bold; + BOOL italic; + BOOL underline; + int script; } -static NSFont *currentFont(void *ctxt) +- (NSFont*) currentFont; + +@end + +@implementation RTFAttribute + +- (id) init +{ + NSFont *font = [NSFont userFontOfSize:12]; + + ASSIGN(fontName, [font familyName]); + fontSize = 12.0; + italic = NO; + bold = NO; + underline = NO; + script = 0; + paragraph = [NSMutableParagraphStyle defaultParagraphStyle]; + changed = YES; + + return self; +} + +- (void) dealloc +{ + RELEASE(paragraph); + RELEASE(fontName); + RELEASE(fgColour); + RELEASE(bgColour); + [super dealloc]; +} + +- (id) copyWithZone: (NSZone*)zone +{ + RTFAttribute *new = [isa allocWithZone: zone]; + + new->paragraph = [paragraph copyWithZone: zone]; + new->fontName = [fontName copyWithZone: zone]; + new->fontSize = fontSize; + new->italic = italic; + new->bold = bold; + new->underline = underline; + new->script = script; + new->changed = NO; + + return new; +} + +- (NSFont*) currentFont { NSFont *font; - BOOL boldOn; - BOOL italicOn; - NSString *name; - float size; NSFontTraitMask traits = 0; int weight; - name = [CTXT objectForKey: GSfontName]; - size = [[CTXT objectForKey: GSfontSize] floatValue]; - boldOn = [[CTXT objectForKey: GSbold] boolValue]; - italicOn = [[CTXT objectForKey: GSitalic] boolValue]; - - if (boldOn) + if (bold) { weight = 9; traits |= NSBoldFontMask; @@ -111,7 +137,7 @@ static NSFont *currentFont(void *ctxt) traits |= NSUnboldFontMask; } - if (italicOn) + if (italic) { traits |= NSItalicFontMask; } @@ -120,12 +146,147 @@ static NSFont *currentFont(void *ctxt) traits |= NSUnitalicFontMask; } - font = [[NSFontManager sharedFontManager] fontWithFamily: name + font = [[NSFontManager sharedFontManager] fontWithFamily: fontName traits: traits weight: weight - size: size]; + size: fontSize]; return font; } + +@end + +@interface RTFConsumer: NSObject +{ +@public + NSMutableDictionary *documentAttributes; + NSMutableDictionary *fonts; + NSMutableArray *colours; + NSMutableArray *attrs; + NSMutableAttributedString *result; + int textPosition; + int ignore; +} + +- (NSDictionary*) documentAttributes; +- (RTFAttribute*) attr; +- (void) push; +- (void) pop; +- (NSAttributedString*) result; + +@end + +@implementation RTFConsumer + +- (id) init +{ + RTFAttribute *attr = [RTFAttribute new]; + + textPosition = 0; + ignore = 0; + result = [[NSMutableAttributedString alloc] init]; + ASSIGN(documentAttributes, [NSMutableDictionary dictionary]); + ASSIGN(fonts, [NSMutableDictionary dictionary]); + ASSIGN(attrs, [NSMutableArray array]); + ASSIGN(colours, [NSMutableArray array]); + [attrs addObject: attr]; + + return self; +} + +- (void)dealloc +{ + RELEASE(fonts); + RELEASE(attrs); + RELEASE(colours); + RELEASE(result); + RELEASE(documentAttributes); + [super dealloc]; +} + +- (NSDictionary*) documentAttributes +{ + RETAIN(documentAttributes); + return AUTORELEASE(documentAttributes); +} + +- (RTFAttribute*) attr +{ + return [attrs lastObject]; +} + +- (void) push +{ + [attrs addObject: [[attrs lastObject] copy]]; +} + +- (void) pop +{ + [attrs removeLastObject]; +} + +- (NSAttributedString*) result +{ + RETAIN(result); + return AUTORELEASE(result); +} + +- (NSAttributedString*) parseRTF: (NSData *)rtfData + documentAttributes: (NSDictionary **)dict +{ + CREATE_AUTORELEASE_POOL(pool); + RTFscannerCtxt scanner; + StringContext stringCtxt; + NSString *rtfString = [[NSString alloc] + initWithData: rtfData + encoding: NSASCIIStringEncoding]; + + // Has this RFTConsumer allready been used? Is so, reset! + if (textPosition) + [self init]; + + initStringContext(&stringCtxt, rtfString); + lexInitContext(&scanner, &stringCtxt, (int (*)(void*))readNSString); + NS_DURING + GSRTFparse((void *)self, &scanner); + NS_HANDLER + NSLog(@"Problem during RTF Parsing: %@", + [localException reason]); + //[localException raise]; + NS_ENDHANDLER + + RELEASE(pool); + // document attributes + if (dict) + *dict = [self documentAttributes]; + + return [self result]; +} + +@end + +#define FONTS ((RTFConsumer *)ctxt)->fonts +#define COLOURS ((RTFConsumer *)ctxt)->colours +#define RESULT ((RTFConsumer *)ctxt)->result +#define IGNORE ((RTFConsumer *)ctxt)->ignore +#define TEXTPOSITION ((RTFConsumer *)ctxt)->textPosition +#define DOCUMENTATTRIBUTES ((RTFConsumer*)ctxt)->documentAttributes + +#define CTXT [((RTFConsumer *)ctxt) attr] +#define CHANGED CTXT->changed +#define PARAGRAPH CTXT->paragraph +#define FONTNAME CTXT->fontName +#define SCRIPT CTXT->script +#define ITALIC CTXT->italic +#define BOLD CTXT->bold +#define UNDERLINE CTXT->underline +#define FGCOLOUR CTXT->fgColour +#define BGCOLOUR CTXT->bgColour + +#define PAPERSIZE @"PaperSize" +#define LEFTMARGIN @"LeftMargin" +#define RIGHTMARGIN @"RightMargin" +#define TOPMARGIN @"TopMargin" +#define BUTTOMMARGIN @"ButtomMargin" /* handle errors (this is the yacc error mech) */ void GSRTFerror(const char *msg) @@ -146,22 +307,6 @@ void GSRTFgenericRTFcommand(void *ctxt, RTFcmd cmd) //Start: we're doing some initialization void GSRTFstart(void *ctxt) { - NSFont *font = [NSFont userFontOfSize:12]; - - [CTXT setObject: [NSNumber numberWithInt:0] forKey: GScurrentTextPosition]; - [CTXT setObject: [NSMutableDictionary dictionary] forKey: GSfontDictName]; - SETCHANGED(YES); - - [CTXT setObject: [NSMutableParagraphStyle defaultParagraphStyle] - forKey: GSparagraph]; - - [CTXT setObject: [font familyName] forKey: GSfontName]; - [CTXT setObject: [NSNumber numberWithFloat: 12.0] forKey: GSfontSize]; - [CTXT setObject: [NSNumber numberWithBool: NO] forKey: GSbold]; - [CTXT setObject: [NSNumber numberWithBool: NO] forKey: GSitalic]; - [CTXT setObject: [NSNumber numberWithBool: NO] forKey: GSunderline]; - [CTXT setObject: [NSNumber numberWithInt: 0] forKey: GSscript]; - [RESULT beginEditing]; } @@ -172,44 +317,50 @@ void GSRTFstop(void *ctxt) [RESULT endEditing]; } -void GSRTFopenBlock(void *ctxt) +void GSRTFopenBlock(void *ctxt, BOOL ignore) { - // FIXME: Should push the current state on a stack + if (!IGNORE) + [(RTFConsumer *)ctxt push]; + // Switch off any output for ignored block statements + if (ignore) + IGNORE++; } -void GSRTFcloseBlock(void *ctxt) +void GSRTFcloseBlock(void *ctxt, BOOL ignore) { - // FIXME: Should pop the current state from a stack + if (ignore) + IGNORE--; + if (!IGNORE) + [(RTFConsumer *)ctxt pop]; } void GSRTFmangleText(void *ctxt, const char *text) { - int oldPosition = textPosition(ctxt); + int oldPosition = TEXTPOSITION; int textlen = strlen(text); int newPosition = oldPosition + textlen; NSRange insertionRange = NSMakeRange(oldPosition,0); NSDictionary *attributes; NSFont *font; - if (textlen) + if (!IGNORE && textlen) { - [CTXT setObject:[NSNumber numberWithInt: newPosition] - forKey: GScurrentTextPosition]; + TEXTPOSITION = newPosition; [RESULT replaceCharactersInRange: insertionRange withString: [NSString stringWithCString:text]]; if (CHANGED) { - font = currentFont(ctxt); + font = [CTXT currentFont]; attributes = [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName, - [CTXT objectForKey: GSscript], NSSuperscriptAttributeName, + SCRIPT, NSSuperscriptAttributeName, PARAGRAPH, NSParagraphStyleAttributeName, nil]; [RESULT setAttributes: attributes range: NSMakeRange(oldPosition, textlen)]; - SETCHANGED(NO); + CHANGED = NO; } } } @@ -224,7 +375,7 @@ void GSRTFregisterFont(void *ctxt, const char *fontName, { [NSException raise:NSInvalidArgumentException format:@"Error in RTF (font omitted?), position:%d", - textPosition(ctxt)]; + TEXTPOSITION]; } // exclude trailing ';' from fontName fontNameString = [NSString stringWithCString: fontName @@ -243,14 +394,14 @@ void GSRTFfontNumber(void *ctxt, int fontNumber) [NSException raise: NSInvalidArgumentException format: @"Error in RTF (referring to undefined font \\f%d), position:%d", fontNumber, - textPosition(ctxt)]; + TEXTPOSITION]; } else { - if (![fontName isEqual: [CTXT objectForKey: GSfontName]]) + if (![fontName isEqual: FONTNAME]) { - [CTXT setObject: fontName forKey: GSfontName]; - SETCHANGED(YES); + ASSIGN(FONTNAME, fontName); + CHANGED = YES; } } } @@ -260,28 +411,77 @@ void GSRTFfontSize(void *ctxt, int fontSize) { float size = halfpoints2points(fontSize); - if (size != [[CTXT objectForKey: GSfontSize] floatValue]) + if (size != CTXT->fontSize) { - [CTXT setObject: [NSNumber numberWithFloat: size] - forKey: GSfontSize]; - SETCHANGED(YES); + CTXT->fontSize = size; + CHANGED = YES; } } void GSRTFpaperWidth(void *ctxt, int width) { + float fwidth = twips2points(width); + NSMutableDictionary *dict = DOCUMENTATTRIBUTES; + NSValue *val = [dict objectForKey: PAPERSIZE]; + NSSize size; + + if (val == nil) + size = NSMakeSize(fwidth, 792); + else + { + size = [val sizeValue]; + size.width = fwidth; + } + [dict setObject: [NSValue valueWithSize: size] forKey: PAPERSIZE]; } void GSRTFpaperHeight(void *ctxt, int height) { + float fheight = twips2points(height); + NSMutableDictionary *dict = DOCUMENTATTRIBUTES; + NSValue *val = [dict objectForKey: PAPERSIZE]; + NSSize size; + + if (val == nil) + size = NSMakeSize(612, fheight); + else + { + size = [val sizeValue]; + size.height = fheight; + } + [dict setObject: [NSValue valueWithSize: size] forKey: PAPERSIZE]; } void GSRTFmarginLeft(void *ctxt, int margin) { + float fmargin = twips2points(margin); + NSMutableDictionary *dict = DOCUMENTATTRIBUTES; + + [dict setObject: [NSNumber numberWithFloat: fmargin] forKey: LEFTMARGIN]; } void GSRTFmarginRight(void *ctxt, int margin) { + float fmargin = twips2points(margin); + NSMutableDictionary *dict = DOCUMENTATTRIBUTES; + + [dict setObject: [NSNumber numberWithFloat: fmargin] forKey: RIGHTMARGIN]; +} + +void GSRTFmarginTop(void *ctxt, int margin) +{ + float fmargin = twips2points(margin); + NSMutableDictionary *dict = DOCUMENTATTRIBUTES; + + [dict setObject: [NSNumber numberWithFloat: fmargin] forKey: TOPMARGIN]; +} + +void GSRTFmarginButtom(void *ctxt, int margin) +{ + float fmargin = twips2points(margin); + NSMutableDictionary *dict = DOCUMENTATTRIBUTES; + + [dict setObject: [NSNumber numberWithFloat: fmargin] forKey: BUTTOMMARGIN]; } void GSRTFfirstLineIndent(void *ctxt, int indent) @@ -289,10 +489,11 @@ void GSRTFfirstLineIndent(void *ctxt, int indent) NSMutableParagraphStyle *para = PARAGRAPH; float findent = twips2points(indent); - if ([para firstLineHeadIndent] != findent) + // for attributed strings only positiv indent is allowed + if ((findent >= 0.0) && ([para firstLineHeadIndent] != findent)) { [para setFirstLineHeadIndent: findent]; - SETCHANGED(YES); + CHANGED = YES; } } @@ -301,10 +502,11 @@ void GSRTFleftIndent(void *ctxt, int indent) NSMutableParagraphStyle *para = PARAGRAPH; float findent = twips2points(indent); - if ([para headIndent] != findent) + // for attributed strings only positiv indent is allowed + if ((findent >= 0.0) && ([para headIndent] != findent)) { [para setHeadIndent: findent]; - SETCHANGED(YES); + CHANGED = YES; } } @@ -315,7 +517,7 @@ void GSRTFalignCenter(void *ctxt) if ([para alignment] != NSCenterTextAlignment) { [para setAlignment: NSCenterTextAlignment]; - SETCHANGED(YES); + CHANGED = YES; } } @@ -326,7 +528,7 @@ void GSRTFalignLeft(void *ctxt) if ([para alignment] != NSLeftTextAlignment) { [para setAlignment: NSLeftTextAlignment]; - SETCHANGED(YES); + CHANGED = YES; } } @@ -337,7 +539,7 @@ void GSRTFalignRight(void *ctxt) if ([para alignment] != NSRightTextAlignment) { [para setAlignment: NSRightTextAlignment]; - SETCHANGED(YES); + CHANGED = YES; } } @@ -345,23 +547,39 @@ void GSRTFstyle(void *ctxt, int style) { } +void GSRTFaddColor(void *ctxt, int red, int green, int blue) +{ + NSColor *colour = [NSColor colorWithCalibratedRed: red/255.0 + green: green/255.0 + blue: blue/255.0 + alpha: 1.0]; + + [COLOURS addObject: colour]; +} + +void GSRTFaddDefaultColor(void *ctxt) +{ + [COLOURS addObject: [NSColor textColor]]; +} + void GSRTFcolorbg(void *ctxt, int color) { + ASSIGN(BGCOLOUR, [COLOURS objectAtIndex: color]); } void GSRTFcolorfg(void *ctxt, int color) { + ASSIGN(FGCOLOUR, [COLOURS objectAtIndex: color]); } void GSRTFsubscript(void *ctxt, int script) { script = (int) (-halfpoints2points(script) / 3.0); - if (script != [[CTXT objectForKey: GSscript] intValue]) + if (script != SCRIPT) { - [CTXT setObject: [NSNumber numberWithInt: script] - forKey: GSscript]; - SETCHANGED(YES); + SCRIPT = script; + CHANGED = YES; } } @@ -369,68 +587,50 @@ void GSRTFsuperscript(void *ctxt, int script) { script = (int) (halfpoints2points(script) / 3.0); - if (script != [[CTXT objectForKey: GSscript] intValue]) + if (script != SCRIPT) { - [CTXT setObject: [NSNumber numberWithInt: script] - forKey: GSscript]; - SETCHANGED(YES); + SCRIPT = script; + CHANGED = YES; } } void GSRTFitalic(void *ctxt, BOOL state) { - if (state != [[CTXT objectForKey: GSitalic] boolValue]) + if (state != ITALIC) { - [CTXT setObject: [NSNumber numberWithBool: state] forKey: GSitalic]; - SETCHANGED(YES); + ITALIC = state; + CHANGED = YES; } } void GSRTFbold(void *ctxt, BOOL state) { - if (state != [[CTXT objectForKey: GSbold] boolValue]) + if (state != BOLD) { - [CTXT setObject: [NSNumber numberWithBool: state] forKey: GSbold]; - SETCHANGED(YES); + BOLD = state; + CHANGED = YES; } } void GSRTFunderline(void *ctxt, BOOL state) { - if (state != [[CTXT objectForKey: GSunderline] boolValue]) + if (state != UNDERLINE) { - [CTXT setObject: [NSNumber numberWithBool: state] forKey: GSunderline]; - SETCHANGED(YES); + UNDERLINE = state; + CHANGED = YES; } } -BOOL parseRTFintoAttributedString(NSString *rtfString, - NSMutableAttributedString *result, - NSDictionary **dict) +NSAttributedString *parseRTFintoAttributedString(NSData *rtfData, + NSDictionary **dict) { - RTFscannerCtxt scanner; - StringContext stringCtxt; - NSMutableDictionary *myDict = [NSMutableDictionary dictionary]; - - [myDict setObject: result forKey: GSresultName]; - initStringContext(&stringCtxt, rtfString); - lexInitContext(&scanner, &stringCtxt, (int (*)(void*))readNSString); - GSRTFparse(myDict, &scanner); + RTFConsumer *consumer = [RTFConsumer new]; + NSAttributedString *result; - // document attributes - if (dict) - (*dict)=[myDict objectForKey: GSdocumentAttributes]; + result = [consumer parseRTF: rtfData documentAttributes: dict]; + RELEASE(consumer); - return YES; -} - -NSMutableAttributedString *attributedStringFromRTF(NSString *rtfString) -{ - NSMutableAttributedString *result = [[NSMutableAttributedString alloc] init]; - - parseRTFintoAttributedString(rtfString, result, NULL); - - return AUTORELEASE(result); + return result; }