libs-gui/TextConverters/RTF/RTFConsumer.m
FredKiefer 149b77b8aa Added method [RTFConsumer appendString:] with code coming from
GSRTFmangleText(). Use this in GSRTFmangleText() and new function
GSRTFunicode(). Corrected GSRTFmangleText() to convert the input
from NSISOLatin1StringEncoding, as this is what our current RTF
exporter produces.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@17224 72102866-910b-0410-8b05-ffd578937521
2003-07-15 22:18:01 +00:00

1031 lines
22 KiB
Objective-C

/* attributedStringConsumer.m
Copyright (C) 1999 Free Software Foundation, Inc.
Author: Stefan Bðhringer (stefan.boehringer@uni-bochum.de)
Date: Dec 1999
Author: Fred Kiefer <FredKiefer@gmx.de>
Date: June 2000
This file is part of the GNUstep GUI Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Foundation/Foundation.h>
#include <AppKit/AppKit.h>
#include "RTFConsumer.h"
#include "RTFConsumerFunctions.h"
#include "RTFProducer.h"
/* we have to satisfy the scanner with a stream reading function */
typedef struct {
NSString *string;
int position;
int length;
} StringContext;
static void
initStringContext (StringContext *ctxt, NSString *string)
{
ctxt->string = string;
ctxt->position = 0;
ctxt->length = [string length];
}
static int
readNSString (StringContext *ctxt)
{
return (ctxt->position < ctxt->length )
? [ctxt->string characterAtIndex:ctxt->position++]: EOF;
}
// Hold the attributs of the current run
@interface RTFAttribute: NSObject <NSCopying>
{
@public
BOOL changed;
BOOL tabChanged;
NSMutableParagraphStyle *paragraph;
NSColor *fgColour;
NSColor *bgColour;
NSString *fontName;
float fontSize;
BOOL bold;
BOOL italic;
BOOL underline;
int script;
float real_fi, real_li;
}
- (NSFont*) currentFont;
- (NSNumber*) script;
- (NSNumber*) underline;
- (void) resetParagraphStyle;
- (void) resetFont;
- (void) addTab: (float)location type: (NSTextTabType)type;
@end
@implementation RTFAttribute
- (id) init
{
[self resetFont];
[self resetParagraphStyle];
return self;
}
- (void) dealloc
{
RELEASE(paragraph);
RELEASE(fontName);
RELEASE(fgColour);
RELEASE(bgColour);
[super dealloc];
}
- (id) copyWithZone: (NSZone*)zone
{
RTFAttribute *new = (RTFAttribute *)NSCopyObject (self, 0, zone);
new->paragraph = [paragraph mutableCopyWithZone: zone];
RETAIN(new->fontName);
RETAIN(new->fgColour);
RETAIN(new->bgColour);
return new;
}
- (NSFont*) currentFont
{
NSFont *font;
NSFontTraitMask traits = 0;
int weight;
if (bold)
{
weight = 9;
traits |= NSBoldFontMask;
}
else
{
weight = 5;
traits |= NSUnboldFontMask;
}
if (italic)
{
traits |= NSItalicFontMask;
}
else
{
traits |= NSUnitalicFontMask;
}
font = [[NSFontManager sharedFontManager] fontWithFamily: fontName
traits: traits
weight: weight
size: fontSize];
if (font == nil)
{
/* Before giving up and using a default font, we try if this is
* not the case of a font with a composite name, such as
* 'Helvetica-Light'. In that case, even if we don't have
* exactly an 'Helvetica-Light' font family, we might have an
* 'Helvetica' one. */
NSRange range = [fontName rangeOfString:@"-"];
if (range.location != NSNotFound)
{
NSString *fontFamily = [fontName substringToIndex: range.location];
font = [[NSFontManager sharedFontManager] fontWithFamily: fontFamily
traits: traits
weight: weight
size: fontSize];
}
if (font == nil)
{
NSDebugMLLog(@"RTFParser",
@"Could not find font %@ size %f traits %d weight %d",
fontName, fontSize, traits, weight);
/* Last resort, default font. :-( */
font = [NSFont userFontOfSize: fontSize];
}
}
return font;
}
- (NSNumber*) script
{
return [NSNumber numberWithInt: script];
}
- (NSNumber*) underline
{
if (underline)
return [NSNumber numberWithInt: NSSingleUnderlineStyle];
else
return nil;
}
- (void) resetParagraphStyle
{
DESTROY(paragraph);
paragraph = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
real_fi = real_li = 0.0;
tabChanged = NO;
changed = YES;
}
- (void) resetFont
{
NSFont *font = [NSFont userFontOfSize:12];
ASSIGN(fontName, [font familyName]);
fontSize = 12.0;
italic = NO;
bold = NO;
underline = NO;
script = 0;
DESTROY(fgColour);
DESTROY(bgColour);
changed = YES;
}
- (void) addTab: (float) location type: (NSTextTabType) type
{
NSTextTab *tab = [[NSTextTab alloc] initWithType: NSLeftTabStopType
location: location];
if (!tabChanged)
{
NSArray *a;
a = [[NSArray alloc] initWithObjects: tab, nil];
// remove all tab stops
[paragraph setTabStops: a];
DESTROY(a);
tabChanged = YES;
}
else
{
[paragraph addTabStop: tab];
}
changed = YES;
RELEASE(tab);
}
@end
static BOOL classInheritsFromNSMutableAttributedString (Class c)
{
Class mutable = [NSMutableAttributedString class];
while (c != Nil)
{
if (c == mutable)
{
return YES;
}
c = [c superclass];
}
return NO;
}
@interface RTFConsumer (Private)
- (NSAttributedString*) parseRTF: (NSData *)rtfData
documentAttributes: (NSDictionary **)dict
class: (Class)class;
- (NSDictionary*) documentAttributes;
- (RTFAttribute*) attr;
- (void) push;
- (void) pop;
- (void) appendString: (NSString*)string;
@end
@implementation RTFConsumer
/* RTFConsumer is the principal class and thus implements this */
+ (Class) classForFormat: (NSString *)format producer: (BOOL)flag
{
Class cClass = Nil;
if (flag)
{
if ([format isEqual: @"RTFD"])
{
cClass = [RTFDProducer class];
}
else if ([format isEqual: @"RTF"])
{
cClass = [RTFProducer class];
}
}
else
{
if ([format isEqual: @"RTFD"])
{
cClass = [RTFDConsumer class];
}
else if ([format isEqual: @"RTF"])
{
cClass = [RTFConsumer class];
}
}
return cClass;
}
+ (NSAttributedString*) parseFile: (NSFileWrapper *)wrapper
documentAttributes: (NSDictionary **)dict
class: (Class)class
{
RTFConsumer *consumer = [RTFConsumer new];
NSAttributedString *text = nil;
if ([wrapper isRegularFile])
{
text = [consumer parseRTF: [wrapper regularFileContents]
documentAttributes: dict
class: class];
}
else if ([wrapper isDirectory])
{
NSDictionary *files = [wrapper fileWrappers];
NSFileWrapper *contents;
//FIXME: We should store the files in the consumer
// We try to read the main file in the directory
if ((contents = [files objectForKey: @"TXT.rtf"]) != nil)
{
text = [consumer parseRTF: [contents regularFileContents]
documentAttributes: dict
class: class];
}
}
RELEASE(consumer);
return text;
}
+ (NSAttributedString*) parseData: (NSData *)rtfData
documentAttributes: (NSDictionary **)dict
class: (Class)class
{
RTFConsumer *consumer = [RTFConsumer new];
NSAttributedString *text;
text = [consumer parseRTF: rtfData
documentAttributes: dict
class: class];
RELEASE(consumer);
return text;
}
- (id) init
{
ignore = 0;
result = nil;
documentAttributes = nil;
fonts = nil;
attrs = nil;
colours = nil;
_class = Nil;
return self;
}
- (void) dealloc
{
RELEASE(fonts);
RELEASE(attrs);
RELEASE(colours);
RELEASE(result);
RELEASE(documentAttributes);
[super dealloc];
}
@end
@implementation RTFDConsumer
+ (NSAttributedString*) parseData: (NSData *)rtfData
documentAttributes: (NSDictionary **)dict
class: (Class)class
{
NSAttributedString *str;
NSFileWrapper *wrapper = [[NSFileWrapper alloc]
initWithSerializedRepresentation: rtfData];
str = [self parseFile: wrapper documentAttributes: dict class: class];
RELEASE (wrapper);
return str;
}
@end
@implementation RTFConsumer (Private)
- (NSDictionary*) documentAttributes
{
RETAIN(documentAttributes);
return AUTORELEASE(documentAttributes);
}
- (void) reset
{
RTFAttribute *attr = [RTFAttribute new];
ignore = 0;
DESTROY(result);
if (classInheritsFromNSMutableAttributedString (_class))
{
result = [[_class alloc] init];
}
else
{
result = [[NSMutableAttributedString alloc] init];
}
ASSIGN(documentAttributes, [NSMutableDictionary dictionary]);
ASSIGN(fonts, [NSMutableDictionary dictionary]);
ASSIGN(attrs, [NSMutableArray array]);
ASSIGN(colours, [NSMutableArray array]);
[attrs addObject: attr];
RELEASE(attr);
}
- (RTFAttribute*) attr
{
return [attrs lastObject];
}
- (void) push
{
RTFAttribute *attr = [[attrs lastObject] copy];
[attrs addObject: attr];
RELEASE(attr);
}
- (void) pop
{
[attrs removeLastObject];
((RTFAttribute*)[attrs lastObject])->changed = YES;
}
- (NSAttributedString*) parseRTF: (NSData *)rtfData
documentAttributes: (NSDictionary **)dict
class: (Class)class
{
CREATE_AUTORELEASE_POOL(pool);
RTFscannerCtxt scanner;
StringContext stringCtxt;
// We should read in the first few characters to find out which
// encoding we have
NSString *rtfString = [[NSString alloc]
initWithData: rtfData
encoding: NSASCIIStringEncoding];
// Reset this RFTConsumer, as it might already have been used!
_class = class;
[self reset];
initStringContext(&stringCtxt, rtfString);
lexInitContext(&scanner, &stringCtxt, (int (*)(void*))readNSString);
[result beginEditing];
NS_DURING
GSRTFparse((void *)self, &scanner);
NS_HANDLER
NSLog(@"Problem during RTF Parsing: %@",
[localException reason]);
//[localException raise];
NS_ENDHANDLER
[result endEditing];
RELEASE(rtfString);
RELEASE(pool);
// document attributes
if (dict)
{
*dict = [self documentAttributes];
}
if (classInheritsFromNSMutableAttributedString (_class))
{
RETAIN (result);
AUTORELEASE (result);
return result;
}
else
{
return AUTORELEASE ([[_class alloc] initWithAttributedString: result]);
}
}
- (void) appendString: (NSString*)string
{
int oldPosition = [result length];
int textlen = [string length];
NSRange insertionRange = NSMakeRange(oldPosition,0);
NSMutableDictionary *attributes;
if (!ignore && textlen)
{
RTFAttribute* attr = [self attr];
[result replaceCharactersInRange: insertionRange
withString: string];
if (attr->changed)
{
NSParagraphStyle *ps = [attr->paragraph copy];
attributes = [[NSMutableDictionary alloc]
initWithObjectsAndKeys:
[attr currentFont], NSFontAttributeName,
ps, NSParagraphStyleAttributeName,
nil];
DESTROY(ps);
if (attr->underline)
{
[attributes setObject: [attr underline]
forKey: NSUnderlineStyleAttributeName];
}
if (attr->script)
{
[attributes setObject: [attr script]
forKey: NSSuperscriptAttributeName];
}
if (attr->fgColour != nil)
{
[attributes setObject: attr->fgColour
forKey: NSForegroundColorAttributeName];
}
if (attr->bgColour != nil)
{
[attributes setObject: attr->bgColour
forKey: NSBackgroundColorAttributeName];
}
[result setAttributes: attributes
range: NSMakeRange(oldPosition, textlen)];
DESTROY(attributes);
attr->changed = NO;
}
}
}
@end
#undef IGNORE
#define FONTS ((RTFConsumer *)ctxt)->fonts
#define COLOURS ((RTFConsumer *)ctxt)->colours
#define RESULT ((RTFConsumer *)ctxt)->result
#define IGNORE ((RTFConsumer *)ctxt)->ignore
#define TEXTPOSITION [RESULT length]
#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"
/*
we must implement from the rtfConsumerFunctions.h file (Supporting files)
this includes the yacc error handling and output
*/
/* handle errors (this is the yacc error mech) */
void GSRTFerror (const char *msg)
{
/* [NSException raise:NSInvalidArgumentException
format:@"Syntax error in RTF: %s", msg];*/
NSDebugLLog(@"RTFParser",@"Syntax error in RTF: %s", msg);
}
void GSRTFgenericRTFcommand (void *ctxt, RTFcmd cmd)
{
NSDebugLLog(@"RTFParser", @"encountered rtf cmd:%s", cmd.name);
if (!cmd.isEmpty)
NSDebugLLog(@"RTFParser", @" argument is %d\n", cmd.parameter);
}
//Start: we're doing some initialization
void GSRTFstart (void *ctxt)
{
NSDebugLLog(@"RTFParser", @"Start RTF parsing");
}
// Finished to parse one piece of RTF.
void GSRTFstop (void *ctxt)
{
//<!> close all open bolds et al.
NSDebugLLog(@"RTFParser", @"End RTF parsing");
}
void GSRTFopenBlock (void *ctxt, BOOL ignore)
{
if (!IGNORE)
{
[(RTFConsumer *)ctxt push];
}
// Switch off any output for ignored block statements
if (ignore)
{
IGNORE++;
}
}
void GSRTFcloseBlock (void *ctxt, BOOL ignore)
{
if (ignore)
{
IGNORE--;
}
if (!IGNORE)
{
[(RTFConsumer *)ctxt pop];
}
}
void GSRTFmangleText (void *ctxt, const char *text)
{
NSData *data = [[NSData alloc] initWithBytes: (void*)text
length: strlen(text)];
NSString *str = [[NSString alloc] initWithData: data
encoding: NSISOLatin1StringEncoding];
[(RTFConsumer *)ctxt appendString: str];
DESTROY(str);
DESTROY(data);
}
void GSRTFunicode (void *ctxt, int uchar)
{
unichar chars = uchar;
NSString *str = [[NSString alloc] initWithCharacters: &chars
length: 1];
[(RTFConsumer *)ctxt appendString: str];
DESTROY(str);
}
void GSRTFregisterFont (void *ctxt, const char *fontName,
RTFfontFamily family, int fontNumber)
{
NSString *fontNameString;
NSNumber *fontId = [NSNumber numberWithInt: fontNumber];
if (!fontName || !*fontName)
{
[NSException raise: NSInvalidArgumentException
format: @"Error in RTF (font omitted?), position:%d",
TEXTPOSITION];
}
// exclude trailing ';' from fontName
if (';' == fontName[strlen(fontName)-1])
{
fontNameString = [NSString stringWithCString: fontName
length: strlen(fontName)-1];
}
else
{
fontNameString = [NSString stringWithCString: fontName
length: strlen(fontName)];
}
[FONTS setObject: fontNameString forKey: fontId];
}
void GSRTFfontNumber (void *ctxt, int fontNumber)
{
NSNumber *fontId = [NSNumber numberWithInt: fontNumber];
NSString *fontName = [FONTS objectForKey: fontId];
if (fontName == nil)
{
/* we're about to set an unknown font */
[NSException raise: NSInvalidArgumentException
format: @"Error in RTF (referring to undefined font \\f%d), position:%d",
fontNumber,
TEXTPOSITION];
}
else
{
if (![fontName isEqual: FONTNAME])
{
ASSIGN(FONTNAME, fontName);
CHANGED = YES;
}
}
}
// <N> fontSize is in halfpoints according to spec
void GSRTFfontSize (void *ctxt, int fontSize)
{
float size = halfpoints2points(fontSize);
if (size != CTXT->fontSize)
{
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)
{
NSMutableParagraphStyle *para = PARAGRAPH;
float findent = twips2points(indent);
CTXT->real_fi = findent;
findent = CTXT->real_li + CTXT->real_fi;
// for attributed strings only positiv indent is allowed
if ((findent >= 0.0) && ([para firstLineHeadIndent] != findent))
{
[para setFirstLineHeadIndent: findent];
CHANGED = YES;
}
}
void GSRTFleftIndent (void *ctxt, int indent)
{
NSMutableParagraphStyle *para = PARAGRAPH;
float findent = twips2points(indent);
CTXT->real_li = findent;
// for attributed strings only positiv indent is allowed
if ((findent >= 0.0) && ([para headIndent] != findent))
{
[para setHeadIndent: findent];
CHANGED = YES;
}
findent = CTXT->real_li + CTXT->real_fi;
if ((findent >= 0.0) && ([para firstLineHeadIndent] != findent))
{
[para setFirstLineHeadIndent: findent];
CHANGED = YES;
}
}
void GSRTFrightIndent (void *ctxt, int indent)
{
NSMutableParagraphStyle *para = PARAGRAPH;
float findent = twips2points(indent);
// for attributed strings only positiv indent is allowed
if ((findent >= 0.0) && ([para tailIndent] != findent))
{
[para setTailIndent: -findent];
CHANGED = YES;
}
}
void GSRTFtabstop (void *ctxt, int location)
{
float flocation = twips2points(location);
if (flocation >= 0.0)
{
[CTXT addTab: flocation type: NSLeftTabStopType];
}
}
void GSRTFalignCenter (void *ctxt)
{
NSMutableParagraphStyle *para = PARAGRAPH;
if ([para alignment] != NSCenterTextAlignment)
{
[para setAlignment: NSCenterTextAlignment];
CHANGED = YES;
}
}
void GSRTFalignJustified (void *ctxt)
{
NSMutableParagraphStyle *para = PARAGRAPH;
if ([para alignment] != NSJustifiedTextAlignment)
{
[para setAlignment: NSJustifiedTextAlignment];
CHANGED = YES;
}
}
void GSRTFalignLeft (void *ctxt)
{
NSMutableParagraphStyle *para = PARAGRAPH;
if ([para alignment] != NSLeftTextAlignment)
{
[para setAlignment: NSLeftTextAlignment];
CHANGED = YES;
}
}
void GSRTFalignRight (void *ctxt)
{
NSMutableParagraphStyle *para = PARAGRAPH;
if ([para alignment] != NSRightTextAlignment)
{
[para setAlignment: NSRightTextAlignment];
CHANGED = YES;
}
}
void GSRTFspaceAbove (void *ctxt, int space)
{
NSMutableParagraphStyle *para = PARAGRAPH;
float fspace = twips2points(space);
if (fspace >= 0.0)
{
[para setParagraphSpacing: fspace];
CHANGED = YES;
}
}
void GSRTFlineSpace (void *ctxt, int space)
{
NSMutableParagraphStyle *para = PARAGRAPH;
float fspace = twips2points(space);
if (space == 1000)
{
[para setMinimumLineHeight: 0.0];
[para setMaximumLineHeight: 0.0];
CHANGED = YES;
}
else if (fspace < 0.0)
{
[para setMaximumLineHeight: -fspace];
CHANGED = YES;
}
else
{
[para setMinimumLineHeight: fspace];
CHANGED = YES;
}
}
void GSRTFdefaultParagraph (void *ctxt)
{
[CTXT resetParagraphStyle];
}
void GSRTFstyle (void *ctxt, int style)
{
}
void GSRTFdefaultCharacterStyle (void *ctxt)
{
[CTXT resetFont];
}
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)
{
if ([COLOURS count] <= (unsigned int)color)
{
ASSIGN (BGCOLOUR, [NSColor whiteColor]);
}
else
{
ASSIGN (BGCOLOUR, [COLOURS objectAtIndex: color]);
}
CHANGED = YES;
}
void GSRTFcolorfg (void *ctxt, int color)
{
if ([COLOURS count] <= (unsigned int)color)
{
ASSIGN (FGCOLOUR, [NSColor blackColor]);
}
else
{
ASSIGN (FGCOLOUR, [COLOURS objectAtIndex: color]);
}
CHANGED = YES;
}
void GSRTFsubscript (void *ctxt, int script)
{
script = (int) (-halfpoints2points(script) / 3.0);
if (script != SCRIPT)
{
SCRIPT = script;
CHANGED = YES;
}
}
void GSRTFsuperscript (void *ctxt, int script)
{
script = (int) (halfpoints2points(script) / 3.0);
if (script != SCRIPT)
{
SCRIPT = script;
CHANGED = YES;
}
}
void GSRTFitalic (void *ctxt, BOOL state)
{
if (state != ITALIC)
{
ITALIC = state;
CHANGED = YES;
}
}
void GSRTFbold (void *ctxt, BOOL state)
{
if (state != BOLD)
{
BOLD = state;
CHANGED = YES;
}
}
void GSRTFunderline (void *ctxt, BOOL state)
{
if (state != UNDERLINE)
{
UNDERLINE = state;
CHANGED = YES;
}
}
void GSRTFparagraph (void *ctxt)
{
GSRTFmangleText(ctxt, "\n");
CTXT->tabChanged = NO;
}