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