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