1/* attributedStringConsumer.m
2
3   Copyright (C) 1999 Free Software Foundation, Inc.
4
5   Author:  Stefan B�hringer (stefan.boehringer@uni-bochum.de)
6   Date: Dec 1999
7   Author: Fred Kiefer <FredKiefer@gmx.de>
8   Date: June 2000
9
10   This file is part of the GNUstep GUI Library.
11
12   This library is free software; you can redistribute it and/or
13   modify it under the terms of the GNU Lesser General Public
14   License as published by the Free Software Foundation; either
15   version 2 of the License, or (at your option) any later version.
16
17   This library is distributed in the hope that it will be useful,
18   but WITHOUT ANY WARRANTY; without even the implied warranty of
19   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
20   Lesser General Public License for more details.
21
22   You should have received a copy of the GNU Lesser General Public
23   License along with this library; see the file COPYING.LIB.
24   If not, see <http://www.gnu.org/licenses/> or write to the
25   Free Software Foundation, 51 Franklin Street, Fifth Floor,
26   Boston, MA 02110-1301, USA.
27*/
28
29#import <Foundation/Foundation.h>
30#import <AppKit/AppKit.h>
31#import <GNUstepGUI/GSHelpAttachment.h>
32#import "RTFConsumer.h"
33#import "RTFConsumerFunctions.h"
34#import "RTFProducer.h"
35
36/*  we have to satisfy the scanner with a stream reading function */
37typedef struct {
38  const char	*string;
39  int		position;
40  int		length;
41} StringContext;
42
43static void
44initStringContext (StringContext *ctxt, NSData *data)
45{
46  ctxt->string = [data bytes];
47  ctxt->position = 0;
48  ctxt->length = [data length];
49}
50
51static int
52readString (StringContext *ctxt)
53{
54  return ctxt->position < ctxt->length ? ctxt->string[ctxt->position++] : EOF;
55}
56
57// Hold the attributs of the current run
58@interface RTFAttribute: NSObject <NSCopying>
59{
60@public
61  BOOL changed;
62  BOOL tabChanged;
63  NSMutableParagraphStyle *paragraph;
64  NSColor *fgColour;
65  NSColor *bgColour;
66  NSColor *ulColour;
67  NSString *fontName;
68  float fontSize;
69  BOOL bold;
70  BOOL italic;
71  NSInteger underline;
72  NSInteger strikethrough;
73  int script;
74
75  float real_fi, real_li;
76}
77
78- (NSFont*) currentFont;
79- (NSNumber*) script;
80- (NSNumber*) underline;
81- (NSNumber*) strikethrough;
82- (void) resetParagraphStyle;
83- (void) resetFont;
84- (void) addTab: (float)location  type: (NSTextTabType)type;
85
86@end
87
88@implementation RTFAttribute
89
90- (id) init
91{
92  [self resetFont];
93  [self resetParagraphStyle];
94
95  return self;
96}
97
98- (void) dealloc
99{
100  RELEASE(paragraph);
101  RELEASE(fontName);
102  RELEASE(fgColour);
103  RELEASE(bgColour);
104  RELEASE(ulColour);
105  [super dealloc];
106}
107
108- (id) copyWithZone: (NSZone*)zone
109{
110  RTFAttribute *new =  (RTFAttribute *)NSCopyObject (self, 0, zone);
111
112  new->paragraph = [paragraph mutableCopyWithZone: zone];
113  RETAIN(new->fontName);
114  RETAIN(new->fgColour);
115  RETAIN(new->bgColour);
116  RETAIN(new->ulColour);
117
118  return new;
119}
120
121- (NSFont*) currentFont
122{
123  NSFont *font;
124  NSFontTraitMask traits = 0;
125  int weight;
126
127  if (bold)
128    {
129      weight = 9;
130      traits |= NSBoldFontMask;
131    }
132  else
133    {
134      weight = 5;
135      traits |= NSUnboldFontMask;
136    }
137
138  if (italic)
139    {
140      traits |= NSItalicFontMask;
141    }
142  else
143    {
144      traits |= NSUnitalicFontMask;
145    }
146
147  font = [[NSFontManager sharedFontManager] fontWithFamily: fontName
148					    traits: traits
149					    weight: weight
150					    size: fontSize];
151  if (font == nil)
152    {
153      /* Before giving up and using a default font, we try if this is
154       * not the case of a font with a composite name, such as
155       * 'Helvetica-Light'.  In that case, even if we don't have
156       * exactly an 'Helvetica-Light' font family, we might have an
157       * 'Helvetica' one.  */
158      NSRange range = [fontName rangeOfString:@"-"];
159
160      if (range.location != NSNotFound)
161	{
162	  NSString *fontFamily = [fontName substringToIndex: range.location];
163
164	  font = [[NSFontManager sharedFontManager] fontWithFamily: fontFamily
165						    traits: traits
166						    weight: weight
167						    size: fontSize];
168	}
169
170      if (font == nil)
171	{
172	  NSDebugMLLog(@"RTFParser",
173		       @"Could not find font %@ size %f traits %d weight %d",
174		       fontName, fontSize, traits, weight);
175
176	  /* Last resort, default font.  :-(  */
177	  font = [NSFont userFontOfSize: fontSize];
178	}
179    }
180
181  return font;
182}
183
184- (NSNumber*) script
185{
186  return [NSNumber numberWithInt: script];
187}
188
189- (NSNumber*) underline
190{
191  if (underline != NSUnderlineStyleNone)
192    return [NSNumber numberWithInteger: underline];
193  else
194    return nil;
195}
196
197- (NSNumber*) strikethrough
198{
199  if (strikethrough != NSUnderlineStyleNone)
200    return [NSNumber numberWithInteger: strikethrough];
201  else
202    return nil;
203}
204
205- (void) resetParagraphStyle
206{
207  DESTROY(paragraph);
208  paragraph = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
209  real_fi = real_li = 0.0;
210
211  tabChanged = NO;
212  changed = YES;
213}
214
215- (void) resetFont
216{
217  NSFont *font = [NSFont userFontOfSize:12];
218
219  ASSIGN(fontName, [font familyName]);
220  fontSize = 12.0;
221  italic = NO;
222  bold = NO;
223
224  underline = NSUnderlineStyleNone;
225  strikethrough = NSUnderlineStyleNone;
226  script = 0;
227  DESTROY(fgColour);
228  DESTROY(bgColour);
229  DESTROY(ulColour);
230
231  changed = YES;
232}
233
234- (void) addTab: (float) location  type: (NSTextTabType) type
235{
236  NSTextTab *tab = [[NSTextTab alloc] initWithType: NSLeftTabStopType
237				      location: location];
238
239  if (!tabChanged)
240    {
241      NSArray *a;
242      a = [[NSArray alloc] initWithObjects: tab, nil];
243      // remove all tab stops
244      [paragraph setTabStops: a];
245      DESTROY(a);
246      tabChanged = YES;
247    }
248  else
249    {
250      [paragraph addTabStop: tab];
251    }
252
253  changed = YES;
254  RELEASE(tab);
255}
256
257@end
258
259static BOOL classInheritsFromNSMutableAttributedString (Class c)
260{
261  Class mutable = [NSMutableAttributedString class];
262
263  while (c != Nil)
264    {
265      if (c == mutable)
266	{
267	  return YES;
268	}
269      c = [c superclass];
270    }
271  return NO;
272}
273
274@interface RTFConsumer (Private)
275
276- (NSAttributedString*) parseRTF: (NSData *)rtfData
277	      documentAttributes: (NSDictionary **)dict
278			   class: (Class)class;
279- (NSDictionary*) documentAttributes;
280
281- (RTFAttribute*) attr;
282- (void) push;
283- (void) pop;
284- (void) appendString: (NSString*)string;
285- (void) appendHelpLink: (NSString*)fileName marker: (NSString *)markerName;
286- (void) appendHelpMarker: (NSString*)markerName;
287- (void) appendField: (int)start
288         instruction: (NSString*)instruction;
289- (void) appendImage: (NSString*) string;
290- (void) reset;
291- (void) setEncoding: (NSStringEncoding)anEncoding;
292@end
293
294@implementation RTFConsumer
295
296/* RTFConsumer is the principal class and thus implements this */
297+ (Class) classForFormat: (NSString *)format producer: (BOOL)flag
298{
299  Class cClass = Nil;
300
301  if (flag)
302    {
303      if (([format isEqual: NSRTFDTextDocumentType]) ||
304          ([format isEqual: @"com.apple.rtfd"]) ||
305          ([format isEqual: @"rtfd"]))
306	{
307	  cClass = [RTFDProducer class];
308	}
309      else if (([format isEqual: NSRTFTextDocumentType]) ||
310               ([format isEqual: @"public.rtf"]) ||
311               ([format isEqual: @"rtf"]))
312	{
313	  cClass = [RTFProducer class];
314	}
315    }
316  else
317    {
318      if (([format isEqual: NSRTFDTextDocumentType]) ||
319          ([format isEqual: @"com.apple.rtfd"]) ||
320          ([format isEqual: @"rtfd"]))
321	{
322	  cClass = [RTFDConsumer class];
323	}
324      else if (([format isEqual: NSRTFTextDocumentType]) ||
325               ([format isEqual: @"public.rtf"]) ||
326               ([format isEqual: @"rtf"]))
327	{
328	  cClass = [RTFConsumer class];
329	}
330    }
331  return cClass;
332}
333
334+ (NSAttributedString*) parseFile: (NSFileWrapper *)wrapper
335                          options: (NSDictionary *)options
336	       documentAttributes: (NSDictionary **)dict
337                            error: (NSError **)error
338			    class: (Class)class
339{
340  NSAttributedString *text = nil;
341
342  if ([wrapper isRegularFile])
343    {
344      RTFConsumer *consumer = [RTFConsumer new];
345      text = [consumer parseRTF: [wrapper regularFileContents]
346		       documentAttributes: dict
347		       class: class];
348      RELEASE(consumer);
349    }
350  else if ([wrapper isDirectory])
351    {
352      NSDictionary *files = [wrapper fileWrappers];
353      NSFileWrapper *contents;
354      RTFDConsumer* consumer = [RTFDConsumer new];
355
356      if ((contents = [files objectForKey: @"TXT.rtf"]) != nil)
357	{
358	  [consumer setFiles: files];
359	  text = [consumer parseRTF: [contents regularFileContents]
360			   documentAttributes: dict
361			   class: class];
362	}
363      RELEASE(consumer);
364    }
365
366
367  return text;
368}
369
370+ (NSAttributedString*) parseData: (NSData *)rtfData
371                          options: (NSDictionary *)options
372	       documentAttributes: (NSDictionary **)dict
373                            error: (NSError **)error
374			    class: (Class)class
375{
376  RTFConsumer *consumer = [RTFConsumer new];
377  NSAttributedString *text;
378
379  text = [consumer parseRTF: rtfData
380		   documentAttributes: dict
381		   class: class];
382  RELEASE(consumer);
383
384  return text;
385}
386
387- (id) init
388{
389  ignore = 0;
390  result = nil;
391  encoding = NSISOLatin1StringEncoding;
392  documentAttributes = nil;
393  fonts = nil;
394  attrs = nil;
395  colours = nil;
396  _class = Nil;
397
398  return self;
399}
400
401- (void) dealloc
402{
403  RELEASE(fonts);
404  RELEASE(attrs);
405  RELEASE(colours);
406  RELEASE(result);
407  RELEASE(documentAttributes);
408  [super dealloc];
409}
410
411@end
412
413
414@implementation RTFDConsumer
415
416- (id) init
417{
418  self = [super init];
419
420  files = nil;
421
422  return self;
423}
424
425- (void) reset
426{
427  [super reset];
428  [documentAttributes setValue: NSRTFDTextDocumentType
429			forKey: NSDocumentTypeDocumentAttribute];
430}
431
432- (void) dealloc
433{
434  RELEASE(files);
435  [super dealloc];
436}
437
438- (void) setFiles: (NSDictionary*) theFiles
439{
440  ASSIGN (files, theFiles);
441}
442
443+ (NSAttributedString*) parseData: (NSData *)rtfData
444                          options: (NSDictionary *)options
445	       documentAttributes: (NSDictionary **)dict
446                            error: (NSError **)error
447			    class: (Class)class
448{
449  NSAttributedString *str;
450  NSFileWrapper *wrapper = [[NSFileWrapper alloc]
451			     initWithSerializedRepresentation: rtfData];
452
453  str = [self parseFile: wrapper
454              options: options
455              documentAttributes: dict
456              error: error
457              class: class];
458  RELEASE (wrapper);
459
460  return str;
461}
462
463- (void) appendImage: (NSString*)string
464{
465  int  oldPosition = [result length];
466  NSRange insertionRange = NSMakeRange(oldPosition,0);
467
468  if (!ignore)
469    {
470      NSString* fileName = [string stringByTrimmingCharactersInSet:
471                         [NSCharacterSet whitespaceAndNewlineCharacterSet]];
472      NSFileWrapper* wrapper = [files objectForKey: fileName];
473
474      if (wrapper != nil)
475        {
476          NSImage* image = [[NSImage alloc] initWithData: [wrapper regularFileContents]];
477          NSTextAttachment* attachment;
478          RTFAttribute* attr = [self attr];
479          NSMutableDictionary* attributes = nil;
480          NSMutableAttributedString* str = nil;
481
482          if (image != nil)
483            {
484              [wrapper setIcon: image];
485            }
486          attachment = [[NSTextAttachment alloc] initWithFileWrapper: wrapper];
487          if (attachment == nil)
488            {
489              NSLog(@"No attachment at %d", oldPosition);
490              RELEASE(image);
491              return;
492            }
493
494          attributes = [[NSMutableDictionary alloc]
495			 initWithObjectsAndKeys:
496			   [attr currentFont], NSFontAttributeName,
497			   attr->paragraph, NSParagraphStyleAttributeName,
498			   nil];
499
500          str = (NSMutableAttributedString*) [NSMutableAttributedString
501                         attributedStringWithAttachment: attachment];
502
503          [str addAttributes: attributes range: NSMakeRange (0, [str length])];
504
505          [result replaceCharactersInRange: insertionRange withAttributedString: str];
506          attr->changed = YES;
507          RELEASE(attributes);
508          RELEASE(attachment);
509          RELEASE(image);
510        }
511    }
512}
513
514@end
515
516@implementation RTFConsumer (Private)
517
518- (NSDictionary*) documentAttributes
519{
520  RETAIN(documentAttributes);
521  return AUTORELEASE(documentAttributes);
522}
523
524- (void) reset
525{
526  RTFAttribute *attr = [RTFAttribute new];
527
528  ignore = 0;
529  DESTROY(result);
530
531  if (classInheritsFromNSMutableAttributedString (_class))
532    {
533      result = [[_class alloc] init];
534    }
535  else
536    {
537      result = [[NSMutableAttributedString alloc] init];
538    }
539  ASSIGN(documentAttributes, [NSMutableDictionary dictionary]);
540  [documentAttributes setValue: NSRTFTextDocumentType
541			forKey: NSDocumentTypeDocumentAttribute];
542  ASSIGN(fonts, [NSMutableDictionary dictionary]);
543  ASSIGN(attrs, [NSMutableArray array]);
544  ASSIGN(colours, [NSMutableArray array]);
545  [attrs addObject: attr];
546  RELEASE(attr);
547}
548
549- (void) setEncoding: (NSStringEncoding)anEncoding
550{
551  encoding = anEncoding;
552}
553
554- (RTFAttribute*) attr
555{
556  return [attrs lastObject];
557}
558
559- (void) push
560{
561  RTFAttribute *attr = [[attrs lastObject] copy];
562
563  [attrs addObject: attr];
564  RELEASE(attr);
565}
566
567- (void) pop
568{
569  [attrs removeLastObject];
570  ((RTFAttribute*)[attrs lastObject])->changed = YES;
571}
572
573- (NSAttributedString*) parseRTF: (NSData *)rtfData
574	      documentAttributes: (NSDictionary **)dict
575			   class: (Class)class
576{
577  CREATE_AUTORELEASE_POOL(pool);
578  RTFscannerCtxt scanner;
579  StringContext stringCtxt;
580
581  // We read in the first few characters to find out which
582  // encoding we have
583  if ([rtfData length] < 10)
584    {
585      // Too short to be an RTF
586      return nil;
587    }
588
589  // Reset this RFTConsumer, as it might already have been used!
590  _class = class;
591  [self reset];
592
593  initStringContext(&stringCtxt, rtfData);
594  lexInitContext(&scanner, &stringCtxt, (int (*)(void*))readString);
595  [result beginEditing];
596  NS_DURING
597    GSRTFparse((void *)self, &scanner);
598  NS_HANDLER
599    NSLog(@"Problem during RTF Parsing: %@",
600	  [localException reason]);
601  //[localException raise];
602  NS_ENDHANDLER
603  [result endEditing];
604
605  RELEASE(pool);
606  // document attributes
607  if (dict)
608    {
609      *dict = [self documentAttributes];
610    }
611
612  if (classInheritsFromNSMutableAttributedString (_class))
613    {
614      RETAIN (result);
615      AUTORELEASE (result);
616      return result;
617    }
618  else
619    {
620      return AUTORELEASE ([[_class alloc] initWithAttributedString: result]);
621    }
622}
623
624- (void) appendString: (NSString*)string
625{
626  int  oldPosition = [result length];
627  int  textlen = [string length];
628  NSRange insertionRange = NSMakeRange(oldPosition,0);
629  NSMutableDictionary *attributes;
630
631  if (!ignore && textlen)
632    {
633      RTFAttribute* attr = [self attr];
634      [result replaceCharactersInRange: insertionRange
635	      withString: string];
636
637      if (attr->changed)
638        {
639	  NSParagraphStyle *ps = [attr->paragraph copy];
640	  attributes = [[NSMutableDictionary alloc]
641			 initWithObjectsAndKeys:
642			   [attr currentFont], NSFontAttributeName,
643			   ps, NSParagraphStyleAttributeName,
644			   nil];
645	  DESTROY(ps);
646	  if ([attr underline])
647	    {
648	      [attributes setObject: [attr underline]
649			  forKey: NSUnderlineStyleAttributeName];
650	    }
651	  if ([attr strikethrough])
652	    {
653	      [attributes setObject: [attr strikethrough]
654			  forKey: NSStrikethroughStyleAttributeName];
655	    }
656	  if (attr->script)
657	    {
658	      [attributes setObject: [attr script]
659			  forKey: NSSuperscriptAttributeName];
660	    }
661	  if (attr->fgColour != nil)
662	    {
663	      [attributes setObject: attr->fgColour
664			  forKey: NSForegroundColorAttributeName];
665	    }
666	  if (attr->bgColour != nil)
667	    {
668	      [attributes setObject: attr->bgColour
669			  forKey: NSBackgroundColorAttributeName];
670	    }
671	  if (attr->ulColour != nil)
672	    {
673	      [attributes setObject: attr->ulColour
674			  forKey: NSUnderlineColorAttributeName];
675	    }
676
677	  [result setAttributes: attributes
678		  range: NSMakeRange(oldPosition, textlen)];
679	  DESTROY(attributes);
680	  attr->changed = NO;
681	}
682    }
683}
684
685- (void) appendHelpLink: (NSString*)fileName marker: (NSString*)markerName
686{
687  int  oldPosition = [result length];
688  NSRange insertionRange = NSMakeRange(oldPosition,0);
689
690  if (!ignore)
691    {
692      GSHelpLinkAttachment* attachment;
693      RTFAttribute* attr = [self attr];
694      NSMutableDictionary* attributes = nil;
695      NSMutableAttributedString* str = nil;
696
697      attachment =
698	[[GSHelpLinkAttachment alloc]
699	  initWithFileName: fileName
700		markerName: markerName];
701      if (attachment == nil)
702	{
703	  NSLog(@"No attachment at %d", oldPosition);
704	  return;
705	}
706
707      attributes = [[NSMutableDictionary alloc]
708		     initWithObjectsAndKeys:
709		       [attr currentFont], NSFontAttributeName,
710		       attr->paragraph, NSParagraphStyleAttributeName,
711		       nil];
712
713      str = (NSMutableAttributedString*) [NSMutableAttributedString
714                     attributedStringWithAttachment: attachment];
715
716      [str addAttributes: attributes range: NSMakeRange (0, [str length])];
717
718      [result replaceCharactersInRange: insertionRange withAttributedString: str];
719      attr->changed = YES;
720      RELEASE(attributes);
721      RELEASE(attachment);
722    }
723}
724
725- (void) appendHelpMarker: (NSString*)markerName
726{
727  int  oldPosition = [result length];
728  NSRange insertionRange = NSMakeRange(oldPosition,0);
729
730  if (!ignore)
731    {
732      GSHelpMarkerAttachment* attachment;
733      RTFAttribute* attr = [self attr];
734      NSMutableDictionary* attributes = nil;
735      NSMutableAttributedString* str = nil;
736
737      attachment =
738	[[GSHelpMarkerAttachment alloc] initWithMarkerName: markerName];
739      if (attachment == nil)
740	{
741	  NSLog(@"No attachment at %d", oldPosition);
742	  return;
743	}
744
745      attributes = [[NSMutableDictionary alloc]
746		     initWithObjectsAndKeys:
747		       [attr currentFont], NSFontAttributeName,
748		       attr->paragraph, NSParagraphStyleAttributeName,
749		       nil];
750
751      str = (NSMutableAttributedString*) [NSMutableAttributedString
752                     attributedStringWithAttachment: attachment];
753
754      [str addAttributes: attributes range: NSMakeRange (0, [str length])];
755
756      [result replaceCharactersInRange: insertionRange withAttributedString: str];
757      attr->changed = YES;
758      RELEASE(attributes);
759      RELEASE(attachment);
760    }
761}
762
763- (void) appendField: (int)start
764         instruction: (NSString*)instruction
765{
766  if (!ignore)
767    {
768      int  oldPosition = start;
769      int  textlen = [result length] - start;
770      NSRange insertionRange = NSMakeRange(oldPosition, textlen);
771
772      if ([instruction hasPrefix: @"HYPERLINK "])
773        {
774          NSDictionary *attributes;
775          NSString *link = [instruction substringFromIndex: 10];
776
777          if ([link characterAtIndex: 0] == (unichar)'\"')
778            {
779              link = [link substringWithRange: NSMakeRange(1, [link length] - 2)];
780            }
781
782          attributes = [[NSDictionary alloc]
783                                       initWithObjectsAndKeys:
784                           link, NSLinkAttributeName,
785                                     [NSNumber numberWithInt : 1], NSUnderlineStyleAttributeName,
786                         [NSColor blueColor], NSForegroundColorAttributeName,
787                         nil];
788          [result addAttributes: attributes
789                         range: insertionRange];
790          DESTROY(attributes);
791        }
792    }
793}
794
795- (void) appendImage: (NSString*)string
796{
797  // Do nothing for RTF
798}
799
800@end
801
802#undef IGNORE
803#define FONTS	((RTFConsumer *)ctxt)->fonts
804#define COLOURS	((RTFConsumer *)ctxt)->colours
805#define RESULT	((RTFConsumer *)ctxt)->result
806#define IGNORE	((RTFConsumer *)ctxt)->ignore
807#define TEXTPOSITION [RESULT length]
808#define DOCUMENTATTRIBUTES ((RTFConsumer*)ctxt)->documentAttributes
809#define ENCODING ((RTFConsumer *)ctxt)->encoding
810
811#define FILES ((RTFDConsumer*)ctxt)->files
812
813#define CTXT [((RTFConsumer *)ctxt) attr]
814#define CHANGED CTXT->changed
815#define PARAGRAPH CTXT->paragraph
816#define FONTNAME CTXT->fontName
817#define SCRIPT CTXT->script
818#define ITALIC CTXT->italic
819#define BOLD CTXT->bold
820#define UNDERLINE CTXT->underline
821#define STRIKETHROUGH CTXT->strikethrough
822#define FGCOLOUR CTXT->fgColour
823#define BGCOLOUR CTXT->bgColour
824#define ULCOLOUR CTXT->ulColour
825
826#define PAPERSIZE @"PaperSize"
827#define LEFTMARGIN @"LeftMargin"
828#define RIGHTMARGIN @"RightMargin"
829#define TOPMARGIN @"TopMargin"
830#define BUTTOMMARGIN @"ButtomMargin"
831
832/*
833  we must implement from the rtfConsumerFunctions.h file (Supporting files)
834  this includes the yacc error handling and output
835*/
836
837/* handle errors (this is the yacc error mech)	*/
838void GSRTFerror (void *ctxt, void *lctxt, const char *msg)
839{
840/*  [NSException raise:NSInvalidArgumentException
841	       format:@"Syntax error in RTF: %s", msg];*/
842  NSDebugLLog(@"RTFParser",@"Syntax error in RTF: %s", msg);
843}
844
845void GSRTFgenericRTFcommand (void *ctxt, RTFcmd cmd)
846{
847  NSDebugLLog(@"RTFParser", @"encountered rtf cmd:%s", cmd.name);
848  if (!cmd.isEmpty)
849    NSDebugLLog(@"RTFParser", @" argument is %d\n", cmd.parameter);
850}
851
852//Start: we're doing some initialization
853void GSRTFstart (void *ctxt)
854{
855  NSDebugLLog(@"RTFParser", @"Start RTF parsing");
856}
857
858// Finished to parse one piece of RTF.
859void GSRTFstop (void *ctxt)
860{
861  //<!> close all open bolds et al.
862  NSDebugLLog(@"RTFParser", @"End RTF parsing");
863}
864
865int GSRTFgetPosition(void *ctxt)
866{
867  return [((RTFConsumer *)ctxt)->result length];
868}
869
870void GSRTFopenBlock (void *ctxt, BOOL ignore)
871{
872  if (!IGNORE)
873    {
874      [(RTFConsumer *)ctxt push];
875    }
876  // Switch off any output for ignored block statements
877  if (ignore)
878    {
879      IGNORE++;
880    }
881}
882
883void GSRTFcloseBlock (void *ctxt, BOOL ignore)
884{
885  if (ignore)
886    {
887      IGNORE--;
888    }
889  if (!IGNORE)
890    {
891      [(RTFConsumer *)ctxt pop];
892    }
893}
894
895void GSRTFmangleText (void *ctxt, const char *text)
896{
897  NSData *data = [[NSData alloc] initWithBytes: (void*)text
898				 length: strlen(text)];
899  NSString *str = [[NSString alloc] initWithData: data
900				    encoding: ENCODING];
901
902  [(RTFConsumer *)ctxt appendString: str];
903  DESTROY(str);
904  DESTROY(data);
905}
906
907void GSRTFunicode (void *ctxt, int uchar)
908{
909  // Don't add the attachment character, this gets handled separatly
910  if (uchar != (int)NSAttachmentCharacter)
911    {
912      unichar chars = uchar;
913      NSString *str = [[NSString alloc] initWithCharacters: &chars
914                                                    length: 1];
915      [(RTFConsumer *)ctxt appendString: str];
916      DESTROY(str);
917    }
918}
919
920void GSRTFregisterFont (void *ctxt, const char *fontName,
921			RTFfontFamily family, int fontNumber)
922{
923  NSString		*fontNameString;
924  NSNumber		*fontId = [NSNumber numberWithInt: fontNumber];
925
926  if (!fontName || !*fontName)
927    {
928      [NSException raise: NSInvalidArgumentException
929		   format: @"Error in RTF (font omitted?), position:%lu",
930		   (unsigned long) TEXTPOSITION];
931    }
932  // exclude trailing ';' from fontName
933  if (';' == fontName[strlen(fontName)-1])
934    {
935      fontNameString = [NSString stringWithCString: fontName
936				 length: strlen(fontName)-1];
937    }
938  else
939    {
940      fontNameString = [NSString stringWithCString: fontName
941				 length: strlen(fontName)];
942    }
943  [FONTS setObject: fontNameString forKey: fontId];
944}
945
946void GSRTFfontNumber (void *ctxt, int fontNumber)
947{
948  NSNumber *fontId = [NSNumber numberWithInt: fontNumber];
949  NSString *fontName = [FONTS objectForKey: fontId];
950
951  if (fontName == nil)
952    {
953      /* we're about to set an unknown font */
954      [NSException raise: NSInvalidArgumentException
955		   format: @"Error in RTF (referring to undefined font \\f%d), position:%lu",
956		   fontNumber,
957		   (unsigned long) TEXTPOSITION];
958    }
959  else
960    {
961      if (![fontName isEqual: FONTNAME])
962        {
963	    ASSIGN(FONTNAME, fontName);
964	    CHANGED = YES;
965	}
966    }
967}
968
969//	<N> fontSize is in halfpoints according to spec
970void GSRTFfontSize (void *ctxt, int fontSize)
971{
972  float size = halfpoints2points(fontSize);
973
974  if (size != CTXT->fontSize)
975    {
976      CTXT->fontSize = size;
977      CHANGED = YES;
978    }
979}
980
981void GSRTFpaperWidth (void *ctxt, int width)
982{
983  float fwidth = twips2points(width);
984  NSMutableDictionary *dict = DOCUMENTATTRIBUTES;
985  NSValue *val = [dict objectForKey: PAPERSIZE];
986  NSSize size;
987
988  if (val == nil)
989    {
990      size = NSMakeSize(fwidth, 792);
991    }
992  else
993    {
994      size = [val sizeValue];
995      size.width = fwidth;
996    }
997  [dict setObject: [NSValue valueWithSize: size]  forKey: PAPERSIZE];
998}
999
1000void GSRTFpaperHeight (void *ctxt, int height)
1001{
1002  float fheight = twips2points(height);
1003  NSMutableDictionary *dict = DOCUMENTATTRIBUTES;
1004  NSValue *val = [dict objectForKey: PAPERSIZE];
1005  NSSize size;
1006
1007  if (val == nil)
1008    {
1009      size = NSMakeSize(612, fheight);
1010    }
1011  else
1012    {
1013      size = [val sizeValue];
1014      size.height = fheight;
1015    }
1016  [dict setObject: [NSValue valueWithSize: size]  forKey: PAPERSIZE];
1017}
1018
1019void GSRTFmarginLeft (void *ctxt, int margin)
1020{
1021  float fmargin = twips2points(margin);
1022  NSMutableDictionary *dict = DOCUMENTATTRIBUTES;
1023
1024  [dict setObject: [NSNumber numberWithFloat: fmargin]  forKey: LEFTMARGIN];
1025}
1026
1027void GSRTFmarginRight (void *ctxt, int margin)
1028{
1029  float fmargin = twips2points(margin);
1030  NSMutableDictionary *dict = DOCUMENTATTRIBUTES;
1031
1032  [dict setObject: [NSNumber numberWithFloat: fmargin]  forKey: RIGHTMARGIN];
1033}
1034
1035void GSRTFmarginTop (void *ctxt, int margin)
1036{
1037  float fmargin = twips2points(margin);
1038  NSMutableDictionary *dict = DOCUMENTATTRIBUTES;
1039
1040  [dict setObject: [NSNumber numberWithFloat: fmargin]  forKey: TOPMARGIN];
1041}
1042
1043void GSRTFmarginButtom (void *ctxt, int margin)
1044{
1045  float fmargin = twips2points(margin);
1046  NSMutableDictionary *dict = DOCUMENTATTRIBUTES;
1047
1048  [dict setObject: [NSNumber numberWithFloat: fmargin]  forKey: BUTTOMMARGIN];
1049}
1050
1051void GSRTFfirstLineIndent (void *ctxt, int indent)
1052{
1053  NSMutableParagraphStyle *para = PARAGRAPH;
1054  float findent = twips2points(indent);
1055
1056  CTXT->real_fi = findent;
1057
1058  findent = CTXT->real_li + CTXT->real_fi;
1059
1060  // for attributed strings only positiv indent is allowed
1061  if ((findent >= 0.0) && ([para firstLineHeadIndent] != findent))
1062    {
1063      [para setFirstLineHeadIndent: findent];
1064      CHANGED = YES;
1065    }
1066}
1067
1068void GSRTFleftIndent (void *ctxt, int indent)
1069{
1070  NSMutableParagraphStyle *para = PARAGRAPH;
1071  float findent = twips2points(indent);
1072
1073  CTXT->real_li = findent;
1074
1075  // for attributed strings only positiv indent is allowed
1076  if ((findent >= 0.0) && ([para headIndent] != findent))
1077    {
1078      [para setHeadIndent: findent];
1079      CHANGED = YES;
1080    }
1081
1082  findent = CTXT->real_li + CTXT->real_fi;
1083  if ((findent >= 0.0) && ([para firstLineHeadIndent] != findent))
1084    {
1085      [para setFirstLineHeadIndent: findent];
1086      CHANGED = YES;
1087    }
1088}
1089
1090void GSRTFrightIndent (void *ctxt, int indent)
1091{
1092  NSMutableParagraphStyle *para = PARAGRAPH;
1093  float findent = twips2points(indent);
1094
1095  // for attributed strings only positiv indent is allowed
1096  if ((findent >= 0.0) && ([para tailIndent] != findent))
1097    {
1098      [para setTailIndent: -findent];
1099      CHANGED = YES;
1100    }
1101}
1102
1103void GSRTFtabstop (void *ctxt, int location)
1104{
1105  float flocation = twips2points(location);
1106
1107  if (flocation >= 0.0)
1108    {
1109      [CTXT addTab: flocation type: NSLeftTabStopType];
1110    }
1111}
1112
1113void GSRTFalignCenter (void *ctxt)
1114{
1115  NSMutableParagraphStyle *para = PARAGRAPH;
1116
1117  if ([para alignment] != NSCenterTextAlignment)
1118    {
1119      [para setAlignment: NSCenterTextAlignment];
1120      CHANGED = YES;
1121    }
1122}
1123
1124void GSRTFalignJustified (void *ctxt)
1125{
1126  NSMutableParagraphStyle *para = PARAGRAPH;
1127
1128  if ([para alignment] != NSJustifiedTextAlignment)
1129    {
1130      [para setAlignment: NSJustifiedTextAlignment];
1131      CHANGED = YES;
1132    }
1133}
1134
1135void GSRTFalignLeft (void *ctxt)
1136{
1137  NSMutableParagraphStyle *para = PARAGRAPH;
1138
1139  if ([para alignment] != NSLeftTextAlignment)
1140    {
1141      [para setAlignment: NSLeftTextAlignment];
1142      CHANGED = YES;
1143    }
1144}
1145
1146void GSRTFalignRight (void *ctxt)
1147{
1148  NSMutableParagraphStyle *para = PARAGRAPH;
1149
1150  if ([para alignment] != NSRightTextAlignment)
1151    {
1152      [para setAlignment: NSRightTextAlignment];
1153      CHANGED = YES;
1154    }
1155}
1156
1157void GSRTFspaceAbove (void *ctxt, int space)
1158{
1159  NSMutableParagraphStyle *para = PARAGRAPH;
1160  float fspace = twips2points(space);
1161
1162  if (fspace >= 0.0)
1163    {
1164      [para setParagraphSpacing: fspace];
1165      CHANGED = YES;
1166    }
1167}
1168
1169void GSRTFlineSpace (void *ctxt, int space)
1170{
1171  NSMutableParagraphStyle *para = PARAGRAPH;
1172  float fspace = twips2points(space);
1173
1174  if (space == 1000)
1175    {
1176      [para setMinimumLineHeight: 0.0];
1177      [para setMaximumLineHeight: 0.0];
1178      CHANGED = YES;
1179    }
1180  else if (fspace < 0.0)
1181    {
1182      [para setMaximumLineHeight: -fspace];
1183      CHANGED = YES;
1184    }
1185  else
1186    {
1187      [para setMinimumLineHeight: fspace];
1188      CHANGED = YES;
1189    }
1190}
1191
1192void GSRTFdefaultParagraph (void *ctxt)
1193{
1194  [CTXT resetParagraphStyle];
1195}
1196
1197void GSRTFstyle (void *ctxt, int style)
1198{
1199}
1200
1201void GSRTFdefaultCharacterStyle (void *ctxt)
1202{
1203  [CTXT resetFont];
1204}
1205
1206void GSRTFaddColor (void *ctxt, int red, int green, int blue)
1207{
1208  NSColor *colour = [NSColor colorWithCalibratedRed: red/255.0
1209			     green: green/255.0
1210			     blue: blue/255.0
1211			     alpha: 1.0];
1212
1213  [COLOURS addObject: colour];
1214}
1215
1216void GSRTFaddDefaultColor (void *ctxt)
1217{
1218  [COLOURS addObject: [NSColor textColor]];
1219}
1220
1221void GSRTFcolorbg (void *ctxt, int color)
1222{
1223  if ([COLOURS count] <= (unsigned int)color)
1224    {
1225      ASSIGN (BGCOLOUR, [NSColor whiteColor]);
1226    }
1227  else
1228    {
1229      ASSIGN (BGCOLOUR, [COLOURS objectAtIndex: color]);
1230    }
1231  CHANGED = YES;
1232}
1233
1234void GSRTFcolorfg (void *ctxt, int color)
1235{
1236  if ([COLOURS count] <= (unsigned int)color)
1237    {
1238      ASSIGN (FGCOLOUR, [NSColor blackColor]);
1239    }
1240  else
1241    {
1242      ASSIGN (FGCOLOUR, [COLOURS objectAtIndex: color]);
1243    }
1244  CHANGED = YES;
1245}
1246
1247void GSRTFunderlinecolor (void *ctxt, int color)
1248{
1249  if ([COLOURS count] <= (unsigned int)color)
1250    {
1251      ASSIGN (ULCOLOUR, [NSColor blackColor]);
1252    }
1253  else
1254    {
1255      ASSIGN (ULCOLOUR, [COLOURS objectAtIndex: color]);
1256    }
1257  CHANGED = YES;
1258}
1259
1260void GSRTFsubscript (void *ctxt, int script)
1261{
1262  script = (int) (-halfpoints2points(script) / 3.0);
1263
1264  if (script != SCRIPT)
1265    {
1266      SCRIPT = script;
1267      CHANGED = YES;
1268    }
1269}
1270
1271void GSRTFsuperscript (void *ctxt, int script)
1272{
1273  script = (int) (halfpoints2points(script) / 3.0);
1274
1275  if (script != SCRIPT)
1276    {
1277      SCRIPT = script;
1278      CHANGED = YES;
1279    }
1280}
1281
1282void GSRTFitalic (void *ctxt, BOOL state)
1283{
1284  if (state != ITALIC)
1285    {
1286      ITALIC = state;
1287      CHANGED = YES;
1288    }
1289}
1290
1291void GSRTFbold (void *ctxt, BOOL state)
1292{
1293  if (state != BOLD)
1294    {
1295      BOLD = state;
1296      CHANGED = YES;
1297    }
1298}
1299
1300void GSRTFunderline (void *ctxt, BOOL state, NSInteger style)
1301{
1302  if (state == NO)
1303    {
1304      style = NSUnderlineStyleNone;
1305    }
1306
1307  if (UNDERLINE != style)
1308    {
1309      UNDERLINE = style;
1310      CHANGED = YES;
1311    }
1312}
1313
1314void GSRTFstrikethrough (void *ctxt, NSInteger style)
1315{
1316  if (STRIKETHROUGH != style)
1317    {
1318      STRIKETHROUGH = style;
1319      CHANGED = YES;
1320    }
1321}
1322
1323void GSRTFstrikethroughDouble (void *ctxt)
1324{
1325  const NSInteger style = NSUnderlineStyleDouble | NSUnderlinePatternSolid;
1326
1327  if (STRIKETHROUGH != style)
1328    {
1329      STRIKETHROUGH = style;
1330      CHANGED = YES;
1331    }
1332}
1333
1334void GSRTFparagraph (void *ctxt)
1335{
1336  GSRTFmangleText(ctxt, "\n");
1337  CTXT->tabChanged = NO;
1338}
1339
1340void GSRTFNeXTGraphic (void *ctxt, const char *fileName, int width, int height)
1341{
1342  [(RTFDConsumer *)ctxt appendImage: [NSString stringWithCString: fileName]];
1343}
1344
1345void GSRTFNeXTHelpLink (void *ctxt, int num, const char *markername,
1346			const char *linkFilename, const char *linkMarkername)
1347{
1348  NSRange range;
1349  NSString *fileName = [NSString stringWithCString: linkFilename];
1350  NSString *markerName = [NSString stringWithCString: linkMarkername];
1351
1352  range = [fileName rangeOfString: @";"];
1353  if (range.location != NSNotFound)
1354    fileName = [fileName substringToIndex:range.location];
1355
1356  range = [markerName rangeOfString: @";"];
1357  if (range.location == 0)
1358    markerName = nil;
1359  else if (range.location != NSNotFound)
1360    markerName = [markerName substringToIndex:range.location];
1361
1362  [(RTFDConsumer *)ctxt appendHelpLink: fileName marker: markerName];
1363}
1364
1365void GSRTFNeXTHelpMarker (void *ctxt, int num, const char *markername)
1366{
1367  NSRange range;
1368  NSString *markerName = [NSString stringWithCString: markername];
1369
1370  range = [markerName rangeOfString: @";"];
1371  if (range.location != NSNotFound)
1372    markerName = [markerName substringToIndex:range.location];
1373  [(RTFDConsumer *)ctxt appendHelpMarker: markerName];
1374}
1375
1376void GSRTFaddField (void *ctxt, int start, const char *inst)
1377{
1378  NSString *fieldInstruction;
1379
1380  // Ignore leading blanks
1381  while (inst[0] == ' ')
1382    {
1383      inst++;
1384    }
1385  fieldInstruction = [[NSString alloc] initWithCString: inst
1386                                              encoding: ENCODING];
1387
1388  [(RTFDConsumer *)ctxt appendField: start instruction: fieldInstruction];
1389  DESTROY(fieldInstruction);
1390}
1391
1392void GSRTFencoding(void *ctxt, int encoding)
1393{
1394  switch (encoding)
1395    {
1396    case 1:
1397      [(RTFDConsumer *)ctxt setEncoding: NSISOLatin1StringEncoding];
1398      break;
1399    case 2:
1400      [(RTFDConsumer *)ctxt setEncoding: NSMacOSRomanStringEncoding];
1401      break;
1402    case 3:
1403      // FIXME: Code page 437 kCFStringEncodingDOSLatinUS
1404      [(RTFDConsumer *)ctxt setEncoding: NSISOLatin1StringEncoding];
1405      break;
1406    case 4:
1407      // FIXME: Code page 850 kCFStringEncodingDOSLatin1
1408      [(RTFDConsumer *)ctxt setEncoding: NSISOLatin1StringEncoding];
1409      break;
1410    case 1250:
1411      [(RTFDConsumer *)ctxt setEncoding: NSWindowsCP1250StringEncoding];
1412      break;
1413    case 1251:
1414      [(RTFDConsumer *)ctxt setEncoding: NSWindowsCP1251StringEncoding];
1415      break;
1416    case 1252:
1417      [(RTFDConsumer *)ctxt setEncoding: NSWindowsCP1252StringEncoding];
1418      break;
1419    case 1253:
1420      [(RTFDConsumer *)ctxt setEncoding: NSWindowsCP1253StringEncoding];
1421      break;
1422    case 1254:
1423      [(RTFDConsumer *)ctxt setEncoding: NSWindowsCP1254StringEncoding];
1424      break;
1425    default:
1426      NSLog(@"Setting unknown encoding %d", encoding);
1427      break;
1428    }
1429}
1430