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