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