Fixed support for RTF documents using ANSI charset and added support
[wine] / dlls / riched20 / reader.c
1 /*
2  * WINE RTF file reader
3  *
4  * Portions Copyright 2004 Mike McCormack for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 /*
22  * Derived from RTF Tools by Paul DuBois (dubois@primate.wisc.edu)
23  * Homepage: http://www.snake.net/software/RTF/
24  * Original license follows:
25  */
26
27 /*
28  * reader.c - RTF file reader.  Release 1.10.
29  *
30  * ....
31  *
32  * Author: Paul DuBois  dubois@primate.wisc.edu
33  *
34  * This software may be redistributed without restriction and used for
35  * any purpose whatsoever.
36  */
37
38 #include <stdio.h>
39 #include <ctype.h>
40 #include <string.h>
41 #include <stdarg.h>
42 #include <stdlib.h>
43 #include <assert.h>
44
45 #include "windef.h"
46 #include "winbase.h"
47 #include "wine/debug.h"
48
49 #include "editor.h"
50 #include "rtf.h"
51
52 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
53
54 extern HANDLE me_heap;
55
56 static int      _RTFGetChar(RTF_Info *);
57 static void     _RTFGetToken (RTF_Info *);
58 static void     _RTFGetToken2 (RTF_Info *);
59 static int      GetChar (RTF_Info *);
60 static void     ReadFontTbl (RTF_Info *);
61 static void     ReadColorTbl (RTF_Info *);
62 static void     ReadStyleSheet (RTF_Info *);
63 static void     ReadInfoGroup (RTF_Info *);
64 static void     ReadPictGroup (RTF_Info *);
65 static void     ReadObjGroup (RTF_Info *);
66 static void     LookupInit (void);
67 static void     Lookup (RTF_Info *, char *);
68 static int      Hash (char*);
69
70 static void     CharAttr(RTF_Info *info);
71 static void     CharSet(RTF_Info *info);
72 static void     DocAttr(RTF_Info *info);
73
74 static void     RTFFlushCPOutputBuffer(RTF_Info *info);
75 static void     RTFPutCodePageChar(RTF_Info *info, int c);
76
77
78 int _RTFGetChar(RTF_Info *info)
79 {
80         int ch;
81
82         TRACE("\n");
83
84         /* if the last buffer wasn't full, it's EOF */
85   if (info->dwInputSize > 0 &&
86     info->dwInputSize == info->dwInputUsed && info->dwInputSize < sizeof(info->InputBuffer))
87     return EOF;
88   if (info->dwInputSize <= info->dwInputUsed)
89         {
90                 long count = 0;
91                 info->editstream.dwError = info->editstream.pfnCallback(info->editstream.dwCookie, 
92                         info->InputBuffer, sizeof(info->InputBuffer), &count);
93         /* if error, it's EOF */
94                 if (info->editstream.dwError)
95                   return EOF;
96         /* if no bytes read, it's EOF */
97                 if(count == 0)
98                         return EOF;
99                 info->dwInputSize = count;
100                 info->dwInputUsed = 0;
101         }
102         ch = info->InputBuffer[info->dwInputUsed++];
103         if (!ch)
104                  return EOF;
105         return ch;
106 }
107
108 void RTFSetEditStream(RTF_Info *info, EDITSTREAM *es)
109 {
110         TRACE("\n");
111
112         info->editstream.dwCookie = es->dwCookie;
113         info->editstream.dwError  = es->dwError;
114         info->editstream.pfnCallback = es->pfnCallback;
115 }
116
117 static void
118 RTFDestroyAttrs(RTF_Info *info)
119 {
120         RTFColor        *cp;
121         RTFFont         *fp;
122         RTFStyle        *sp;
123         RTFStyleElt     *eltList, *ep;
124
125         while (info->fontList != (RTFFont *) NULL)
126         {
127                 fp = info->fontList->rtfNextFont;
128                 RTFFree (info->fontList->rtfFName);
129                 RTFFree ((char *) info->fontList);
130                 info->fontList = fp;
131         }
132         while (info->colorList != (RTFColor *) NULL)
133         {
134                 cp = info->colorList->rtfNextColor;
135                 RTFFree ((char *) info->colorList);
136                 info->colorList = cp;
137         }
138         while (info->styleList != (RTFStyle *) NULL)
139         {
140                 sp = info->styleList->rtfNextStyle;
141                 eltList = info->styleList->rtfSSEList;
142                 while (eltList != (RTFStyleElt *) NULL)
143                 {
144                         ep = eltList->rtfNextSE;
145                         RTFFree (eltList->rtfSEText);
146                         RTFFree ((char *) eltList);
147                         eltList = ep;
148                 }
149                 RTFFree (info->styleList->rtfSName);
150                 RTFFree ((char *) info->styleList);
151                 info->styleList = sp;
152         }
153 }
154
155
156 void
157 RTFDestroy(RTF_Info *info)
158 {
159         if (info->rtfTextBuf)
160         {
161                 RTFFree(info->rtfTextBuf);
162                 RTFFree(info->pushedTextBuf);
163         }
164         RTFDestroyAttrs(info);
165         RTFFree(info->cpOutputBuffer);
166 }
167
168
169 /*
170  * Initialize the reader.  This may be called multiple times,
171  * to read multiple files.  The only thing not reset is the input
172  * stream; that must be done with RTFSetStream().
173  */
174
175 void RTFInit(RTF_Info *info)
176 {
177         int     i;
178
179         TRACE("\n");
180
181         if (info->rtfTextBuf == (char *) NULL)  /* initialize the text buffers */
182         {
183                 info->rtfTextBuf = RTFAlloc (rtfBufSiz);
184                 info->pushedTextBuf = RTFAlloc (rtfBufSiz);
185                 if (info->rtfTextBuf == (char *) NULL
186                         || info->pushedTextBuf == (char *) NULL)
187                         RTFPanic (info,"Cannot allocate text buffers.");
188                 info->rtfTextBuf[0] = info->pushedTextBuf[0] = '\0';
189         }
190
191         RTFFree (info->inputName);
192         RTFFree (info->outputName);
193         info->inputName = info->outputName = (char *) NULL;
194
195         /* initialize lookup table */
196         LookupInit ();
197
198         for (i = 0; i < rtfMaxClass; i++)
199                 RTFSetClassCallback (info, i, (RTFFuncPtr) NULL);
200         for (i = 0; i < rtfMaxDestination; i++)
201                 RTFSetDestinationCallback (info, i, (RTFFuncPtr) NULL);
202
203         /* install built-in destination readers */
204         RTFSetDestinationCallback (info, rtfFontTbl, ReadFontTbl);
205         RTFSetDestinationCallback (info, rtfColorTbl, ReadColorTbl);
206         RTFSetDestinationCallback (info, rtfStyleSheet, ReadStyleSheet);
207         RTFSetDestinationCallback (info, rtfInfo, ReadInfoGroup);
208         RTFSetDestinationCallback (info, rtfPict, ReadPictGroup);
209         RTFSetDestinationCallback (info, rtfObject, ReadObjGroup);
210
211
212         RTFSetReadHook (info, (RTFFuncPtr) NULL);
213
214         /* dump old lists if necessary */
215
216         RTFDestroyAttrs(info);
217
218         info->ansiCodePage = 1252; /* Latin-1 */
219         
220         info->unicodeLength = 1; /* \uc1 is the default */
221         info->codePage = info->ansiCodePage;
222
223         info->rtfClass = -1;
224         info->pushedClass = -1;
225         info->pushedChar = EOF;
226
227         info->rtfLineNum = 0;
228         info->rtfLinePos = 0;
229         info->prevChar = EOF;
230         info->bumpLine = 0;
231
232         info->dwCPOutputCount = 0;
233         if (!info->cpOutputBuffer)
234         {
235                 info->dwMaxCPOutputCount = 0x1000;
236                 info->cpOutputBuffer = RTFAlloc(info->dwMaxCPOutputCount);
237         }
238 }
239
240 /*
241  * Set or get the input or output file name.  These are never guaranteed
242  * to be accurate, only insofar as the calling program makes them so.
243  */
244
245 void RTFSetInputName(RTF_Info *info, char *name)
246 {
247         TRACE("\n");
248
249         if ((info->inputName = RTFStrSave (name)) == (char *) NULL)
250                 RTFPanic (info,"RTFSetInputName: out of memory");
251 }
252
253
254 char *RTFGetInputName(RTF_Info *info)
255 {
256         return (info->inputName);
257 }
258
259
260 void RTFSetOutputName(RTF_Info *info, char *name)
261 {
262         TRACE("\n");
263
264         if ((info->outputName = RTFStrSave (name)) == (char *) NULL)
265                 RTFPanic (info, "RTFSetOutputName: out of memory");
266 }
267
268
269 char *RTFGetOutputName(RTF_Info *info)
270 {
271         return (info->outputName);
272 }
273
274
275
276 /* ---------------------------------------------------------------------- */
277
278 /*
279  * Callback table manipulation routines
280  */
281
282
283 /*
284  * Install or return a writer callback for a token class
285  */
286
287 void RTFSetClassCallback(RTF_Info *info, int class, RTFFuncPtr callback)
288 {
289         if (class >= 0 && class < rtfMaxClass)
290                 info->ccb[class] = callback;
291 }
292
293
294 RTFFuncPtr RTFGetClassCallback(RTF_Info *info, int class)
295 {
296         if (class >= 0 && class < rtfMaxClass)
297                 return (info->ccb[class]);
298         return ((RTFFuncPtr) NULL);
299 }
300
301
302 /*
303  * Install or return a writer callback for a destination type
304  */
305
306 void RTFSetDestinationCallback(RTF_Info *info, int dest, RTFFuncPtr callback)
307 {
308         if (dest >= 0 && dest < rtfMaxDestination)
309                 info->dcb[dest] = callback;
310 }
311
312
313 RTFFuncPtr RTFGetDestinationCallback(RTF_Info *info, int dest)
314 {
315         if (dest >= 0 && dest < rtfMaxDestination)
316                 return (info->dcb[dest]);
317         return ((RTFFuncPtr) NULL);
318 }
319
320
321 /* ---------------------------------------------------------------------- */
322
323 /*
324  * Token reading routines
325  */
326
327
328 /*
329  * Read the input stream, invoking the writer's callbacks
330  * where appropriate.
331  */
332
333 void RTFRead(RTF_Info *info)
334 {
335         while (RTFGetToken (info) != rtfEOF)
336                 RTFRouteToken (info);
337 }
338
339
340 /*
341  * Route a token.  If it's a destination for which a reader is
342  * installed, process the destination internally, otherwise
343  * pass the token to the writer's class callback.
344  */
345
346 void RTFRouteToken(RTF_Info *info)
347 {
348         RTFFuncPtr      p;
349
350         TRACE("\n");
351
352         if (info->rtfClass < 0 || info->rtfClass >= rtfMaxClass)        /* watchdog */
353         {
354                 RTFPanic (info,"Unknown class %d: %s (reader malfunction)",
355                                                         info->rtfClass, info->rtfTextBuf);
356         }
357         if (RTFCheckCM (info, rtfControl, rtfDestination))
358         {
359                 /* invoke destination-specific callback if there is one */
360                 if ((p = RTFGetDestinationCallback (info, info->rtfMinor))
361                                                         != (RTFFuncPtr) NULL)
362                 {
363                         (*p) (info);
364                         return;
365                 }
366         }
367         /* invoke class callback if there is one */
368         if ((p = RTFGetClassCallback (info, info->rtfClass)) != (RTFFuncPtr) NULL)
369                 (*p) (info);
370 }
371
372
373 /*
374  * Skip to the end of the current group.  When this returns,
375  * writers that maintain a state stack may want to call their
376  * state unstacker; global vars will still be set to the group's
377  * closing brace.
378  */
379
380 void RTFSkipGroup(RTF_Info *info)
381 {
382         int     level = 1;
383
384         TRACE("\n");
385
386         while (RTFGetToken (info) != rtfEOF)
387         {
388                 if (info->rtfClass == rtfGroup)
389                 {
390                         if (info->rtfMajor == rtfBeginGroup)
391                                 ++level;
392                         else if (info->rtfMajor == rtfEndGroup)
393                         {
394                                 if (--level < 1)
395                                         break;  /* end of initial group */
396                         }
397                 }
398         }
399 }
400
401
402 /*
403  * Read one token.  Call the read hook if there is one.  The
404  * token class is the return value.  Returns rtfEOF when there
405  * are no more tokens.
406  */
407
408 int RTFGetToken(RTF_Info *info)
409 {
410         RTFFuncPtr      p;
411
412         TRACE("\n");
413
414         for (;;)
415         {
416                 _RTFGetToken (info);
417                 if ((p = RTFGetReadHook (info)) != (RTFFuncPtr) NULL)
418                         (*p) (info);    /* give read hook a look at token */
419
420                 /* Silently discard newlines, carriage returns, nulls.  */
421                 if (!(info->rtfClass == rtfText && info->rtfFormat != SF_TEXT
422                         && (info->rtfMajor == '\r' || info->rtfMajor == '\n' || info->rtfMajor == '\0')))
423                         break;
424         }
425         return (info->rtfClass);
426 }
427
428
429 /*
430  * Install or return a token reader hook.
431  */
432
433 void RTFSetReadHook(RTF_Info *info, RTFFuncPtr f)
434 {
435         info->readHook = f;
436 }
437
438
439 RTFFuncPtr RTFGetReadHook(RTF_Info *info)
440 {
441         return (info->readHook);
442 }
443
444
445 void RTFUngetToken(RTF_Info *info)
446 {
447         TRACE("\n");
448
449         if (info->pushedClass >= 0)     /* there's already an ungotten token */
450                 RTFPanic (info,"cannot unget two tokens");
451         if (info->rtfClass < 0)
452                 RTFPanic (info,"no token to unget");
453         info->pushedClass = info->rtfClass;
454         info->pushedMajor = info->rtfMajor;
455         info->pushedMinor = info->rtfMinor;
456         info->pushedParam = info->rtfParam;
457         (void) strcpy (info->pushedTextBuf, info->rtfTextBuf);
458 }
459
460
461 int RTFPeekToken(RTF_Info *info)
462 {
463         _RTFGetToken (info);
464         RTFUngetToken (info);
465         return (info->rtfClass);
466 }
467
468
469 static void _RTFGetToken(RTF_Info *info)
470 {
471         TRACE("\n");
472
473         if (info->rtfFormat == SF_TEXT) {
474             info->rtfMajor = GetChar (info);
475             info->rtfMinor = 0;
476             info->rtfParam = rtfNoParam;
477             info->rtfTextBuf[info->rtfTextLen = 0] = '\0';
478             if (info->rtfMajor == EOF)
479                 info->rtfClass = rtfEOF;
480             else
481                 info->rtfClass = rtfText;
482             return;
483         }
484
485         /* first check for pushed token from RTFUngetToken() */
486
487         if (info->pushedClass >= 0)
488         {
489                 info->rtfClass = info->pushedClass;
490                 info->rtfMajor = info->pushedMajor;
491                 info->rtfMinor = info->pushedMinor;
492                 info->rtfParam = info->pushedParam;
493                 (void) strcpy (info->rtfTextBuf, info->pushedTextBuf);
494                 info->rtfTextLen = strlen (info->rtfTextBuf);
495                 info->pushedClass = -1;
496                 return;
497         }
498
499         /*
500          * Beyond this point, no token is ever seen twice, which is
501          * important, e.g., for making sure no "}" pops the font stack twice.
502          */
503
504         _RTFGetToken2 (info);
505 }
506
507
508 static int
509 RTFCharSetToCodePage(RTF_Info *info, int charset)
510 {
511         switch (charset)
512         {
513                 case ANSI_CHARSET:
514                 case DEFAULT_CHARSET:
515                         return info->ansiCodePage;
516                 case SYMBOL_CHARSET:
517                         return CP_SYMBOL;
518                 case MAC_CHARSET:
519                         return CP_MACCP;
520                 case SHIFTJIS_CHARSET:
521                         return 932;
522                 case HANGEUL_CHARSET:
523                         return 949;
524                 case JOHAB_CHARSET:
525                         return 1361;
526                 case GB2312_CHARSET:
527                         return 936;
528                 case CHINESEBIG5_CHARSET:
529                         return 950;
530                 case GREEK_CHARSET:
531                         return 1253;
532                 case TURKISH_CHARSET:
533                         return 1254;
534                 case VIETNAMESE_CHARSET:
535                         return 1258;
536                 case HEBREW_CHARSET:
537                         return 1255;
538                 case ARABIC_CHARSET:
539                         return 1256;
540                 case BALTIC_CHARSET:
541                         return 1257;
542                 case RUSSIAN_CHARSET:
543                         return 1251;
544                 case THAI_CHARSET:
545                         return 874;
546                 case EASTEUROPE_CHARSET:
547                         return 1250;
548                 case OEM_CHARSET:
549                         return CP_OEMCP;
550                 default:
551                 {
552                         CHARSETINFO csi;
553                         DWORD n = (DWORD)charset;
554                         
555                         /* FIXME: TranslateCharsetInfo does not work as good as it
556                          * should, so let's use it only when all else fails */
557                         if (!TranslateCharsetInfo(&n, &csi, TCI_SRCCHARSET))
558                                 RTFMsg(info,"%s: unknown charset %u\n", __FUNCTION__, charset);
559                         else
560                                 return csi.ciACP;
561                 }
562         }
563         return 0;
564 }
565
566
567 /* this shouldn't be called anywhere but from _RTFGetToken() */
568
569 static void _RTFGetToken2(RTF_Info *info)
570 {
571         int     sign;
572         int     c;
573
574         TRACE("\n");
575
576         /* initialize token vars */
577
578         info->rtfClass = rtfUnknown;
579         info->rtfParam = rtfNoParam;
580         info->rtfTextBuf[info->rtfTextLen = 0] = '\0';
581
582         /* get first character, which may be a pushback from previous token */
583
584         if (info->pushedChar != EOF)
585         {
586                 c = info->pushedChar;
587                 info->rtfTextBuf[info->rtfTextLen++] = c;
588                 info->rtfTextBuf[info->rtfTextLen] = '\0';
589                 info->pushedChar = EOF;
590         }
591         else if ((c = GetChar (info)) == EOF)
592         {
593                 info->rtfClass = rtfEOF;
594                 return;
595         }
596
597         if (c == '{')
598         {
599                 info->rtfClass = rtfGroup;
600                 info->rtfMajor = rtfBeginGroup;
601                 return;
602         }
603         if (c == '}')
604         {
605                 info->rtfClass = rtfGroup;
606                 info->rtfMajor = rtfEndGroup;
607                 return;
608         }
609         if (c != '\\')
610         {
611                 /*
612                  * Two possibilities here:
613                  * 1) ASCII 9, effectively like \tab control symbol
614                  * 2) literal text char
615                  */
616                 if (c == '\t')                  /* ASCII 9 */
617                 {
618                         info->rtfClass = rtfControl;
619                         info->rtfMajor = rtfSpecialChar;
620                         info->rtfMinor = rtfTab;
621                 }
622                 else
623                 {
624                         info->rtfClass = rtfText;
625                         info->rtfMajor = c;
626                 }
627                 return;
628         }
629         if ((c = GetChar (info)) == EOF)
630         {
631                 /* early eof, whoops (class is rtfUnknown) */
632                 return;
633         }
634         if (!isalpha (c))
635         {
636                 /*
637                  * Three possibilities here:
638                  * 1) hex encoded text char, e.g., \'d5, \'d3
639                  * 2) special escaped text char, e.g., \{, \}
640                  * 3) control symbol, e.g., \_, \-, \|, \<10>
641                  */
642                 if (c == '\'')                          /* hex char */
643                 {
644                 int     c2;
645
646                         if ((c = GetChar (info)) != EOF && (c2 = GetChar (info)) != EOF)
647                         {
648                                 /* should do isxdigit check! */
649                                 info->rtfClass = rtfText;
650                                 info->rtfMajor = RTFCharToHex (c) * 16 + RTFCharToHex (c2);
651                                 return;
652                         }
653                         /* early eof, whoops (class is rtfUnknown) */
654                         return;
655                 }
656
657                 /* escaped char */
658                 /*if (index (":{}\\", c) != (char *) NULL)*/ /* escaped char */
659                 if (c == ':' || c == '{' || c == '}' || c == '\\')
660                 {
661                         info->rtfClass = rtfText;
662                         info->rtfMajor = c;
663                         return;
664                 }
665
666                 /* control symbol */
667                 Lookup (info, info->rtfTextBuf);        /* sets class, major, minor */
668                 return;
669         }
670         /* control word */
671         while (isalpha (c))
672         {
673                 if ((c = GetChar (info)) == EOF)
674                         break;
675         }
676
677         /*
678          * At this point, the control word is all collected, so the
679          * major/minor numbers are determined before the parameter
680          * (if any) is scanned.  There will be one too many characters
681          * in the buffer, though, so fix up before and restore after
682          * looking up.
683          */
684
685         if (c != EOF)
686                 info->rtfTextBuf[info->rtfTextLen-1] = '\0';
687         Lookup (info, info->rtfTextBuf);        /* sets class, major, minor */
688         if (c != EOF)
689                 info->rtfTextBuf[info->rtfTextLen-1] = c;
690
691         /*
692          * Should be looking at first digit of parameter if there
693          * is one, unless it's negative.  In that case, next char
694          * is '-', so need to gobble next char, and remember sign.
695          */
696
697         sign = 1;
698         if (c == '-')
699         {
700                 sign = -1;
701                 c = GetChar (info);
702         }
703         if (c != EOF && isdigit (c))
704         {
705                 info->rtfParam = 0;
706                 while (isdigit (c))     /* gobble parameter */
707                 {
708                         info->rtfParam = info->rtfParam * 10 + c - '0';
709                         if ((c = GetChar (info)) == EOF)
710                                 break;
711                 }
712                 info->rtfParam *= sign;
713         }
714         /*
715          * If control symbol delimiter was a blank, gobble it.
716          * Otherwise the character is first char of next token, so
717          * push it back for next call.  In either case, delete the
718          * delimiter from the token buffer.
719          */
720         if (c != EOF)
721         {
722                 if (c != ' ')
723                         info->pushedChar = c;
724                 info->rtfTextBuf[--info->rtfTextLen] = '\0';
725         }
726 }
727
728
729 /*
730  * Read the next character from the input.  This handles setting the
731  * current line and position-within-line variables.  Those variable are
732  * set correctly whether lines end with CR, LF, or CRLF (the last being
733  * the tricky case).
734  *
735  * bumpLine indicates whether the line number should be incremented on
736  * the *next* input character.
737  */
738
739
740 static int GetChar(RTF_Info *info)
741 {
742         int     c;
743         int     oldBumpLine;
744
745         TRACE("\n");
746
747         if ((c = _RTFGetChar(info)) != EOF)
748         {
749                 info->rtfTextBuf[info->rtfTextLen++] = c;
750                 info->rtfTextBuf[info->rtfTextLen] = '\0';
751         }
752         if (info->prevChar == EOF)
753                 info->bumpLine = 1;
754         oldBumpLine = info->bumpLine;   /* non-zero if prev char was line ending */
755         info->bumpLine = 0;
756         if (c == '\r')
757                 info->bumpLine = 1;
758         else if (c == '\n')
759         {
760                 info->bumpLine = 1;
761                 if (info->prevChar == '\r')             /* oops, previous \r wasn't */
762                         oldBumpLine = 0;        /* really a line ending */
763         }
764         ++info->rtfLinePos;
765         if (oldBumpLine)        /* were we supposed to increment the */
766         {                       /* line count on this char? */
767                 ++info->rtfLineNum;
768                 info->rtfLinePos = 1;
769         }
770         info->prevChar = c;
771         return (c);
772 }
773
774
775 /*
776  * Synthesize a token by setting the global variables to the
777  * values supplied.  Typically this is followed with a call
778  * to RTFRouteToken().
779  *
780  * If a param value other than rtfNoParam is passed, it becomes
781  * part of the token text.
782  */
783
784 void RTFSetToken(RTF_Info *info, int class, int major, int minor, int param, const char *text)
785 {
786         TRACE("\n");
787
788         info->rtfClass = class;
789         info->rtfMajor = major;
790         info->rtfMinor = minor;
791         info->rtfParam = param;
792         if (param == rtfNoParam)
793                 (void) strcpy (info->rtfTextBuf, text);
794         else
795                 sprintf (info->rtfTextBuf, "%s%d", text, param);
796         info->rtfTextLen = strlen (info->rtfTextBuf);
797 }
798
799
800 /* ---------------------------------------------------------------------- */
801
802 /*
803  * Special destination readers.  They gobble the destination so the
804  * writer doesn't have to deal with them.  That's wrong for any
805  * translator that wants to process any of these itself.  In that
806  * case, these readers should be overridden by installing a different
807  * destination callback.
808  *
809  * NOTE: The last token read by each of these reader will be the
810  * destination's terminating '}', which will then be the current token.
811  * That '}' token is passed to RTFRouteToken() - the writer has already
812  * seen the '{' that began the destination group, and may have pushed a
813  * state; it also needs to know at the end of the group that a state
814  * should be popped.
815  *
816  * It's important that rtf.h and the control token lookup table list
817  * as many symbols as possible, because these destination readers
818  * unfortunately make strict assumptions about the input they expect,
819  * and a token of class rtfUnknown will throw them off easily.
820  */
821
822
823 /*
824  * Read { \fonttbl ... } destination.  Old font tables don't have
825  * braces around each table entry; try to adjust for that.
826  */
827
828 static void ReadFontTbl(RTF_Info *info)
829 {
830         RTFFont         *fp = NULL;
831         char            buf[rtfBufSiz], *bp;
832         int             old = -1;
833         const char      *fn = "ReadFontTbl";
834
835         TRACE("\n");
836
837         for (;;)
838         {
839                 (void) RTFGetToken (info);
840                 if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
841                         break;
842                 if (old < 0)            /* first entry - determine tbl type */
843                 {
844                         if (RTFCheckCMM (info, rtfControl, rtfCharAttr, rtfFontNum))
845                                 old = 1;        /* no brace */
846                         else if (RTFCheckCM (info, rtfGroup, rtfBeginGroup))
847                                 old = 0;        /* brace */
848                         else                    /* can't tell! */
849                                 RTFPanic (info, "%s: Cannot determine format", fn);
850                 }
851                 if (old == 0)           /* need to find "{" here */
852                 {
853                         if (!RTFCheckCM (info, rtfGroup, rtfBeginGroup))
854                                 RTFPanic (info, "%s: missing \"{\"", fn);
855                         (void) RTFGetToken (info);      /* yes, skip to next token */
856                 }
857                 if ((fp = New (RTFFont)) == (RTFFont *) NULL)
858                         RTFPanic (info, "%s: cannot allocate font entry", fn);
859
860                 fp->rtfNextFont = info->fontList;
861                 info->fontList = fp;
862
863                 fp->rtfFName = (char *) NULL;
864                 fp->rtfFAltName = (char *) NULL;
865                 fp->rtfFNum = -1;
866                 fp->rtfFFamily = 0;
867                 fp->rtfFCharSet = 0;
868                 fp->rtfFPitch = 0;
869                 fp->rtfFType = 0;
870                 fp->rtfFCodePage = 0;
871
872                 while (info->rtfClass != rtfEOF
873                        && !RTFCheckCM (info, rtfText, ';')
874                        && !RTFCheckCM (info, rtfGroup, rtfEndGroup))
875                 {
876                         if (info->rtfClass == rtfControl)
877                         {
878                                 switch (info->rtfMajor)
879                                 {
880                                 default:
881                                         /* ignore token but announce it */
882                                         RTFMsg (info,"%s: unknown token \"%s\"\n",
883                                                         fn, info->rtfTextBuf);
884                                         break;
885                                 case rtfFontFamily:
886                                         fp->rtfFFamily = info->rtfMinor;
887                                         break;
888                                 case rtfCharAttr:
889                                         switch (info->rtfMinor)
890                                         {
891                                         default:
892                                                 break;  /* ignore unknown? */
893                                         case rtfFontNum:
894                                                 fp->rtfFNum = info->rtfParam;
895                                                 break;
896                                         }
897                                         break;
898                                 case rtfFontAttr:
899                                         switch (info->rtfMinor)
900                                         {
901                                         default:
902                                                 break;  /* ignore unknown? */
903                                         case rtfFontCharSet:
904                                                 fp->rtfFCharSet = info->rtfParam;
905                                                 if (!fp->rtfFCodePage)
906                                                         fp->rtfFCodePage = RTFCharSetToCodePage(info, info->rtfParam);
907                                                 break;
908                                         case rtfFontPitch:
909                                                 fp->rtfFPitch = info->rtfParam;
910                                                 break;
911                                         case rtfFontCodePage:
912                                                 fp->rtfFCodePage = info->rtfParam;
913                                                 break;
914                                         case rtfFTypeNil:
915                                         case rtfFTypeTrueType:
916                                                 fp->rtfFType = info->rtfParam;
917                                                 break;
918                                         }
919                                         break;
920                                 }
921                         }
922                         else if (RTFCheckCM (info, rtfGroup, rtfBeginGroup))    /* dest */
923                         {
924                                 RTFSkipGroup (info);    /* ignore for now */
925                         }
926                         else if (info->rtfClass == rtfText)     /* font name */
927                         {
928                                 bp = buf;
929                                 while (info->rtfClass == rtfText
930                                         && !RTFCheckCM (info, rtfText, ';'))
931                                 {
932                                         *bp++ = info->rtfMajor;
933                                         (void) RTFGetToken (info);
934                                 }
935
936                                 /* FIX: in some cases the <fontinfo> isn't finished with a semi-column */
937                                 if(RTFCheckCM (info, rtfGroup, rtfEndGroup))
938                                 {
939                                   RTFUngetToken (info);
940                                 }
941                                 *bp = '\0';
942                                 fp->rtfFName = RTFStrSave (buf);
943                                 if (fp->rtfFName == (char *) NULL)
944                                         RTFPanic (info, "%s: cannot allocate font name", fn);
945                                 /* already have next token; don't read one */
946                                 /* at bottom of loop */
947                                 continue;
948                         }
949                         else
950                         {
951                                 /* ignore token but announce it */
952                                 RTFMsg (info, "%s: unknown token \"%s\"\n",
953                                                         fn,info->rtfTextBuf);
954                         }
955                         (void) RTFGetToken (info);
956                 }
957                 if (old == 0)   /* need to see "}" here */
958                 {
959                         (void) RTFGetToken (info);
960                         if (!RTFCheckCM (info, rtfGroup, rtfEndGroup))
961                                 RTFPanic (info, "%s: missing \"}\"", fn);
962                 }
963         }
964         if (fp->rtfFNum == -1)
965                 RTFPanic (info,"%s: missing font number", fn);
966 /*
967  * Could check other pieces of structure here, too, I suppose.
968  */
969         RTFRouteToken (info);   /* feed "}" back to router */
970 }
971
972
973 /*
974  * The color table entries have color values of -1 if
975  * the default color should be used for the entry (only
976  * a semi-colon is given in the definition, no color values).
977  * There will be a problem if a partial entry (1 or 2 but
978  * not 3 color values) is given.  The possibility is ignored
979  * here.
980  */
981
982 static void ReadColorTbl(RTF_Info *info)
983 {
984         RTFColor        *cp;
985         int             cnum = 0;
986         const char      *fn = "ReadColorTbl";
987
988         TRACE("\n");
989
990         for (;;)
991         {
992                 (void) RTFGetToken (info);
993                 if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
994                         break;
995                 if ((cp = New (RTFColor)) == (RTFColor *) NULL)
996                         RTFPanic (info,"%s: cannot allocate color entry", fn);
997                 cp->rtfCNum = cnum++;
998                 cp->rtfCRed = cp->rtfCGreen = cp->rtfCBlue = -1;
999                 cp->rtfNextColor = info->colorList;
1000                 info->colorList = cp;
1001                 while (RTFCheckCM (info, rtfControl, rtfColorName))
1002                 {
1003                         switch (info->rtfMinor)
1004                         {
1005                         case rtfRed:    cp->rtfCRed = info->rtfParam; break;
1006                         case rtfGreen:  cp->rtfCGreen = info->rtfParam; break;
1007                         case rtfBlue:   cp->rtfCBlue = info->rtfParam; break;
1008                         }
1009                         RTFGetToken (info);
1010                 }
1011                 if (!RTFCheckCM (info, rtfText, (int) ';'))
1012                         RTFPanic (info,"%s: malformed entry", fn);
1013         }
1014         RTFRouteToken (info);   /* feed "}" back to router */
1015 }
1016
1017
1018 /*
1019  * The "Normal" style definition doesn't contain any style number,
1020  * all others do.  Normal style is given style rtfNormalStyleNum.
1021  */
1022
1023 static void ReadStyleSheet(RTF_Info *info)
1024 {
1025         RTFStyle        *sp;
1026         RTFStyleElt     *sep, *sepLast;
1027         char            buf[rtfBufSiz], *bp;
1028         const char      *fn = "ReadStyleSheet";
1029
1030         TRACE("\n");
1031
1032         for (;;)
1033         {
1034                 (void) RTFGetToken (info);
1035                 if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
1036                         break;
1037                 if ((sp = New (RTFStyle)) == (RTFStyle *) NULL)
1038                         RTFPanic (info,"%s: cannot allocate stylesheet entry", fn);
1039                 sp->rtfSName = (char *) NULL;
1040                 sp->rtfSNum = -1;
1041                 sp->rtfSType = rtfParStyle;
1042                 sp->rtfSAdditive = 0;
1043                 sp->rtfSBasedOn = rtfNoStyleNum;
1044                 sp->rtfSNextPar = -1;
1045                 sp->rtfSSEList = sepLast = (RTFStyleElt *) NULL;
1046                 sp->rtfNextStyle = info->styleList;
1047                 sp->rtfExpanding = 0;
1048                 info->styleList = sp;
1049                 if (!RTFCheckCM (info, rtfGroup, rtfBeginGroup))
1050                         RTFPanic (info,"%s: missing \"{\"", fn);
1051                 for (;;)
1052                 {
1053                         (void) RTFGetToken (info);
1054                         if (info->rtfClass == rtfEOF
1055                                 || RTFCheckCM (info, rtfText, ';'))
1056                                 break;
1057                         if (info->rtfClass == rtfControl)
1058                         {
1059                                 if (RTFCheckMM (info, rtfSpecialChar, rtfOptDest))
1060                                         continue;       /* ignore "\*" */
1061                                 if (RTFCheckMM (info, rtfParAttr, rtfStyleNum))
1062                                 {
1063                                         sp->rtfSNum = info->rtfParam;
1064                                         sp->rtfSType = rtfParStyle;
1065                                         continue;
1066                                 }
1067                                 if (RTFCheckMM (info, rtfCharAttr, rtfCharStyleNum))
1068                                 {
1069                                         sp->rtfSNum = info->rtfParam;
1070                                         sp->rtfSType = rtfCharStyle;
1071                                         continue;
1072                                 }
1073                                 if (RTFCheckMM (info, rtfSectAttr, rtfSectStyleNum))
1074                                 {
1075                                         sp->rtfSNum = info->rtfParam;
1076                                         sp->rtfSType = rtfSectStyle;
1077                                         continue;
1078                                 }
1079                                 if (RTFCheckMM (info, rtfStyleAttr, rtfBasedOn))
1080                                 {
1081                                         sp->rtfSBasedOn = info->rtfParam;
1082                                         continue;
1083                                 }
1084                                 if (RTFCheckMM (info, rtfStyleAttr, rtfAdditive))
1085                                 {
1086                                         sp->rtfSAdditive = 1;
1087                                         continue;
1088                                 }
1089                                 if (RTFCheckMM (info, rtfStyleAttr, rtfNext))
1090                                 {
1091                                         sp->rtfSNextPar = info->rtfParam;
1092                                         continue;
1093                                 }
1094                                 if ((sep = New (RTFStyleElt)) == (RTFStyleElt *) NULL)
1095                                         RTFPanic (info,"%s: cannot allocate style element", fn);
1096                                 sep->rtfSEClass = info->rtfClass;
1097                                 sep->rtfSEMajor = info->rtfMajor;
1098                                 sep->rtfSEMinor = info->rtfMinor;
1099                                 sep->rtfSEParam = info->rtfParam;
1100                                 if ((sep->rtfSEText = RTFStrSave (info->rtfTextBuf))
1101                                                                 == (char *) NULL)
1102                                         RTFPanic (info,"%s: cannot allocate style element text", fn);
1103                                 if (sepLast == (RTFStyleElt *) NULL)
1104                                         sp->rtfSSEList = sep;   /* first element */
1105                                 else                            /* add to end */
1106                                         sepLast->rtfNextSE = sep;
1107                                 sep->rtfNextSE = (RTFStyleElt *) NULL;
1108                                 sepLast = sep;
1109                         }
1110                         else if (RTFCheckCM (info, rtfGroup, rtfBeginGroup))
1111                         {
1112                                 /*
1113                                  * This passes over "{\*\keycode ... }, among
1114                                  * other things. A temporary (perhaps) hack.
1115                                  */
1116                                 RTFSkipGroup (info);
1117                                 continue;
1118                         }
1119                         else if (info->rtfClass == rtfText)     /* style name */
1120                         {
1121                                 bp = buf;
1122                                 while (info->rtfClass == rtfText)
1123                                 {
1124                                         if (info->rtfMajor == ';')
1125                                         {
1126                                                 /* put back for "for" loop */
1127                                                 (void) RTFUngetToken (info);
1128                                                 break;
1129                                         }
1130                                         *bp++ = info->rtfMajor;
1131                                         (void) RTFGetToken (info);
1132                                 }
1133                                 *bp = '\0';
1134                                 if ((sp->rtfSName = RTFStrSave (buf)) == (char *) NULL)
1135                                         RTFPanic (info, "%s: cannot allocate style name", fn);
1136                         }
1137                         else            /* unrecognized */
1138                         {
1139                                 /* ignore token but announce it */
1140                                 RTFMsg (info, "%s: unknown token \"%s\"\n",
1141                                                         fn, info->rtfTextBuf);
1142                         }
1143                 }
1144                 (void) RTFGetToken (info);
1145                 if (!RTFCheckCM (info, rtfGroup, rtfEndGroup))
1146                         RTFPanic (info, "%s: missing \"}\"", fn);
1147
1148                 /*
1149                  * Check over the style structure.  A name is a must.
1150                  * If no style number was specified, check whether it's the
1151                  * Normal style (in which case it's given style number
1152                  * rtfNormalStyleNum).  Note that some "normal" style names
1153                  * just begin with "Normal" and can have other stuff following,
1154                  * e.g., "Normal,Times 10 point".  Ugh.
1155                  *
1156                  * Some German RTF writers use "Standard" instead of "Normal".
1157                  */
1158                 if (sp->rtfSName == (char *) NULL)
1159                         RTFPanic (info,"%s: missing style name", fn);
1160                 if (sp->rtfSNum < 0)
1161                 {
1162                         if (strncmp (buf, "Normal", 6) != 0
1163                                 && strncmp (buf, "Standard", 8) != 0)
1164                                 RTFPanic (info,"%s: missing style number", fn);
1165                         sp->rtfSNum = rtfNormalStyleNum;
1166                 }
1167                 if (sp->rtfSNextPar == -1)      /* if \snext not given, */
1168                         sp->rtfSNextPar = sp->rtfSNum;  /* next is itself */
1169         }
1170         RTFRouteToken (info);   /* feed "}" back to router */
1171 }
1172
1173
1174 static void ReadInfoGroup(RTF_Info *info)
1175 {
1176         RTFSkipGroup (info);
1177         RTFRouteToken (info);   /* feed "}" back to router */
1178 }
1179
1180
1181 static void ReadPictGroup(RTF_Info *info)
1182 {
1183         RTFSkipGroup (info);
1184         RTFRouteToken (info);   /* feed "}" back to router */
1185 }
1186
1187
1188 static void ReadObjGroup(RTF_Info *info)
1189 {
1190         RTFSkipGroup (info);
1191         RTFRouteToken (info);   /* feed "}" back to router */
1192 }
1193
1194
1195 /* ---------------------------------------------------------------------- */
1196
1197 /*
1198  * Routines to return pieces of stylesheet, or font or color tables.
1199  * References to style 0 are mapped onto the Normal style.
1200  */
1201
1202
1203 RTFStyle *RTFGetStyle(RTF_Info *info, int num)
1204 {
1205         RTFStyle        *s;
1206
1207         if (num == -1)
1208                 return (info->styleList);
1209         for (s = info->styleList; s != (RTFStyle *) NULL; s = s->rtfNextStyle)
1210         {
1211                 if (s->rtfSNum == num)
1212                         break;
1213         }
1214         return (s);             /* NULL if not found */
1215 }
1216
1217
1218 RTFFont *RTFGetFont(RTF_Info *info, int num)
1219 {
1220         RTFFont *f;
1221
1222         if (num == -1)
1223                 return (info->fontList);
1224         for (f = info->fontList; f != (RTFFont *) NULL; f = f->rtfNextFont)
1225         {
1226                 if (f->rtfFNum == num)
1227                         break;
1228         }
1229         return (f);             /* NULL if not found */
1230 }
1231
1232
1233 RTFColor *RTFGetColor(RTF_Info *info, int num)
1234 {
1235         RTFColor        *c;
1236
1237         if (num == -1)
1238                 return (info->colorList);
1239         for (c = info->colorList; c != (RTFColor *) NULL; c = c->rtfNextColor)
1240         {
1241                 if (c->rtfCNum == num)
1242                         break;
1243         }
1244         return (c);             /* NULL if not found */
1245 }
1246
1247
1248 /* ---------------------------------------------------------------------- */
1249
1250
1251 /*
1252  * Expand style n, if there is such a style.
1253  */
1254
1255 void RTFExpandStyle(RTF_Info *info, int n)
1256 {
1257         RTFStyle        *s;
1258         RTFStyleElt     *se;
1259
1260         TRACE("\n");
1261
1262         if (n == -1 || (s = RTFGetStyle (info, n)) == (RTFStyle *) NULL)
1263                 return;
1264         if (s->rtfExpanding != 0)
1265                 RTFPanic (info,"Style expansion loop, style %d", n);
1266         s->rtfExpanding = 1;    /* set expansion flag for loop detection */
1267         /*
1268          * Expand "based-on" style (unless it's the same as the current
1269          * style -- Normal style usually gives itself as its own based-on
1270          * style).  Based-on style expansion is done by synthesizing
1271          * the token that the writer needs to see in order to trigger
1272          * another style expansion, and feeding to token back through
1273          * the router so the writer sees it.
1274          */
1275         if (n != s->rtfSBasedOn)
1276         {
1277                 RTFSetToken (info, rtfControl, rtfParAttr, rtfStyleNum,
1278                                                         s->rtfSBasedOn, "\\s");
1279                 RTFRouteToken (info);
1280         }
1281         /*
1282          * Now route the tokens unique to this style.  RTFSetToken()
1283          * isn't used because it would add the param value to the end
1284          * of the token text, which already has it in.
1285          */
1286         for (se = s->rtfSSEList; se != (RTFStyleElt *) NULL; se = se->rtfNextSE)
1287         {
1288                 info->rtfClass = se->rtfSEClass;
1289                 info->rtfMajor = se->rtfSEMajor;
1290                 info->rtfMinor = se->rtfSEMinor;
1291                 info->rtfParam = se->rtfSEParam;
1292                 (void) strcpy (info->rtfTextBuf, se->rtfSEText);
1293                 info->rtfTextLen = strlen (info->rtfTextBuf);
1294                 RTFRouteToken (info);
1295         }
1296         s->rtfExpanding = 0;    /* done - clear expansion flag */
1297 }
1298
1299
1300 /* ---------------------------------------------------------------------- */
1301
1302 /*
1303  * Control symbol lookup routines
1304  */
1305
1306
1307 typedef struct RTFKey   RTFKey;
1308
1309 struct RTFKey
1310 {
1311         int        rtfKMajor;   /* major number */
1312         int        rtfKMinor;   /* minor number */
1313         const char *rtfKStr;    /* symbol name */
1314         int        rtfKHash;    /* symbol name hash value */
1315 };
1316
1317 /*
1318  * A minor number of -1 means the token has no minor number
1319  * (all valid minor numbers are >= 0).
1320  */
1321
1322 static RTFKey   rtfKey[] =
1323 {
1324         /*
1325          * Special characters
1326          */
1327
1328         { rtfSpecialChar,       rtfIIntVersion,         "vern",         0 },
1329         { rtfSpecialChar,       rtfICreateTime,         "creatim",      0 },
1330         { rtfSpecialChar,       rtfIRevisionTime,       "revtim",       0 },
1331         { rtfSpecialChar,       rtfIPrintTime,          "printim",      0 },
1332         { rtfSpecialChar,       rtfIBackupTime,         "buptim",       0 },
1333         { rtfSpecialChar,       rtfIEditTime,           "edmins",       0 },
1334         { rtfSpecialChar,       rtfIYear,               "yr",           0 },
1335         { rtfSpecialChar,       rtfIMonth,              "mo",           0 },
1336         { rtfSpecialChar,       rtfIDay,                "dy",           0 },
1337         { rtfSpecialChar,       rtfIHour,               "hr",           0 },
1338         { rtfSpecialChar,       rtfIMinute,             "min",          0 },
1339         { rtfSpecialChar,       rtfISecond,             "sec",          0 },
1340         { rtfSpecialChar,       rtfINPages,             "nofpages",     0 },
1341         { rtfSpecialChar,       rtfINWords,             "nofwords",     0 },
1342         { rtfSpecialChar,       rtfINChars,             "nofchars",     0 },
1343         { rtfSpecialChar,       rtfIIntID,              "id",           0 },
1344
1345         { rtfSpecialChar,       rtfCurHeadDate,         "chdate",       0 },
1346         { rtfSpecialChar,       rtfCurHeadDateLong,     "chdpl",        0 },
1347         { rtfSpecialChar,       rtfCurHeadDateAbbrev,   "chdpa",        0 },
1348         { rtfSpecialChar,       rtfCurHeadTime,         "chtime",       0 },
1349         { rtfSpecialChar,       rtfCurHeadPage,         "chpgn",        0 },
1350         { rtfSpecialChar,       rtfSectNum,             "sectnum",      0 },
1351         { rtfSpecialChar,       rtfCurFNote,            "chftn",        0 },
1352         { rtfSpecialChar,       rtfCurAnnotRef,         "chatn",        0 },
1353         { rtfSpecialChar,       rtfFNoteSep,            "chftnsep",     0 },
1354         { rtfSpecialChar,       rtfFNoteCont,           "chftnsepc",    0 },
1355         { rtfSpecialChar,       rtfCell,                "cell",         0 },
1356         { rtfSpecialChar,       rtfRow,                 "row",          0 },
1357         { rtfSpecialChar,       rtfPar,                 "par",          0 },
1358         /* newline and carriage return are synonyms for */
1359         /* \par when they are preceded by a \ character */
1360         { rtfSpecialChar,       rtfPar,                 "\n",           0 },
1361         { rtfSpecialChar,       rtfPar,                 "\r",           0 },
1362         { rtfSpecialChar,       rtfSect,                "sect",         0 },
1363         { rtfSpecialChar,       rtfPage,                "page",         0 },
1364         { rtfSpecialChar,       rtfColumn,              "column",       0 },
1365         { rtfSpecialChar,       rtfLine,                "line",         0 },
1366         { rtfSpecialChar,       rtfSoftPage,            "softpage",     0 },
1367         { rtfSpecialChar,       rtfSoftColumn,          "softcol",      0 },
1368         { rtfSpecialChar,       rtfSoftLine,            "softline",     0 },
1369         { rtfSpecialChar,       rtfSoftLineHt,          "softlheight",  0 },
1370         { rtfSpecialChar,       rtfTab,                 "tab",          0 },
1371         { rtfSpecialChar,       rtfEmDash,              "emdash",       0 },
1372         { rtfSpecialChar,       rtfEnDash,              "endash",       0 },
1373         { rtfSpecialChar,       rtfEmSpace,             "emspace",      0 },
1374         { rtfSpecialChar,       rtfEnSpace,             "enspace",      0 },
1375         { rtfSpecialChar,       rtfBullet,              "bullet",       0 },
1376         { rtfSpecialChar,       rtfLQuote,              "lquote",       0 },
1377         { rtfSpecialChar,       rtfRQuote,              "rquote",       0 },
1378         { rtfSpecialChar,       rtfLDblQuote,           "ldblquote",    0 },
1379         { rtfSpecialChar,       rtfRDblQuote,           "rdblquote",    0 },
1380         { rtfSpecialChar,       rtfFormula,             "|",            0 },
1381         { rtfSpecialChar,       rtfNoBrkSpace,          "~",            0 },
1382         { rtfSpecialChar,       rtfNoReqHyphen,         "-",            0 },
1383         { rtfSpecialChar,       rtfNoBrkHyphen,         "_",            0 },
1384         { rtfSpecialChar,       rtfOptDest,             "*",            0 },
1385         { rtfSpecialChar,       rtfLTRMark,             "ltrmark",      0 },
1386         { rtfSpecialChar,       rtfRTLMark,             "rtlmark",      0 },
1387         { rtfSpecialChar,       rtfNoWidthJoiner,       "zwj",          0 },
1388         { rtfSpecialChar,       rtfNoWidthNonJoiner,    "zwnj",         0 },
1389         /* is this valid? */
1390         { rtfSpecialChar,       rtfCurHeadPict,         "chpict",       0 },
1391         { rtfSpecialChar,       rtfUnicode,             "u",            0 },
1392
1393         /*
1394          * Character formatting attributes
1395          */
1396
1397         { rtfCharAttr,  rtfPlain,               "plain",        0 },
1398         { rtfCharAttr,  rtfBold,                "b",            0 },
1399         { rtfCharAttr,  rtfAllCaps,             "caps",         0 },
1400         { rtfCharAttr,  rtfDeleted,             "deleted",      0 },
1401         { rtfCharAttr,  rtfSubScript,           "dn",           0 },
1402         { rtfCharAttr,  rtfSubScrShrink,        "sub",          0 },
1403         { rtfCharAttr,  rtfNoSuperSub,          "nosupersub",   0 },
1404         { rtfCharAttr,  rtfExpand,              "expnd",        0 },
1405         { rtfCharAttr,  rtfExpandTwips,         "expndtw",      0 },
1406         { rtfCharAttr,  rtfKerning,             "kerning",      0 },
1407         { rtfCharAttr,  rtfFontNum,             "f",            0 },
1408         { rtfCharAttr,  rtfFontSize,            "fs",           0 },
1409         { rtfCharAttr,  rtfItalic,              "i",            0 },
1410         { rtfCharAttr,  rtfOutline,             "outl",         0 },
1411         { rtfCharAttr,  rtfRevised,             "revised",      0 },
1412         { rtfCharAttr,  rtfRevAuthor,           "revauth",      0 },
1413         { rtfCharAttr,  rtfRevDTTM,             "revdttm",      0 },
1414         { rtfCharAttr,  rtfSmallCaps,           "scaps",        0 },
1415         { rtfCharAttr,  rtfShadow,              "shad",         0 },
1416         { rtfCharAttr,  rtfStrikeThru,          "strike",       0 },
1417         { rtfCharAttr,  rtfUnderline,           "ul",           0 },
1418         { rtfCharAttr,  rtfDotUnderline,        "uld",          0 },
1419         { rtfCharAttr,  rtfDbUnderline,         "uldb",         0 },
1420         { rtfCharAttr,  rtfNoUnderline,         "ulnone",       0 },
1421         { rtfCharAttr,  rtfWordUnderline,       "ulw",          0 },
1422         { rtfCharAttr,  rtfSuperScript,         "up",           0 },
1423         { rtfCharAttr,  rtfSuperScrShrink,      "super",        0 },
1424         { rtfCharAttr,  rtfInvisible,           "v",            0 },
1425         { rtfCharAttr,  rtfForeColor,           "cf",           0 },
1426         { rtfCharAttr,  rtfBackColor,           "cb",           0 },
1427         { rtfCharAttr,  rtfRTLChar,             "rtlch",        0 },
1428         { rtfCharAttr,  rtfLTRChar,             "ltrch",        0 },
1429         { rtfCharAttr,  rtfCharStyleNum,        "cs",           0 },
1430         { rtfCharAttr,  rtfCharCharSet,         "cchs",         0 },
1431         { rtfCharAttr,  rtfLanguage,            "lang",         0 },
1432         /* this has disappeared from spec 1.2 */
1433         { rtfCharAttr,  rtfGray,                "gray",         0 },
1434         { rtfCharAttr,  rtfUnicodeLength,       "uc",           0 },
1435
1436         /*
1437          * Paragraph formatting attributes
1438          */
1439
1440         { rtfParAttr,   rtfParDef,              "pard",         0 },
1441         { rtfParAttr,   rtfStyleNum,            "s",            0 },
1442         { rtfParAttr,   rtfHyphenate,           "hyphpar",      0 },
1443         { rtfParAttr,   rtfInTable,             "intbl",        0 },
1444         { rtfParAttr,   rtfKeep,                "keep",         0 },
1445         { rtfParAttr,   rtfNoWidowControl,      "nowidctlpar",  0 },
1446         { rtfParAttr,   rtfKeepNext,            "keepn",        0 },
1447         { rtfParAttr,   rtfOutlineLevel,        "level",        0 },
1448         { rtfParAttr,   rtfNoLineNum,           "noline",       0 },
1449         { rtfParAttr,   rtfPBBefore,            "pagebb",       0 },
1450         { rtfParAttr,   rtfSideBySide,          "sbys",         0 },
1451         { rtfParAttr,   rtfQuadLeft,            "ql",           0 },
1452         { rtfParAttr,   rtfQuadRight,           "qr",           0 },
1453         { rtfParAttr,   rtfQuadJust,            "qj",           0 },
1454         { rtfParAttr,   rtfQuadCenter,          "qc",           0 },
1455         { rtfParAttr,   rtfFirstIndent,         "fi",           0 },
1456         { rtfParAttr,   rtfLeftIndent,          "li",           0 },
1457         { rtfParAttr,   rtfRightIndent,         "ri",           0 },
1458         { rtfParAttr,   rtfSpaceBefore,         "sb",           0 },
1459         { rtfParAttr,   rtfSpaceAfter,          "sa",           0 },
1460         { rtfParAttr,   rtfSpaceBetween,        "sl",           0 },
1461         { rtfParAttr,   rtfSpaceMultiply,       "slmult",       0 },
1462
1463         { rtfParAttr,   rtfSubDocument,         "subdocument",  0 },
1464
1465         { rtfParAttr,   rtfRTLPar,              "rtlpar",       0 },
1466         { rtfParAttr,   rtfLTRPar,              "ltrpar",       0 },
1467
1468         { rtfParAttr,   rtfTabPos,              "tx",           0 },
1469         /*
1470          * FrameMaker writes \tql (to mean left-justified tab, apparently)
1471          * although it's not in the spec.  It's also redundant, since lj
1472          * tabs are the default.
1473          */
1474         { rtfParAttr,   rtfTabLeft,             "tql",          0 },
1475         { rtfParAttr,   rtfTabRight,            "tqr",          0 },
1476         { rtfParAttr,   rtfTabCenter,           "tqc",          0 },
1477         { rtfParAttr,   rtfTabDecimal,          "tqdec",        0 },
1478         { rtfParAttr,   rtfTabBar,              "tb",           0 },
1479         { rtfParAttr,   rtfLeaderDot,           "tldot",        0 },
1480         { rtfParAttr,   rtfLeaderHyphen,        "tlhyph",       0 },
1481         { rtfParAttr,   rtfLeaderUnder,         "tlul",         0 },
1482         { rtfParAttr,   rtfLeaderThick,         "tlth",         0 },
1483         { rtfParAttr,   rtfLeaderEqual,         "tleq",         0 },
1484
1485         { rtfParAttr,   rtfParLevel,            "pnlvl",        0 },
1486         { rtfParAttr,   rtfParBullet,           "pnlvlblt",     0 },
1487         { rtfParAttr,   rtfParSimple,           "pnlvlbody",    0 },
1488         { rtfParAttr,   rtfParNumCont,          "pnlvlcont",    0 },
1489         { rtfParAttr,   rtfParNumOnce,          "pnnumonce",    0 },
1490         { rtfParAttr,   rtfParNumAcross,        "pnacross",     0 },
1491         { rtfParAttr,   rtfParHangIndent,       "pnhang",       0 },
1492         { rtfParAttr,   rtfParNumRestart,       "pnrestart",    0 },
1493         { rtfParAttr,   rtfParNumCardinal,      "pncard",       0 },
1494         { rtfParAttr,   rtfParNumDecimal,       "pndec",        0 },
1495         { rtfParAttr,   rtfParNumULetter,       "pnucltr",      0 },
1496         { rtfParAttr,   rtfParNumURoman,        "pnucrm",       0 },
1497         { rtfParAttr,   rtfParNumLLetter,       "pnlcltr",      0 },
1498         { rtfParAttr,   rtfParNumLRoman,        "pnlcrm",       0 },
1499         { rtfParAttr,   rtfParNumOrdinal,       "pnord",        0 },
1500         { rtfParAttr,   rtfParNumOrdinalText,   "pnordt",       0 },
1501         { rtfParAttr,   rtfParNumBold,          "pnb",          0 },
1502         { rtfParAttr,   rtfParNumItalic,        "pni",          0 },
1503         { rtfParAttr,   rtfParNumAllCaps,       "pncaps",       0 },
1504         { rtfParAttr,   rtfParNumSmallCaps,     "pnscaps",      0 },
1505         { rtfParAttr,   rtfParNumUnder,         "pnul",         0 },
1506         { rtfParAttr,   rtfParNumDotUnder,      "pnuld",        0 },
1507         { rtfParAttr,   rtfParNumDbUnder,       "pnuldb",       0 },
1508         { rtfParAttr,   rtfParNumNoUnder,       "pnulnone",     0 },
1509         { rtfParAttr,   rtfParNumWordUnder,     "pnulw",        0 },
1510         { rtfParAttr,   rtfParNumStrikethru,    "pnstrike",     0 },
1511         { rtfParAttr,   rtfParNumForeColor,     "pncf",         0 },
1512         { rtfParAttr,   rtfParNumFont,          "pnf",          0 },
1513         { rtfParAttr,   rtfParNumFontSize,      "pnfs",         0 },
1514         { rtfParAttr,   rtfParNumIndent,        "pnindent",     0 },
1515         { rtfParAttr,   rtfParNumSpacing,       "pnsp",         0 },
1516         { rtfParAttr,   rtfParNumInclPrev,      "pnprev",       0 },
1517         { rtfParAttr,   rtfParNumCenter,        "pnqc",         0 },
1518         { rtfParAttr,   rtfParNumLeft,          "pnql",         0 },
1519         { rtfParAttr,   rtfParNumRight,         "pnqr",         0 },
1520         { rtfParAttr,   rtfParNumStartAt,       "pnstart",      0 },
1521
1522         { rtfParAttr,   rtfBorderTop,           "brdrt",        0 },
1523         { rtfParAttr,   rtfBorderBottom,        "brdrb",        0 },
1524         { rtfParAttr,   rtfBorderLeft,          "brdrl",        0 },
1525         { rtfParAttr,   rtfBorderRight,         "brdrr",        0 },
1526         { rtfParAttr,   rtfBorderBetween,       "brdrbtw",      0 },
1527         { rtfParAttr,   rtfBorderBar,           "brdrbar",      0 },
1528         { rtfParAttr,   rtfBorderBox,           "box",          0 },
1529         { rtfParAttr,   rtfBorderSingle,        "brdrs",        0 },
1530         { rtfParAttr,   rtfBorderThick,         "brdrth",       0 },
1531         { rtfParAttr,   rtfBorderShadow,        "brdrsh",       0 },
1532         { rtfParAttr,   rtfBorderDouble,        "brdrdb",       0 },
1533         { rtfParAttr,   rtfBorderDot,           "brdrdot",      0 },
1534         { rtfParAttr,   rtfBorderDot,           "brdrdash",     0 },
1535         { rtfParAttr,   rtfBorderHair,          "brdrhair",     0 },
1536         { rtfParAttr,   rtfBorderWidth,         "brdrw",        0 },
1537         { rtfParAttr,   rtfBorderColor,         "brdrcf",       0 },
1538         { rtfParAttr,   rtfBorderSpace,         "brsp",         0 },
1539
1540         { rtfParAttr,   rtfShading,             "shading",      0 },
1541         { rtfParAttr,   rtfBgPatH,              "bghoriz",      0 },
1542         { rtfParAttr,   rtfBgPatV,              "bgvert",       0 },
1543         { rtfParAttr,   rtfFwdDiagBgPat,        "bgfdiag",      0 },
1544         { rtfParAttr,   rtfBwdDiagBgPat,        "bgbdiag",      0 },
1545         { rtfParAttr,   rtfHatchBgPat,          "bgcross",      0 },
1546         { rtfParAttr,   rtfDiagHatchBgPat,      "bgdcross",     0 },
1547         { rtfParAttr,   rtfDarkBgPatH,          "bgdkhoriz",    0 },
1548         { rtfParAttr,   rtfDarkBgPatV,          "bgdkvert",     0 },
1549         { rtfParAttr,   rtfFwdDarkBgPat,        "bgdkfdiag",    0 },
1550         { rtfParAttr,   rtfBwdDarkBgPat,        "bgdkbdiag",    0 },
1551         { rtfParAttr,   rtfDarkHatchBgPat,      "bgdkcross",    0 },
1552         { rtfParAttr,   rtfDarkDiagHatchBgPat,  "bgdkdcross",   0 },
1553         { rtfParAttr,   rtfBgPatLineColor,      "cfpat",        0 },
1554         { rtfParAttr,   rtfBgPatColor,          "cbpat",        0 },
1555
1556         /*
1557          * Section formatting attributes
1558          */
1559
1560         { rtfSectAttr,  rtfSectDef,             "sectd",        0 },
1561         { rtfSectAttr,  rtfENoteHere,           "endnhere",     0 },
1562         { rtfSectAttr,  rtfPrtBinFirst,         "binfsxn",      0 },
1563         { rtfSectAttr,  rtfPrtBin,              "binsxn",       0 },
1564         { rtfSectAttr,  rtfSectStyleNum,        "ds",           0 },
1565
1566         { rtfSectAttr,  rtfNoBreak,             "sbknone",      0 },
1567         { rtfSectAttr,  rtfColBreak,            "sbkcol",       0 },
1568         { rtfSectAttr,  rtfPageBreak,           "sbkpage",      0 },
1569         { rtfSectAttr,  rtfEvenBreak,           "sbkeven",      0 },
1570         { rtfSectAttr,  rtfOddBreak,            "sbkodd",       0 },
1571
1572         { rtfSectAttr,  rtfColumns,             "cols",         0 },
1573         { rtfSectAttr,  rtfColumnSpace,         "colsx",        0 },
1574         { rtfSectAttr,  rtfColumnNumber,        "colno",        0 },
1575         { rtfSectAttr,  rtfColumnSpRight,       "colsr",        0 },
1576         { rtfSectAttr,  rtfColumnWidth,         "colw",         0 },
1577         { rtfSectAttr,  rtfColumnLine,          "linebetcol",   0 },
1578
1579         { rtfSectAttr,  rtfLineModulus,         "linemod",      0 },
1580         { rtfSectAttr,  rtfLineDist,            "linex",        0 },
1581         { rtfSectAttr,  rtfLineStarts,          "linestarts",   0 },
1582         { rtfSectAttr,  rtfLineRestart,         "linerestart",  0 },
1583         { rtfSectAttr,  rtfLineRestartPg,       "lineppage",    0 },
1584         { rtfSectAttr,  rtfLineCont,            "linecont",     0 },
1585
1586         { rtfSectAttr,  rtfSectPageWid,         "pgwsxn",       0 },
1587         { rtfSectAttr,  rtfSectPageHt,          "pghsxn",       0 },
1588         { rtfSectAttr,  rtfSectMarginLeft,      "marglsxn",     0 },
1589         { rtfSectAttr,  rtfSectMarginRight,     "margrsxn",     0 },
1590         { rtfSectAttr,  rtfSectMarginTop,       "margtsxn",     0 },
1591         { rtfSectAttr,  rtfSectMarginBottom,    "margbsxn",     0 },
1592         { rtfSectAttr,  rtfSectMarginGutter,    "guttersxn",    0 },
1593         { rtfSectAttr,  rtfSectLandscape,       "lndscpsxn",    0 },
1594         { rtfSectAttr,  rtfTitleSpecial,        "titlepg",      0 },
1595         { rtfSectAttr,  rtfHeaderY,             "headery",      0 },
1596         { rtfSectAttr,  rtfFooterY,             "footery",      0 },
1597
1598         { rtfSectAttr,  rtfPageStarts,          "pgnstarts",    0 },
1599         { rtfSectAttr,  rtfPageCont,            "pgncont",      0 },
1600         { rtfSectAttr,  rtfPageRestart,         "pgnrestart",   0 },
1601         { rtfSectAttr,  rtfPageNumRight,        "pgnx",         0 },
1602         { rtfSectAttr,  rtfPageNumTop,          "pgny",         0 },
1603         { rtfSectAttr,  rtfPageDecimal,         "pgndec",       0 },
1604         { rtfSectAttr,  rtfPageURoman,          "pgnucrm",      0 },
1605         { rtfSectAttr,  rtfPageLRoman,          "pgnlcrm",      0 },
1606         { rtfSectAttr,  rtfPageULetter,         "pgnucltr",     0 },
1607         { rtfSectAttr,  rtfPageLLetter,         "pgnlcltr",     0 },
1608         { rtfSectAttr,  rtfPageNumHyphSep,      "pgnhnsh",      0 },
1609         { rtfSectAttr,  rtfPageNumSpaceSep,     "pgnhnsp",      0 },
1610         { rtfSectAttr,  rtfPageNumColonSep,     "pgnhnsc",      0 },
1611         { rtfSectAttr,  rtfPageNumEmdashSep,    "pgnhnsm",      0 },
1612         { rtfSectAttr,  rtfPageNumEndashSep,    "pgnhnsn",      0 },
1613
1614         { rtfSectAttr,  rtfTopVAlign,           "vertalt",      0 },
1615         /* misspelled as "vertal" in specification 1.0 */
1616         { rtfSectAttr,  rtfBottomVAlign,        "vertalb",      0 },
1617         { rtfSectAttr,  rtfCenterVAlign,        "vertalc",      0 },
1618         { rtfSectAttr,  rtfJustVAlign,          "vertalj",      0 },
1619
1620         { rtfSectAttr,  rtfRTLSect,             "rtlsect",      0 },
1621         { rtfSectAttr,  rtfLTRSect,             "ltrsect",      0 },
1622
1623         /* I've seen these in an old spec, but not in real files... */
1624         /*rtfSectAttr,  rtfNoBreak,             "nobreak",      0,*/
1625         /*rtfSectAttr,  rtfColBreak,            "colbreak",     0,*/
1626         /*rtfSectAttr,  rtfPageBreak,           "pagebreak",    0,*/
1627         /*rtfSectAttr,  rtfEvenBreak,           "evenbreak",    0,*/
1628         /*rtfSectAttr,  rtfOddBreak,            "oddbreak",     0,*/
1629
1630         /*
1631          * Document formatting attributes
1632          */
1633
1634         { rtfDocAttr,   rtfDefTab,              "deftab",       0 },
1635         { rtfDocAttr,   rtfHyphHotZone,         "hyphhotz",     0 },
1636         { rtfDocAttr,   rtfHyphConsecLines,     "hyphconsec",   0 },
1637         { rtfDocAttr,   rtfHyphCaps,            "hyphcaps",     0 },
1638         { rtfDocAttr,   rtfHyphAuto,            "hyphauto",     0 },
1639         { rtfDocAttr,   rtfLineStart,           "linestart",    0 },
1640         { rtfDocAttr,   rtfFracWidth,           "fracwidth",    0 },
1641         /* \makeback was given in old version of spec, it's now */
1642         /* listed as \makebackup */
1643         { rtfDocAttr,   rtfMakeBackup,          "makeback",     0 },
1644         { rtfDocAttr,   rtfMakeBackup,          "makebackup",   0 },
1645         { rtfDocAttr,   rtfRTFDefault,          "defformat",    0 },
1646         { rtfDocAttr,   rtfPSOverlay,           "psover",       0 },
1647         { rtfDocAttr,   rtfDocTemplate,         "doctemp",      0 },
1648         { rtfDocAttr,   rtfDefLanguage,         "deflang",      0 },
1649
1650         { rtfDocAttr,   rtfFENoteType,          "fet",          0 },
1651         { rtfDocAttr,   rtfFNoteEndSect,        "endnotes",     0 },
1652         { rtfDocAttr,   rtfFNoteEndDoc,         "enddoc",       0 },
1653         { rtfDocAttr,   rtfFNoteText,           "ftntj",        0 },
1654         { rtfDocAttr,   rtfFNoteBottom,         "ftnbj",        0 },
1655         { rtfDocAttr,   rtfENoteEndSect,        "aendnotes",    0 },
1656         { rtfDocAttr,   rtfENoteEndDoc,         "aenddoc",      0 },
1657         { rtfDocAttr,   rtfENoteText,           "aftntj",       0 },
1658         { rtfDocAttr,   rtfENoteBottom,         "aftnbj",       0 },
1659         { rtfDocAttr,   rtfFNoteStart,          "ftnstart",     0 },
1660         { rtfDocAttr,   rtfENoteStart,          "aftnstart",    0 },
1661         { rtfDocAttr,   rtfFNoteRestartPage,    "ftnrstpg",     0 },
1662         { rtfDocAttr,   rtfFNoteRestart,        "ftnrestart",   0 },
1663         { rtfDocAttr,   rtfFNoteRestartCont,    "ftnrstcont",   0 },
1664         { rtfDocAttr,   rtfENoteRestart,        "aftnrestart",  0 },
1665         { rtfDocAttr,   rtfENoteRestartCont,    "aftnrstcont",  0 },
1666         { rtfDocAttr,   rtfFNoteNumArabic,      "ftnnar",       0 },
1667         { rtfDocAttr,   rtfFNoteNumLLetter,     "ftnnalc",      0 },
1668         { rtfDocAttr,   rtfFNoteNumULetter,     "ftnnauc",      0 },
1669         { rtfDocAttr,   rtfFNoteNumLRoman,      "ftnnrlc",      0 },
1670         { rtfDocAttr,   rtfFNoteNumURoman,      "ftnnruc",      0 },
1671         { rtfDocAttr,   rtfFNoteNumChicago,     "ftnnchi",      0 },
1672         { rtfDocAttr,   rtfENoteNumArabic,      "aftnnar",      0 },
1673         { rtfDocAttr,   rtfENoteNumLLetter,     "aftnnalc",     0 },
1674         { rtfDocAttr,   rtfENoteNumULetter,     "aftnnauc",     0 },
1675         { rtfDocAttr,   rtfENoteNumLRoman,      "aftnnrlc",     0 },
1676         { rtfDocAttr,   rtfENoteNumURoman,      "aftnnruc",     0 },
1677         { rtfDocAttr,   rtfENoteNumChicago,     "aftnnchi",     0 },
1678
1679         { rtfDocAttr,   rtfPaperWidth,          "paperw",       0 },
1680         { rtfDocAttr,   rtfPaperHeight,         "paperh",       0 },
1681         { rtfDocAttr,   rtfPaperSize,           "psz",          0 },
1682         { rtfDocAttr,   rtfLeftMargin,          "margl",        0 },
1683         { rtfDocAttr,   rtfRightMargin,         "margr",        0 },
1684         { rtfDocAttr,   rtfTopMargin,           "margt",        0 },
1685         { rtfDocAttr,   rtfBottomMargin,        "margb",        0 },
1686         { rtfDocAttr,   rtfFacingPage,          "facingp",      0 },
1687         { rtfDocAttr,   rtfGutterWid,           "gutter",       0 },
1688         { rtfDocAttr,   rtfMirrorMargin,        "margmirror",   0 },
1689         { rtfDocAttr,   rtfLandscape,           "landscape",    0 },
1690         { rtfDocAttr,   rtfPageStart,           "pgnstart",     0 },
1691         { rtfDocAttr,   rtfWidowCtrl,           "widowctrl",    0 },
1692
1693         { rtfDocAttr,   rtfLinkStyles,          "linkstyles",   0 },
1694
1695         { rtfDocAttr,   rtfNoAutoTabIndent,     "notabind",     0 },
1696         { rtfDocAttr,   rtfWrapSpaces,          "wraptrsp",     0 },
1697         { rtfDocAttr,   rtfPrintColorsBlack,    "prcolbl",      0 },
1698         { rtfDocAttr,   rtfNoExtraSpaceRL,      "noextrasprl",  0 },
1699         { rtfDocAttr,   rtfNoColumnBalance,     "nocolbal",     0 },
1700         { rtfDocAttr,   rtfCvtMailMergeQuote,   "cvmme",        0 },
1701         { rtfDocAttr,   rtfSuppressTopSpace,    "sprstsp",      0 },
1702         { rtfDocAttr,   rtfSuppressPreParSpace, "sprsspbf",     0 },
1703         { rtfDocAttr,   rtfCombineTblBorders,   "otblrul",      0 },
1704         { rtfDocAttr,   rtfTranspMetafiles,     "transmf",      0 },
1705         { rtfDocAttr,   rtfSwapBorders,         "swpbdr",       0 },
1706         { rtfDocAttr,   rtfShowHardBreaks,      "brkfrm",       0 },
1707
1708         { rtfDocAttr,   rtfFormProtected,       "formprot",     0 },
1709         { rtfDocAttr,   rtfAllProtected,        "allprot",      0 },
1710         { rtfDocAttr,   rtfFormShading,         "formshade",    0 },
1711         { rtfDocAttr,   rtfFormDisplay,         "formdisp",     0 },
1712         { rtfDocAttr,   rtfPrintData,           "printdata",    0 },
1713
1714         { rtfDocAttr,   rtfRevProtected,        "revprot",      0 },
1715         { rtfDocAttr,   rtfRevisions,           "revisions",    0 },
1716         { rtfDocAttr,   rtfRevDisplay,          "revprop",      0 },
1717         { rtfDocAttr,   rtfRevBar,              "revbar",       0 },
1718
1719         { rtfDocAttr,   rtfAnnotProtected,      "annotprot",    0 },
1720
1721         { rtfDocAttr,   rtfRTLDoc,              "rtldoc",       0 },
1722         { rtfDocAttr,   rtfLTRDoc,              "ltrdoc",       0 },
1723        
1724         { rtfDocAttr,   rtfAnsiCodePage,        "ansicpg",      0 },
1725         { rtfDocAttr,   rtfUTF8RTF,             "urtf",         0 },
1726
1727         /*
1728          * Style attributes
1729          */
1730
1731         { rtfStyleAttr, rtfAdditive,            "additive",     0 },
1732         { rtfStyleAttr, rtfBasedOn,             "sbasedon",     0 },
1733         { rtfStyleAttr, rtfNext,                "snext",        0 },
1734
1735         /*
1736          * Picture attributes
1737          */
1738
1739         { rtfPictAttr,  rtfMacQD,               "macpict",      0 },
1740         { rtfPictAttr,  rtfPMMetafile,          "pmmetafile",   0 },
1741         { rtfPictAttr,  rtfWinMetafile,         "wmetafile",    0 },
1742         { rtfPictAttr,  rtfDevIndBitmap,        "dibitmap",     0 },
1743         { rtfPictAttr,  rtfWinBitmap,           "wbitmap",      0 },
1744         { rtfPictAttr,  rtfPixelBits,           "wbmbitspixel", 0 },
1745         { rtfPictAttr,  rtfBitmapPlanes,        "wbmplanes",    0 },
1746         { rtfPictAttr,  rtfBitmapWid,           "wbmwidthbytes", 0 },
1747
1748         { rtfPictAttr,  rtfPicWid,              "picw",         0 },
1749         { rtfPictAttr,  rtfPicHt,               "pich",         0 },
1750         { rtfPictAttr,  rtfPicGoalWid,          "picwgoal",     0 },
1751         { rtfPictAttr,  rtfPicGoalHt,           "pichgoal",     0 },
1752         /* these two aren't in the spec, but some writers emit them */
1753         { rtfPictAttr,  rtfPicGoalWid,          "picwGoal",     0 },
1754         { rtfPictAttr,  rtfPicGoalHt,           "pichGoal",     0 },
1755         { rtfPictAttr,  rtfPicScaleX,           "picscalex",    0 },
1756         { rtfPictAttr,  rtfPicScaleY,           "picscaley",    0 },
1757         { rtfPictAttr,  rtfPicScaled,           "picscaled",    0 },
1758         { rtfPictAttr,  rtfPicCropTop,          "piccropt",     0 },
1759         { rtfPictAttr,  rtfPicCropBottom,       "piccropb",     0 },
1760         { rtfPictAttr,  rtfPicCropLeft,         "piccropl",     0 },
1761         { rtfPictAttr,  rtfPicCropRight,        "piccropr",     0 },
1762
1763         { rtfPictAttr,  rtfPicMFHasBitmap,      "picbmp",       0 },
1764         { rtfPictAttr,  rtfPicMFBitsPerPixel,   "picbpp",       0 },
1765
1766         { rtfPictAttr,  rtfPicBinary,           "bin",          0 },
1767
1768         /*
1769          * NeXT graphic attributes
1770          */
1771
1772         { rtfNeXTGrAttr,        rtfNeXTGWidth,          "width",        0 },
1773         { rtfNeXTGrAttr,        rtfNeXTGHeight,         "height",       0 },
1774
1775         /*
1776          * Destinations
1777          */
1778
1779         { rtfDestination,       rtfFontTbl,             "fonttbl",      0 },
1780         { rtfDestination,       rtfFontAltName,         "falt",         0 },
1781         { rtfDestination,       rtfEmbeddedFont,        "fonteb",       0 },
1782         { rtfDestination,       rtfFontFile,            "fontfile",     0 },
1783         { rtfDestination,       rtfFileTbl,             "filetbl",      0 },
1784         { rtfDestination,       rtfFileInfo,            "file",         0 },
1785         { rtfDestination,       rtfColorTbl,            "colortbl",     0 },
1786         { rtfDestination,       rtfStyleSheet,          "stylesheet",   0 },
1787         { rtfDestination,       rtfKeyCode,             "keycode",      0 },
1788         { rtfDestination,       rtfRevisionTbl,         "revtbl",       0 },
1789         { rtfDestination,       rtfGenerator,           "generator",    0 },
1790         { rtfDestination,       rtfInfo,                "info",         0 },
1791         { rtfDestination,       rtfITitle,              "title",        0 },
1792         { rtfDestination,       rtfISubject,            "subject",      0 },
1793         { rtfDestination,       rtfIAuthor,             "author",       0 },
1794         { rtfDestination,       rtfIOperator,           "operator",     0 },
1795         { rtfDestination,       rtfIKeywords,           "keywords",     0 },
1796         { rtfDestination,       rtfIComment,            "comment",      0 },
1797         { rtfDestination,       rtfIVersion,            "version",      0 },
1798         { rtfDestination,       rtfIDoccomm,            "doccomm",      0 },
1799         /* \verscomm may not exist -- was seen in earlier spec version */
1800         { rtfDestination,       rtfIVerscomm,           "verscomm",     0 },
1801         { rtfDestination,       rtfNextFile,            "nextfile",     0 },
1802         { rtfDestination,       rtfTemplate,            "template",     0 },
1803         { rtfDestination,       rtfFNSep,               "ftnsep",       0 },
1804         { rtfDestination,       rtfFNContSep,           "ftnsepc",      0 },
1805         { rtfDestination,       rtfFNContNotice,        "ftncn",        0 },
1806         { rtfDestination,       rtfENSep,               "aftnsep",      0 },
1807         { rtfDestination,       rtfENContSep,           "aftnsepc",     0 },
1808         { rtfDestination,       rtfENContNotice,        "aftncn",       0 },
1809         { rtfDestination,       rtfPageNumLevel,        "pgnhn",        0 },
1810         { rtfDestination,       rtfParNumLevelStyle,    "pnseclvl",     0 },
1811         { rtfDestination,       rtfHeader,              "header",       0 },
1812         { rtfDestination,       rtfFooter,              "footer",       0 },
1813         { rtfDestination,       rtfHeaderLeft,          "headerl",      0 },
1814         { rtfDestination,       rtfHeaderRight,         "headerr",      0 },
1815         { rtfDestination,       rtfHeaderFirst,         "headerf",      0 },
1816         { rtfDestination,       rtfFooterLeft,          "footerl",      0 },
1817         { rtfDestination,       rtfFooterRight,         "footerr",      0 },
1818         { rtfDestination,       rtfFooterFirst,         "footerf",      0 },
1819         { rtfDestination,       rtfParNumText,          "pntext",       0 },
1820         { rtfDestination,       rtfParNumbering,        "pn",           0 },
1821         { rtfDestination,       rtfParNumTextAfter,     "pntexta",      0 },
1822         { rtfDestination,       rtfParNumTextBefore,    "pntextb",      0 },
1823         { rtfDestination,       rtfBookmarkStart,       "bkmkstart",    0 },
1824         { rtfDestination,       rtfBookmarkEnd,         "bkmkend",      0 },
1825         { rtfDestination,       rtfPict,                "pict",         0 },
1826         { rtfDestination,       rtfObject,              "object",       0 },
1827         { rtfDestination,       rtfObjClass,            "objclass",     0 },
1828         { rtfDestination,       rtfObjName,             "objname",      0 },
1829         { rtfObjAttr,   rtfObjTime,             "objtime",      0 },
1830         { rtfDestination,       rtfObjData,             "objdata",      0 },
1831         { rtfDestination,       rtfObjAlias,            "objalias",     0 },
1832         { rtfDestination,       rtfObjSection,          "objsect",      0 },
1833         /* objitem and objtopic aren't documented in the spec! */
1834         { rtfDestination,       rtfObjItem,             "objitem",      0 },
1835         { rtfDestination,       rtfObjTopic,            "objtopic",     0 },
1836         { rtfDestination,       rtfObjResult,           "result",       0 },
1837         { rtfDestination,       rtfDrawObject,          "do",           0 },
1838         { rtfDestination,       rtfFootnote,            "footnote",     0 },
1839         { rtfDestination,       rtfAnnotRefStart,       "atrfstart",    0 },
1840         { rtfDestination,       rtfAnnotRefEnd,         "atrfend",      0 },
1841         { rtfDestination,       rtfAnnotID,             "atnid",        0 },
1842         { rtfDestination,       rtfAnnotAuthor,         "atnauthor",    0 },
1843         { rtfDestination,       rtfAnnotation,          "annotation",   0 },
1844         { rtfDestination,       rtfAnnotRef,            "atnref",       0 },
1845         { rtfDestination,       rtfAnnotTime,           "atntime",      0 },
1846         { rtfDestination,       rtfAnnotIcon,           "atnicn",       0 },
1847         { rtfDestination,       rtfField,               "field",        0 },
1848         { rtfDestination,       rtfFieldInst,           "fldinst",      0 },
1849         { rtfDestination,       rtfFieldResult,         "fldrslt",      0 },
1850         { rtfDestination,       rtfDataField,           "datafield",    0 },
1851         { rtfDestination,       rtfIndex,               "xe",           0 },
1852         { rtfDestination,       rtfIndexText,           "txe",          0 },
1853         { rtfDestination,       rtfIndexRange,          "rxe",          0 },
1854         { rtfDestination,       rtfTOC,                 "tc",           0 },
1855         { rtfDestination,       rtfNeXTGraphic,         "NeXTGraphic",  0 },
1856
1857         /*
1858          * Font families
1859          */
1860
1861         { rtfFontFamily,        rtfFFNil,               "fnil",         0 },
1862         { rtfFontFamily,        rtfFFRoman,             "froman",       0 },
1863         { rtfFontFamily,        rtfFFSwiss,             "fswiss",       0 },
1864         { rtfFontFamily,        rtfFFModern,            "fmodern",      0 },
1865         { rtfFontFamily,        rtfFFScript,            "fscript",      0 },
1866         { rtfFontFamily,        rtfFFDecor,             "fdecor",       0 },
1867         { rtfFontFamily,        rtfFFTech,              "ftech",        0 },
1868         { rtfFontFamily,        rtfFFBidirectional,     "fbidi",        0 },
1869
1870         /*
1871          * Font attributes
1872          */
1873
1874         { rtfFontAttr,  rtfFontCharSet,         "fcharset",     0 },
1875         { rtfFontAttr,  rtfFontPitch,           "fprq",         0 },
1876         { rtfFontAttr,  rtfFontCodePage,        "cpg",          0 },
1877         { rtfFontAttr,  rtfFTypeNil,            "ftnil",        0 },
1878         { rtfFontAttr,  rtfFTypeTrueType,       "fttruetype",   0 },
1879
1880         /*
1881          * File table attributes
1882          */
1883
1884         { rtfFileAttr,  rtfFileNum,             "fid",          0 },
1885         { rtfFileAttr,  rtfFileRelPath,         "frelative",    0 },
1886         { rtfFileAttr,  rtfFileOSNum,           "fosnum",       0 },
1887
1888         /*
1889          * File sources
1890          */
1891
1892         { rtfFileSource,        rtfSrcMacintosh,        "fvalidmac",    0 },
1893         { rtfFileSource,        rtfSrcDOS,              "fvaliddos",    0 },
1894         { rtfFileSource,        rtfSrcNTFS,             "fvalidntfs",   0 },
1895         { rtfFileSource,        rtfSrcHPFS,             "fvalidhpfs",   0 },
1896         { rtfFileSource,        rtfSrcNetwork,          "fnetwork",     0 },
1897
1898         /*
1899          * Color names
1900          */
1901
1902         { rtfColorName, rtfRed,                 "red",          0 },
1903         { rtfColorName, rtfGreen,               "green",        0 },
1904         { rtfColorName, rtfBlue,                "blue",         0 },
1905
1906         /*
1907          * Charset names
1908          */
1909
1910         { rtfCharSet,   rtfMacCharSet,          "mac",          0 },
1911         { rtfCharSet,   rtfAnsiCharSet,         "ansi",         0 },
1912         { rtfCharSet,   rtfPcCharSet,           "pc",           0 },
1913         { rtfCharSet,   rtfPcaCharSet,          "pca",          0 },
1914
1915         /*
1916          * Table attributes
1917          */
1918
1919         { rtfTblAttr,   rtfRowDef,              "trowd",        0 },
1920         { rtfTblAttr,   rtfRowGapH,             "trgaph",       0 },
1921         { rtfTblAttr,   rtfCellPos,             "cellx",        0 },
1922         { rtfTblAttr,   rtfMergeRngFirst,       "clmgf",        0 },
1923         { rtfTblAttr,   rtfMergePrevious,       "clmrg",        0 },
1924
1925         { rtfTblAttr,   rtfRowLeft,             "trql",         0 },
1926         { rtfTblAttr,   rtfRowRight,            "trqr",         0 },
1927         { rtfTblAttr,   rtfRowCenter,           "trqc",         0 },
1928         { rtfTblAttr,   rtfRowLeftEdge,         "trleft",       0 },
1929         { rtfTblAttr,   rtfRowHt,               "trrh",         0 },
1930         { rtfTblAttr,   rtfRowHeader,           "trhdr",        0 },
1931         { rtfTblAttr,   rtfRowKeep,             "trkeep",       0 },
1932
1933         { rtfTblAttr,   rtfRTLRow,              "rtlrow",       0 },
1934         { rtfTblAttr,   rtfLTRRow,              "ltrrow",       0 },
1935
1936         { rtfTblAttr,   rtfRowBordTop,          "trbrdrt",      0 },
1937         { rtfTblAttr,   rtfRowBordLeft,         "trbrdrl",      0 },
1938         { rtfTblAttr,   rtfRowBordBottom,       "trbrdrb",      0 },
1939         { rtfTblAttr,   rtfRowBordRight,        "trbrdrr",      0 },
1940         { rtfTblAttr,   rtfRowBordHoriz,        "trbrdrh",      0 },
1941         { rtfTblAttr,   rtfRowBordVert,         "trbrdrv",      0 },
1942
1943         { rtfTblAttr,   rtfCellBordBottom,      "clbrdrb",      0 },
1944         { rtfTblAttr,   rtfCellBordTop,         "clbrdrt",      0 },
1945         { rtfTblAttr,   rtfCellBordLeft,        "clbrdrl",      0 },
1946         { rtfTblAttr,   rtfCellBordRight,       "clbrdrr",      0 },
1947
1948         { rtfTblAttr,   rtfCellShading,         "clshdng",      0 },
1949         { rtfTblAttr,   rtfCellBgPatH,          "clbghoriz",    0 },
1950         { rtfTblAttr,   rtfCellBgPatV,          "clbgvert",     0 },
1951         { rtfTblAttr,   rtfCellFwdDiagBgPat,    "clbgfdiag",    0 },
1952         { rtfTblAttr,   rtfCellBwdDiagBgPat,    "clbgbdiag",    0 },
1953         { rtfTblAttr,   rtfCellHatchBgPat,      "clbgcross",    0 },
1954         { rtfTblAttr,   rtfCellDiagHatchBgPat,  "clbgdcross",   0 },
1955         /*
1956          * The spec lists "clbgdkhor", but the corresponding non-cell
1957          * control is "bgdkhoriz".  At any rate Macintosh Word seems
1958          * to accept both "clbgdkhor" and "clbgdkhoriz".
1959          */
1960         { rtfTblAttr,   rtfCellDarkBgPatH,      "clbgdkhoriz",  0 },
1961         { rtfTblAttr,   rtfCellDarkBgPatH,      "clbgdkhor",    0 },
1962         { rtfTblAttr,   rtfCellDarkBgPatV,      "clbgdkvert",   0 },
1963         { rtfTblAttr,   rtfCellFwdDarkBgPat,    "clbgdkfdiag",  0 },
1964         { rtfTblAttr,   rtfCellBwdDarkBgPat,    "clbgdkbdiag",  0 },
1965         { rtfTblAttr,   rtfCellDarkHatchBgPat,  "clbgdkcross",  0 },
1966         { rtfTblAttr,   rtfCellDarkDiagHatchBgPat, "clbgdkdcross",      0 },
1967         { rtfTblAttr,   rtfCellBgPatLineColor, "clcfpat",       0 },
1968         { rtfTblAttr,   rtfCellBgPatColor,      "clcbpat",      0 },
1969
1970         /*
1971          * Field attributes
1972          */
1973
1974         { rtfFieldAttr, rtfFieldDirty,          "flddirty",     0 },
1975         { rtfFieldAttr, rtfFieldEdited,         "fldedit",      0 },
1976         { rtfFieldAttr, rtfFieldLocked,         "fldlock",      0 },
1977         { rtfFieldAttr, rtfFieldPrivate,        "fldpriv",      0 },
1978         { rtfFieldAttr, rtfFieldAlt,            "fldalt",       0 },
1979
1980         /*
1981          * Positioning attributes
1982          */
1983
1984         { rtfPosAttr,   rtfAbsWid,              "absw",         0 },
1985         { rtfPosAttr,   rtfAbsHt,               "absh",         0 },
1986
1987         { rtfPosAttr,   rtfRPosMargH,           "phmrg",        0 },
1988         { rtfPosAttr,   rtfRPosPageH,           "phpg",         0 },
1989         { rtfPosAttr,   rtfRPosColH,            "phcol",        0 },
1990         { rtfPosAttr,   rtfPosX,                "posx",         0 },
1991         { rtfPosAttr,   rtfPosNegX,             "posnegx",      0 },
1992         { rtfPosAttr,   rtfPosXCenter,          "posxc",        0 },
1993         { rtfPosAttr,   rtfPosXInside,          "posxi",        0 },
1994         { rtfPosAttr,   rtfPosXOutSide,         "posxo",        0 },
1995         { rtfPosAttr,   rtfPosXRight,           "posxr",        0 },
1996         { rtfPosAttr,   rtfPosXLeft,            "posxl",        0 },
1997
1998         { rtfPosAttr,   rtfRPosMargV,           "pvmrg",        0 },
1999         { rtfPosAttr,   rtfRPosPageV,           "pvpg",         0 },
2000         { rtfPosAttr,   rtfRPosParaV,           "pvpara",       0 },
2001         { rtfPosAttr,   rtfPosY,                "posy",         0 },
2002         { rtfPosAttr,   rtfPosNegY,             "posnegy",      0 },
2003         { rtfPosAttr,   rtfPosYInline,          "posyil",       0 },
2004         { rtfPosAttr,   rtfPosYTop,             "posyt",        0 },
2005         { rtfPosAttr,   rtfPosYCenter,          "posyc",        0 },
2006         { rtfPosAttr,   rtfPosYBottom,          "posyb",        0 },
2007
2008         { rtfPosAttr,   rtfNoWrap,              "nowrap",       0 },
2009         { rtfPosAttr,   rtfDistFromTextAll,     "dxfrtext",     0 },
2010         { rtfPosAttr,   rtfDistFromTextX,       "dfrmtxtx",     0 },
2011         { rtfPosAttr,   rtfDistFromTextY,       "dfrmtxty",     0 },
2012         /* \dyfrtext no longer exists in spec 1.2, apparently */
2013         /* replaced by \dfrmtextx and \dfrmtexty. */
2014         { rtfPosAttr,   rtfTextDistY,           "dyfrtext",     0 },
2015
2016         { rtfPosAttr,   rtfDropCapLines,        "dropcapli",    0 },
2017         { rtfPosAttr,   rtfDropCapType,         "dropcapt",     0 },
2018
2019         /*
2020          * Object controls
2021          */
2022
2023         { rtfObjAttr,   rtfObjEmb,              "objemb",       0 },
2024         { rtfObjAttr,   rtfObjLink,             "objlink",      0 },
2025         { rtfObjAttr,   rtfObjAutoLink,         "objautlink",   0 },
2026         { rtfObjAttr,   rtfObjSubscriber,       "objsub",       0 },
2027         { rtfObjAttr,   rtfObjPublisher,        "objpub",       0 },
2028         { rtfObjAttr,   rtfObjICEmb,            "objicemb",     0 },
2029
2030         { rtfObjAttr,   rtfObjLinkSelf,         "linkself",     0 },
2031         { rtfObjAttr,   rtfObjLock,             "objupdate",    0 },
2032         { rtfObjAttr,   rtfObjUpdate,           "objlock",      0 },
2033
2034         { rtfObjAttr,   rtfObjHt,               "objh",         0 },
2035         { rtfObjAttr,   rtfObjWid,              "objw",         0 },
2036         { rtfObjAttr,   rtfObjSetSize,          "objsetsize",   0 },
2037         { rtfObjAttr,   rtfObjAlign,            "objalign",     0 },
2038         { rtfObjAttr,   rtfObjTransposeY,       "objtransy",    0 },
2039         { rtfObjAttr,   rtfObjCropTop,          "objcropt",     0 },
2040         { rtfObjAttr,   rtfObjCropBottom,       "objcropb",     0 },
2041         { rtfObjAttr,   rtfObjCropLeft,         "objcropl",     0 },
2042         { rtfObjAttr,   rtfObjCropRight,        "objcropr",     0 },
2043         { rtfObjAttr,   rtfObjScaleX,           "objscalex",    0 },
2044         { rtfObjAttr,   rtfObjScaleY,           "objscaley",    0 },
2045
2046         { rtfObjAttr,   rtfObjResRTF,           "rsltrtf",      0 },
2047         { rtfObjAttr,   rtfObjResPict,          "rsltpict",     0 },
2048         { rtfObjAttr,   rtfObjResBitmap,        "rsltbmp",      0 },
2049         { rtfObjAttr,   rtfObjResText,          "rslttxt",      0 },
2050         { rtfObjAttr,   rtfObjResMerge,         "rsltmerge",    0 },
2051
2052         { rtfObjAttr,   rtfObjBookmarkPubObj,   "bkmkpub",      0 },
2053         { rtfObjAttr,   rtfObjPubAutoUpdate,    "pubauto",      0 },
2054
2055         /*
2056          * Associated character formatting attributes
2057          */
2058
2059         { rtfACharAttr, rtfACBold,              "ab",           0 },
2060         { rtfACharAttr, rtfACAllCaps,           "caps",         0 },
2061         { rtfACharAttr, rtfACForeColor,         "acf",          0 },
2062         { rtfACharAttr, rtfACSubScript,         "adn",          0 },
2063         { rtfACharAttr, rtfACExpand,            "aexpnd",       0 },
2064         { rtfACharAttr, rtfACFontNum,           "af",           0 },
2065         { rtfACharAttr, rtfACFontSize,          "afs",          0 },
2066         { rtfACharAttr, rtfACItalic,            "ai",           0 },
2067         { rtfACharAttr, rtfACLanguage,          "alang",        0 },
2068         { rtfACharAttr, rtfACOutline,           "aoutl",        0 },
2069         { rtfACharAttr, rtfACSmallCaps,         "ascaps",       0 },
2070         { rtfACharAttr, rtfACShadow,            "ashad",        0 },
2071         { rtfACharAttr, rtfACStrikeThru,        "astrike",      0 },
2072         { rtfACharAttr, rtfACUnderline,         "aul",          0 },
2073         { rtfACharAttr, rtfACDotUnderline,      "auld",         0 },
2074         { rtfACharAttr, rtfACDbUnderline,       "auldb",        0 },
2075         { rtfACharAttr, rtfACNoUnderline,       "aulnone",      0 },
2076         { rtfACharAttr, rtfACWordUnderline,     "aulw",         0 },
2077         { rtfACharAttr, rtfACSuperScript,       "aup",          0 },
2078
2079         /*
2080          * Footnote attributes
2081          */
2082
2083         { rtfFNoteAttr, rtfFNAlt,               "ftnalt",       0 },
2084
2085         /*
2086          * Key code attributes
2087          */
2088
2089         { rtfKeyCodeAttr,       rtfAltKey,              "alt",          0 },
2090         { rtfKeyCodeAttr,       rtfShiftKey,            "shift",        0 },
2091         { rtfKeyCodeAttr,       rtfControlKey,          "ctrl",         0 },
2092         { rtfKeyCodeAttr,       rtfFunctionKey,         "fn",           0 },
2093
2094         /*
2095          * Bookmark attributes
2096          */
2097
2098         { rtfBookmarkAttr, rtfBookmarkFirstCol, "bkmkcolf",     0 },
2099         { rtfBookmarkAttr, rtfBookmarkLastCol,  "bkmkcoll",     0 },
2100
2101         /*
2102          * Index entry attributes
2103          */
2104
2105         { rtfIndexAttr, rtfIndexNumber,         "xef",          0 },
2106         { rtfIndexAttr, rtfIndexBold,           "bxe",          0 },
2107         { rtfIndexAttr, rtfIndexItalic,         "ixe",          0 },
2108
2109         /*
2110          * Table of contents attributes
2111          */
2112
2113         { rtfTOCAttr,   rtfTOCType,             "tcf",          0 },
2114         { rtfTOCAttr,   rtfTOCLevel,            "tcl",          0 },
2115
2116         /*
2117          * Drawing object attributes
2118          */
2119
2120         { rtfDrawAttr,  rtfDrawLock,            "dolock",       0 },
2121         { rtfDrawAttr,  rtfDrawPageRelX,        "doxpage",      0 },
2122         { rtfDrawAttr,  rtfDrawColumnRelX,      "dobxcolumn",   0 },
2123         { rtfDrawAttr,  rtfDrawMarginRelX,      "dobxmargin",   0 },
2124         { rtfDrawAttr,  rtfDrawPageRelY,        "dobypage",     0 },
2125         { rtfDrawAttr,  rtfDrawColumnRelY,      "dobycolumn",   0 },
2126         { rtfDrawAttr,  rtfDrawMarginRelY,      "dobymargin",   0 },
2127         { rtfDrawAttr,  rtfDrawHeight,          "dobhgt",       0 },
2128
2129         { rtfDrawAttr,  rtfDrawBeginGroup,      "dpgroup",      0 },
2130         { rtfDrawAttr,  rtfDrawGroupCount,      "dpcount",      0 },
2131         { rtfDrawAttr,  rtfDrawEndGroup,        "dpendgroup",   0 },
2132         { rtfDrawAttr,  rtfDrawArc,             "dparc",        0 },
2133         { rtfDrawAttr,  rtfDrawCallout,         "dpcallout",    0 },
2134         { rtfDrawAttr,  rtfDrawEllipse,         "dpellipse",    0 },
2135         { rtfDrawAttr,  rtfDrawLine,            "dpline",       0 },
2136         { rtfDrawAttr,  rtfDrawPolygon,         "dppolygon",    0 },
2137         { rtfDrawAttr,  rtfDrawPolyLine,        "dppolyline",   0 },
2138         { rtfDrawAttr,  rtfDrawRect,            "dprect",       0 },
2139         { rtfDrawAttr,  rtfDrawTextBox,         "dptxbx",       0 },
2140
2141         { rtfDrawAttr,  rtfDrawOffsetX,         "dpx",          0 },
2142         { rtfDrawAttr,  rtfDrawSizeX,           "dpxsize",      0 },
2143         { rtfDrawAttr,  rtfDrawOffsetY,         "dpy",          0 },
2144         { rtfDrawAttr,  rtfDrawSizeY,           "dpysize",      0 },
2145
2146         { rtfDrawAttr,  rtfCOAngle,             "dpcoa",        0 },
2147         { rtfDrawAttr,  rtfCOAccentBar,         "dpcoaccent",   0 },
2148         { rtfDrawAttr,  rtfCOBestFit,           "dpcobestfit",  0 },
2149         { rtfDrawAttr,  rtfCOBorder,            "dpcoborder",   0 },
2150         { rtfDrawAttr,  rtfCOAttachAbsDist,     "dpcodabs",     0 },
2151         { rtfDrawAttr,  rtfCOAttachBottom,      "dpcodbottom",  0 },
2152         { rtfDrawAttr,  rtfCOAttachCenter,      "dpcodcenter",  0 },
2153         { rtfDrawAttr,  rtfCOAttachTop,         "dpcodtop",     0 },
2154         { rtfDrawAttr,  rtfCOLength,            "dpcolength",   0 },
2155         { rtfDrawAttr,  rtfCONegXQuadrant,      "dpcominusx",   0 },
2156         { rtfDrawAttr,  rtfCONegYQuadrant,      "dpcominusy",   0 },
2157         { rtfDrawAttr,  rtfCOOffset,            "dpcooffset",   0 },
2158         { rtfDrawAttr,  rtfCOAttachSmart,       "dpcosmarta",   0 },
2159         { rtfDrawAttr,  rtfCODoubleLine,        "dpcotdouble",  0 },
2160         { rtfDrawAttr,  rtfCORightAngle,        "dpcotright",   0 },
2161         { rtfDrawAttr,  rtfCOSingleLine,        "dpcotsingle",  0 },
2162         { rtfDrawAttr,  rtfCOTripleLine,        "dpcottriple",  0 },
2163
2164         { rtfDrawAttr,  rtfDrawTextBoxMargin,   "dptxbxmar",    0 },
2165         { rtfDrawAttr,  rtfDrawTextBoxText,     "dptxbxtext",   0 },
2166         { rtfDrawAttr,  rtfDrawRoundRect,       "dproundr",     0 },
2167
2168         { rtfDrawAttr,  rtfDrawPointX,          "dpptx",        0 },
2169         { rtfDrawAttr,  rtfDrawPointY,          "dppty",        0 },
2170         { rtfDrawAttr,  rtfDrawPolyCount,       "dppolycount",  0 },
2171
2172         { rtfDrawAttr,  rtfDrawArcFlipX,        "dparcflipx",   0 },
2173         { rtfDrawAttr,  rtfDrawArcFlipY,        "dparcflipy",   0 },
2174
2175         { rtfDrawAttr,  rtfDrawLineBlue,        "dplinecob",    0 },
2176         { rtfDrawAttr,  rtfDrawLineGreen,       "dplinecog",    0 },
2177         { rtfDrawAttr,  rtfDrawLineRed,         "dplinecor",    0 },
2178         { rtfDrawAttr,  rtfDrawLinePalette,     "dplinepal",    0 },
2179         { rtfDrawAttr,  rtfDrawLineDashDot,     "dplinedado",   0 },
2180         { rtfDrawAttr,  rtfDrawLineDashDotDot,  "dplinedadodo", 0 },
2181         { rtfDrawAttr,  rtfDrawLineDash,        "dplinedash",   0 },
2182         { rtfDrawAttr,  rtfDrawLineDot,         "dplinedot",    0 },
2183         { rtfDrawAttr,  rtfDrawLineGray,        "dplinegray",   0 },
2184         { rtfDrawAttr,  rtfDrawLineHollow,      "dplinehollow", 0 },
2185         { rtfDrawAttr,  rtfDrawLineSolid,       "dplinesolid",  0 },
2186         { rtfDrawAttr,  rtfDrawLineWidth,       "dplinew",      0 },
2187
2188         { rtfDrawAttr,  rtfDrawHollowEndArrow,  "dpaendhol",    0 },
2189         { rtfDrawAttr,  rtfDrawEndArrowLength,  "dpaendl",      0 },
2190         { rtfDrawAttr,  rtfDrawSolidEndArrow,   "dpaendsol",    0 },
2191         { rtfDrawAttr,  rtfDrawEndArrowWidth,   "dpaendw",      0 },
2192         { rtfDrawAttr,  rtfDrawHollowStartArrow,"dpastarthol",  0 },
2193         { rtfDrawAttr,  rtfDrawStartArrowLength,"dpastartl",    0 },
2194         { rtfDrawAttr,  rtfDrawSolidStartArrow, "dpastartsol",  0 },
2195         { rtfDrawAttr,  rtfDrawStartArrowWidth, "dpastartw",    0 },
2196
2197         { rtfDrawAttr,  rtfDrawBgFillBlue,      "dpfillbgcb",   0 },
2198         { rtfDrawAttr,  rtfDrawBgFillGreen,     "dpfillbgcg",   0 },
2199         { rtfDrawAttr,  rtfDrawBgFillRed,       "dpfillbgcr",   0 },
2200         { rtfDrawAttr,  rtfDrawBgFillPalette,   "dpfillbgpal",  0 },
2201         { rtfDrawAttr,  rtfDrawBgFillGray,      "dpfillbggray", 0 },
2202         { rtfDrawAttr,  rtfDrawFgFillBlue,      "dpfillfgcb",   0 },
2203         { rtfDrawAttr,  rtfDrawFgFillGreen,     "dpfillfgcg",   0 },
2204         { rtfDrawAttr,  rtfDrawFgFillRed,       "dpfillfgcr",   0 },
2205         { rtfDrawAttr,  rtfDrawFgFillPalette,   "dpfillfgpal",  0 },
2206         { rtfDrawAttr,  rtfDrawFgFillGray,      "dpfillfggray", 0 },
2207         { rtfDrawAttr,  rtfDrawFillPatIndex,    "dpfillpat",    0 },
2208
2209         { rtfDrawAttr,  rtfDrawShadow,          "dpshadow",     0 },
2210         { rtfDrawAttr,  rtfDrawShadowXOffset,   "dpshadx",      0 },
2211         { rtfDrawAttr,  rtfDrawShadowYOffset,   "dpshady",      0 },
2212
2213         { rtfVersion,   -1,                     "rtf",          0 },
2214         { rtfDefFont,   -1,                     "deff",         0 },
2215
2216         { 0,            -1,                     (char *) NULL,  0 }
2217 };
2218 #define RTF_KEY_COUNT (sizeof(rtfKey) / sizeof(RTFKey))
2219
2220 typedef struct tagRTFHashTableEntry {
2221   int count;
2222   RTFKey **value;
2223 } RTFHashTableEntry;
2224
2225 static RTFHashTableEntry rtfHashTable[RTF_KEY_COUNT * 2];
2226
2227
2228 /*
2229  * Initialize lookup table hash values.  Only need to do this once.
2230  */
2231
2232 static void LookupInit(void)
2233 {
2234         static int      inited = 0;
2235         RTFKey  *rp;
2236
2237         if (inited == 0)
2238         {
2239                 memset(rtfHashTable, 0, RTF_KEY_COUNT * 2 * sizeof(*rtfHashTable));
2240                 for (rp = rtfKey; rp->rtfKStr != (char *) NULL; rp++) {
2241                         int index;
2242                         
2243                         rp->rtfKHash = Hash ((char*)rp->rtfKStr);
2244                         index = rp->rtfKHash % (RTF_KEY_COUNT * 2);
2245                         if (!rtfHashTable[index].count)
2246                                 rtfHashTable[index].value = (void *)RTFAlloc(sizeof(RTFKey *));
2247                         else
2248                                 rtfHashTable[index].value = (void *)RTFReAlloc((void *)rtfHashTable[index].value, sizeof(RTFKey *) * (rtfHashTable[index].count + 1));
2249                         rtfHashTable[index].value[rtfHashTable[index].count++] = rp;
2250                 }
2251                 ++inited;
2252         }
2253 }
2254
2255
2256 /*
2257  * Determine major and minor number of control token.  If it's
2258  * not found, the class turns into rtfUnknown.
2259  */
2260
2261 static void Lookup(RTF_Info *info, char *s)
2262 {
2263         RTFKey  *rp;
2264         int     hash;
2265         RTFHashTableEntry *entry;
2266         int i;
2267
2268         TRACE("\n");
2269         ++s;                    /* skip over the leading \ character */
2270         hash = Hash (s);
2271         entry = &rtfHashTable[hash % (RTF_KEY_COUNT * 2)];
2272         for (i = 0; i < entry->count; i++)
2273         {
2274                 rp = entry->value[i];
2275                 if (hash == rp->rtfKHash && strcmp (s, rp->rtfKStr) == 0)
2276                 {
2277                         info->rtfClass = rtfControl;
2278                         info->rtfMajor = rp->rtfKMajor;
2279                         info->rtfMinor = rp->rtfKMinor;
2280                         return;
2281                 }
2282         }
2283         info->rtfClass = rtfUnknown;
2284 }
2285
2286
2287 /*
2288  * Compute hash value of symbol
2289  */
2290
2291 static int Hash(char *s)
2292 {
2293         char    c;
2294         int     val = 0;
2295
2296         while ((c = *s++) != '\0')
2297                 val += (int) c;
2298         return (val);
2299 }
2300
2301
2302 /* ---------------------------------------------------------------------- */
2303
2304 /*
2305  * Memory allocation routines
2306  */
2307
2308
2309 /*
2310  * Return pointer to block of size bytes, or NULL if there's
2311  * not enough memory available.
2312  *
2313  * This is called through RTFAlloc(), a define which coerces the
2314  * argument to int.  This avoids the persistent problem of allocation
2315  * failing under THINK C when a long is passed.
2316  */
2317
2318 char *_RTFAlloc(int size)
2319 {
2320         return HeapAlloc(me_heap, 0, size);
2321 }
2322
2323
2324 char *
2325 RTFReAlloc(char *ptr, int size)
2326 {
2327         return HeapReAlloc(me_heap, 0, ptr, size);
2328 }
2329
2330
2331 /*
2332  * Saves a string on the heap and returns a pointer to it.
2333  */
2334
2335
2336 char *RTFStrSave(char *s)
2337 {
2338         char    *p;
2339
2340         if ((p = RTFAlloc ((int) (strlen (s) + 1))) == (char *) NULL)
2341                 return ((char *) NULL);
2342         return (strcpy (p, s));
2343 }
2344
2345
2346 void RTFFree(char *p)
2347 {
2348         HeapFree(me_heap, 0, p);
2349 }
2350
2351
2352 /* ---------------------------------------------------------------------- */
2353
2354
2355 /*
2356  * Token comparison routines
2357  */
2358
2359 int RTFCheckCM(RTF_Info *info, int class, int major)
2360 {
2361         return (info->rtfClass == class && info->rtfMajor == major);
2362 }
2363
2364
2365 int RTFCheckCMM(RTF_Info *info, int class, int major, int minor)
2366 {
2367         return (info->rtfClass == class && info->rtfMajor == major && info->rtfMinor == minor);
2368 }
2369
2370
2371 int RTFCheckMM(RTF_Info *info, int major, int minor)
2372 {
2373         return (info->rtfMajor == major && info->rtfMinor == minor);
2374 }
2375
2376
2377 /* ---------------------------------------------------------------------- */
2378
2379
2380 int RTFCharToHex(char c)
2381 {
2382         if (isupper (c))
2383                 c = tolower (c);
2384         if (isdigit (c))
2385                 return (c - '0');       /* '0'..'9' */
2386         return (c - 'a' + 10);          /* 'a'..'f' */
2387 }
2388
2389
2390 int RTFHexToChar(int i)
2391 {
2392         if (i < 10)
2393                 return (i + '0');
2394         return (i - 10 + 'a');
2395 }
2396
2397
2398 /* ---------------------------------------------------------------------- */
2399
2400 /*
2401  * Open a library file.
2402  */
2403
2404
2405 void RTFSetOpenLibFileProc(RTF_Info *info, FILE *(*proc)())
2406 {
2407         info->libFileOpen = proc;
2408 }
2409
2410
2411 FILE *RTFOpenLibFile (RTF_Info *info, char *file, char *mode)
2412 {
2413         if (info->libFileOpen == NULL)
2414                 return ((FILE *) NULL);
2415         return ((*info->libFileOpen) (file, mode));
2416 }
2417
2418
2419 /* ---------------------------------------------------------------------- */
2420
2421 /*
2422  * Print message.  Default is to send message to stderr
2423  * but this may be overridden with RTFSetMsgProc().
2424  *
2425  * Message should include linefeeds as necessary.  If the default
2426  * function is overridden, the overriding function may want to
2427  * map linefeeds to another line ending character or sequence if
2428  * the host system doesn't use linefeeds.
2429  */
2430
2431
2432 void RTFMsg (RTF_Info *info, const char *fmt, ...)
2433 {
2434         char    buf[rtfBufSiz];
2435
2436         va_list args;
2437         va_start (args,fmt);
2438         vsprintf (buf, fmt, args);
2439         va_end (args);
2440         MESSAGE( "%s", buf);
2441 }
2442
2443
2444 /* ---------------------------------------------------------------------- */
2445
2446
2447 /*
2448  * Process termination.  Print error message and exit.  Also prints
2449  * current token, and current input line number and position within
2450  * line if any input has been read from the current file.  (No input
2451  * has been read if prevChar is EOF).
2452  */
2453
2454 static void DefaultPanicProc(RTF_Info *info, char *s)
2455 {
2456         MESSAGE( "%s", s);
2457         /*exit (1);*/
2458 }
2459
2460
2461
2462 void RTFPanic(RTF_Info *info, const char *fmt, ...)
2463 {
2464         char    buf[rtfBufSiz];
2465
2466         va_list args;
2467         va_start (args,fmt);
2468         vsprintf (buf, fmt, args);
2469         va_end (args);
2470         (void) strcat (buf, "\n");
2471         if (info->prevChar != EOF && info->rtfTextBuf != (char *) NULL)
2472         {
2473                 sprintf (buf + strlen (buf),
2474                         "Last token read was \"%s\" near line %ld, position %d.\n",
2475                         info->rtfTextBuf, info->rtfLineNum, info->rtfLinePos);
2476         }
2477         DefaultPanicProc(info, buf);
2478 }
2479
2480 /* ---------------------------------------------------------------------- */
2481
2482 /*
2483  * originally from RTF tools' text-writer.c
2484  *
2485  * text-writer -- RTF-to-text translation writer code.
2486  *
2487  * Read RTF input, write text of document (text extraction).
2488  */
2489
2490 static void     TextClass (RTF_Info *info);
2491 static void     ControlClass (RTF_Info *info);
2492 static void     Destination (RTF_Info *info);
2493 static void     SpecialChar (RTF_Info *info);
2494 static void     RTFPutUnicodeChar (RTF_Info *info, int c);
2495
2496 /*
2497  * Initialize the writer.
2498  */
2499
2500 void
2501 WriterInit (RTF_Info *info )
2502 {
2503 }
2504
2505
2506 int
2507 BeginFile (RTF_Info *info )
2508 {
2509         /* install class callbacks */
2510
2511         RTFSetClassCallback (info, rtfText, TextClass);
2512         RTFSetClassCallback (info, rtfControl, ControlClass);
2513
2514         return (1);
2515 }
2516
2517 /*
2518  * Write out a character.
2519  */
2520
2521 static void
2522 TextClass (RTF_Info *info)
2523 {
2524         RTFPutCodePageChar(info, info->rtfMajor);
2525 }
2526
2527
2528 static void
2529 ControlClass (RTF_Info *info)
2530 {
2531         TRACE("\n");
2532
2533         switch (info->rtfMajor)
2534         {
2535         case rtfCharAttr:
2536                 CharAttr(info);
2537                 break;
2538         case rtfCharSet:
2539                 CharSet(info);
2540                 break;
2541         case rtfDestination:
2542                 Destination (info);
2543                 break;
2544         case rtfDocAttr:
2545                 DocAttr(info);
2546                 break;
2547         case rtfSpecialChar:
2548                 SpecialChar (info);
2549                 break;
2550         }
2551 }
2552
2553
2554 static void
2555 CharAttr(RTF_Info *info)
2556 {
2557         RTFFont *font;
2558         
2559         switch (info->rtfMinor)
2560         {
2561         case rtfFontNum:
2562                 font = RTFGetFont(info, info->rtfParam);
2563                 if (font)
2564                         info->codePage = font->rtfFCodePage;
2565                 else
2566                         RTFMsg(info, "unknown font %d\n", info->rtfParam);
2567                 break;
2568         case rtfUnicodeLength:
2569                 info->unicodeLength = info->rtfParam;
2570                 break;
2571         }
2572 }
2573
2574
2575 static void
2576 CharSet(RTF_Info *info)
2577 {
2578         switch (info->rtfMinor)
2579         {
2580         case rtfAnsiCharSet:
2581                 info->ansiCodePage = 1252; /* Latin-1 */
2582                 break;
2583         case rtfMacCharSet:
2584                 info->ansiCodePage = 10000; /* MacRoman */
2585                 break;
2586         case rtfPcCharSet:
2587                 info->ansiCodePage = 437;
2588                 break;
2589         case rtfPcaCharSet:
2590                 info->ansiCodePage = 850;
2591                 break;
2592         }
2593 }
2594
2595 /*
2596  * This function notices destinations that aren't explicitly handled
2597  * and skips to their ends.  This keeps, for instance, picture
2598  * data from being considered as plain text.
2599  */
2600
2601 static void
2602 Destination (RTF_Info *info)
2603 {
2604         TRACE("\n");
2605         if (!RTFGetDestinationCallback(info, info->rtfMinor))
2606                 RTFSkipGroup (info);    
2607 }
2608
2609
2610 static void
2611 DocAttr(RTF_Info *info)
2612 {
2613         switch (info->rtfMinor)
2614         {
2615         case rtfAnsiCodePage:
2616                 info->ansiCodePage = info->rtfParam;
2617                 break;
2618         case rtfUTF8RTF:
2619                 info->ansiCodePage = CP_UTF8;
2620                 break;
2621         }
2622 }
2623
2624
2625 static void SpecialChar (RTF_Info *info)
2626 {
2627
2628         TRACE("\n");
2629
2630         switch (info->rtfMinor)
2631         {
2632         case rtfOptDest:
2633                 /* the next token determines destination, if it's unknown, skip the group */
2634                 /* this way we filter out the garbage coming from unknown destinations */ 
2635                 RTFGetToken(info); 
2636                 if (info->rtfClass != rtfDestination)
2637                         RTFSkipGroup(info);
2638                 else
2639                         RTFRouteToken(info); /* "\*" is ignored with known destinations */
2640                 break;
2641         case rtfUnicode:
2642         {
2643                 int i;
2644                
2645                 RTFPutUnicodeChar(info, info->rtfParam);
2646                 
2647                 /* After \u we must skip number of character tokens set by \ucN */
2648                 for (i = 0; i < info->unicodeLength; i++)
2649                 {
2650                         RTFGetToken(info);
2651                         if (info->rtfClass != rtfText)
2652                         {
2653                                 ERR("The token behind \\u is not text, but (%d,%d,%d)\n",
2654                                 info->rtfClass, info->rtfMajor, info->rtfMinor);
2655                                 RTFUngetToken(info);
2656                                 break;
2657                         }
2658                 }
2659                 break;
2660         }
2661         case rtfPage:
2662         case rtfSect:
2663         case rtfRow:
2664         case rtfLine:
2665         case rtfPar:
2666                 RTFPutUnicodeChar (info, '\n');
2667                 break;
2668         case rtfCell:
2669                 RTFPutUnicodeChar (info, ' ');  /* make sure cells are separated */
2670                 break;
2671         case rtfNoBrkSpace:
2672                 RTFPutUnicodeChar (info, 0x00A0);
2673                 break;
2674         case rtfTab:
2675                 RTFPutUnicodeChar (info, '\t');
2676                 break;
2677         case rtfNoBrkHyphen:
2678                 RTFPutUnicodeChar (info, 0x2011);
2679                 break;
2680         case rtfBullet:
2681                 RTFPutUnicodeChar (info, 0x2022);
2682                 break;
2683         case rtfEmDash:
2684                 RTFPutUnicodeChar (info, 0x2014);
2685                 break;
2686         case rtfEnDash:
2687                 RTFPutUnicodeChar (info, 0x2013);
2688                 break;
2689         case rtfLQuote:
2690                 RTFPutUnicodeChar (info, 0x2018);
2691                 break;
2692         case rtfRQuote:
2693                 RTFPutUnicodeChar (info, 0x2019);
2694                 break;
2695         case rtfLDblQuote:
2696                 RTFPutUnicodeChar (info, 0x201C);
2697                 break;
2698         case rtfRDblQuote:
2699                 RTFPutUnicodeChar (info, 0x201D);
2700                 break;
2701         }
2702 }
2703
2704
2705 static void
2706 RTFFlushUnicodeOutputBuffer(RTF_Info *info)
2707 {
2708         if (info->dwOutputCount)
2709         {
2710                 ME_InsertTextFromCursor(info->editor, 0, info->OutputBuffer,
2711                                         info->dwOutputCount, info->style);
2712                 info->dwOutputCount = 0;
2713         }
2714 }
2715
2716 static void
2717 RTFPutUnicodeString(RTF_Info *info, WCHAR *string, int length)
2718 {
2719         if (info->dwCPOutputCount)
2720                 RTFFlushCPOutputBuffer(info);
2721         while (length)
2722         {
2723                 int fit = min(length, sizeof(info->OutputBuffer) / sizeof(WCHAR) - info->dwOutputCount);
2724
2725                 memmove(info->OutputBuffer + info->dwOutputCount, string, fit * sizeof(WCHAR));
2726                 if (fit == sizeof(info->OutputBuffer) / sizeof(WCHAR) - info->dwOutputCount)
2727                         RTFFlushUnicodeOutputBuffer(info);
2728                 else
2729                         info->dwOutputCount += fit;
2730                 length -= fit;
2731                 string += fit;
2732         }
2733 }
2734
2735 static void
2736 RTFFlushCPOutputBuffer(RTF_Info *info)
2737 {
2738         int bufferMax = info->dwCPOutputCount * 2 * sizeof(WCHAR);
2739         WCHAR *buffer = (WCHAR *)RTFAlloc(bufferMax);
2740         int length;
2741
2742         length = MultiByteToWideChar(info->codePage, 0, info->cpOutputBuffer,
2743                                      info->dwCPOutputCount, buffer, bufferMax);
2744         info->dwCPOutputCount = 0;
2745
2746         RTFPutUnicodeString(info, buffer, length);
2747         RTFFree((char *)buffer);
2748 }
2749
2750 void
2751 RTFFlushOutputBuffer(RTF_Info *info)
2752 {
2753         if (info->dwCPOutputCount)
2754                 RTFFlushCPOutputBuffer(info);
2755         RTFFlushUnicodeOutputBuffer(info);
2756 }
2757
2758 static void
2759 RTFPutUnicodeChar(RTF_Info *info, int c)
2760 {
2761         if (info->dwCPOutputCount)
2762                 RTFFlushCPOutputBuffer(info);
2763         if (info->dwOutputCount * sizeof(WCHAR) >= ( sizeof info->OutputBuffer - 1 ) )
2764                 RTFFlushUnicodeOutputBuffer( info );
2765         info->OutputBuffer[info->dwOutputCount++] = c;
2766 }
2767
2768 static void
2769 RTFPutCodePageChar(RTF_Info *info, int c)
2770 {
2771         /* Use dynamic buffer here because it's the best way to handle
2772          * MBCS codepages without having to worry about partial chars */
2773         if (info->dwCPOutputCount >= info->dwMaxCPOutputCount)
2774         {
2775                 info->dwMaxCPOutputCount *= 2;
2776                 info->cpOutputBuffer = RTFReAlloc(info->cpOutputBuffer, info->dwMaxCPOutputCount);
2777         }
2778         info->cpOutputBuffer[info->dwCPOutputCount++] = c;
2779 }