xref: /reactos/dll/win32/riched20/reader.c (revision fb5d5ecd)
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 EOF;
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 #define RTF_KEY_COUNT (sizeof(rtfKey) / sizeof(RTFKey))
2179 
2180 typedef struct tagRTFHashTableEntry {
2181 	int count;
2182 	RTFKey **value;
2183 } RTFHashTableEntry;
2184 
2185 static RTFHashTableEntry rtfHashTable[RTF_KEY_COUNT * 2];
2186 
2187 
2188 /*
2189  * Initialize lookup table hash values.  Only need to do this once.
2190  */
2191 
2192 void LookupInit(void)
2193 {
2194 	RTFKey	*rp;
2195 
2196 	memset(rtfHashTable, 0, sizeof rtfHashTable);
2197 	for (rp = rtfKey; rp->rtfKStr != NULL; rp++)
2198 	{
2199 		int index;
2200 
2201 		rp->rtfKHash = Hash (rp->rtfKStr);
2202 		index = rp->rtfKHash % (RTF_KEY_COUNT * 2);
2203 		if (!rtfHashTable[index].count)
2204 			rtfHashTable[index].value = heap_alloc(sizeof(RTFKey *));
2205 		else
2206 			rtfHashTable[index].value = heap_realloc(rtfHashTable[index].value, sizeof(RTFKey *) * (rtfHashTable[index].count + 1));
2207 		rtfHashTable[index].value[rtfHashTable[index].count++] = rp;
2208 	}
2209 }
2210 
2211 void LookupCleanup(void)
2212 {
2213 	unsigned int i;
2214 
2215 	for (i=0; i<RTF_KEY_COUNT*2; i++)
2216 	{
2217 		heap_free( rtfHashTable[i].value );
2218 		rtfHashTable[i].value = NULL;
2219 		rtfHashTable[i].count = 0;
2220 	}
2221 }
2222 
2223 
2224 /*
2225  * Determine major and minor number of control token.  If it's
2226  * not found, the class turns into rtfUnknown.
2227  */
2228 
2229 static void Lookup(RTF_Info *info, char *s)
2230 {
2231 	RTFKey	*rp;
2232 	int	hash;
2233         RTFHashTableEntry *entry;
2234         int i;
2235 
2236 	++s;			/* skip over the leading \ character */
2237 	hash = Hash (s);
2238         entry = &rtfHashTable[hash % (RTF_KEY_COUNT * 2)];
2239 	for (i = 0; i < entry->count; i++)
2240 	{
2241                 rp = entry->value[i];
2242 		if (hash == rp->rtfKHash && strcmp (s, rp->rtfKStr) == 0)
2243 		{
2244 			info->rtfClass = rtfControl;
2245 			info->rtfMajor = rp->rtfKMajor;
2246 			info->rtfMinor = rp->rtfKMinor;
2247 			return;
2248 		}
2249 	}
2250 	info->rtfClass = rtfUnknown;
2251 }
2252 
2253 
2254 /*
2255  * Compute hash value of symbol
2256  */
2257 
2258 static int Hash(const char *s)
2259 {
2260 	char	c;
2261 	int	val = 0;
2262 
2263 	while ((c = *s++) != '\0')
2264 		val += c;
2265 	return (val);
2266 }
2267 
2268 
2269 
2270 /* ---------------------------------------------------------------------- */
2271 
2272 
2273 /*
2274  * Token comparison routines
2275  */
2276 
2277 int RTFCheckCM(const RTF_Info *info, int class, int major)
2278 {
2279 	return (info->rtfClass == class && info->rtfMajor == major);
2280 }
2281 
2282 
2283 int RTFCheckCMM(const RTF_Info *info, int class, int major, int minor)
2284 {
2285 	return (info->rtfClass == class && info->rtfMajor == major && info->rtfMinor == minor);
2286 }
2287 
2288 
2289 int RTFCheckMM(const RTF_Info *info, int major, int minor)
2290 {
2291 	return (info->rtfMajor == major && info->rtfMinor == minor);
2292 }
2293 
2294 
2295 /* ---------------------------------------------------------------------- */
2296 
2297 
2298 int RTFCharToHex(char c)
2299 {
2300 	if (isupper (c))
2301 		c = tolower (c);
2302 	if (isdigit (c))
2303 		return (c - '0');	/* '0'..'9' */
2304 	return (c - 'a' + 10);		/* 'a'..'f' */
2305 }
2306 
2307 
2308 /* ---------------------------------------------------------------------- */
2309 
2310 /*
2311  * originally from RTF tools' text-writer.c
2312  *
2313  * text-writer -- RTF-to-text translation writer code.
2314  *
2315  * Read RTF input, write text of document (text extraction).
2316  */
2317 
2318 static void	TextClass (RTF_Info *info);
2319 static void	ControlClass (RTF_Info *info);
2320 static void     DefFont(RTF_Info *info);
2321 static void	Destination (RTF_Info *info);
2322 static void	SpecialChar (RTF_Info *info);
2323 static void	RTFPutUnicodeChar (RTF_Info *info, int c);
2324 
2325 /*
2326  * Initialize the writer.
2327  */
2328 
2329 void
2330 WriterInit (RTF_Info *info )
2331 {
2332 }
2333 
2334 
2335 int
2336 BeginFile (RTF_Info *info )
2337 {
2338 	/* install class callbacks */
2339 
2340 	RTFSetClassCallback (info, rtfText, TextClass);
2341 	RTFSetClassCallback (info, rtfControl, ControlClass);
2342 
2343 	return (1);
2344 }
2345 
2346 /*
2347  * Write out a character.
2348  */
2349 
2350 static void
2351 TextClass (RTF_Info *info)
2352 {
2353 	RTFPutCodePageChar(info, info->rtfMajor);
2354 }
2355 
2356 
2357 static void
2358 ControlClass (RTF_Info *info)
2359 {
2360 	switch (info->rtfMajor)
2361 	{
2362         case rtfCharAttr:
2363                 CharAttr(info);
2364                 ME_RTFCharAttrHook(info);
2365                 break;
2366         case rtfParAttr:
2367                 ME_RTFParAttrHook(info);
2368                 break;
2369         case rtfTblAttr:
2370                 ME_RTFTblAttrHook(info);
2371                 break;
2372         case rtfCharSet:
2373                 CharSet(info);
2374                 break;
2375         case rtfDefFont:
2376                 DefFont(info);
2377                 break;
2378 	case rtfDestination:
2379 		Destination (info);
2380 		break;
2381         case rtfDocAttr:
2382                 DocAttr(info);
2383                 break;
2384 	case rtfSpecialChar:
2385                 SpecialChar (info);
2386                 ME_RTFSpecialCharHook(info);
2387 		break;
2388 	}
2389 }
2390 
2391 
2392 static void
2393 CharAttr(RTF_Info *info)
2394 {
2395         RTFFont *font;
2396 
2397         switch (info->rtfMinor)
2398         {
2399         case rtfFontNum:
2400                 font = RTFGetFont(info, info->rtfParam);
2401                 if (font)
2402                 {
2403                         if (info->ansiCodePage != CP_UTF8 && info->codePage != font->rtfFCodePage)
2404                         {
2405                                 RTFFlushOutputBuffer(info);
2406                                 info->codePage = font->rtfFCodePage;
2407                         }
2408                         TRACE("font %d codepage %d\n", info->rtfParam, info->codePage);
2409                 }
2410                 else
2411                         ERR( "unknown font %d\n", info->rtfParam);
2412                 break;
2413         case rtfUnicodeLength:
2414                 info->unicodeLength = info->rtfParam;
2415                 break;
2416         }
2417 }
2418 
2419 
2420 static void
2421 CharSet(RTF_Info *info)
2422 {
2423 	if (info->ansiCodePage == CP_UTF8)
2424 		return;
2425 
2426         switch (info->rtfMinor)
2427         {
2428         case rtfAnsiCharSet:
2429                 info->ansiCodePage = 1252; /* Latin-1 */
2430                 break;
2431         case rtfMacCharSet:
2432                 info->ansiCodePage = 10000; /* MacRoman */
2433                 break;
2434         case rtfPcCharSet:
2435                 info->ansiCodePage = 437;
2436                 break;
2437         case rtfPcaCharSet:
2438                 info->ansiCodePage = 850;
2439                 break;
2440         }
2441 }
2442 
2443 /*
2444  * This function notices destinations that aren't explicitly handled
2445  * and skips to their ends.  This keeps, for instance, picture
2446  * data from being considered as plain text.
2447  */
2448 
2449 static void
2450 Destination (RTF_Info *info)
2451 {
2452 	if (!RTFGetDestinationCallback(info, info->rtfMinor))
2453 		RTFSkipGroup (info);
2454 }
2455 
2456 
2457 static void
2458 DefFont(RTF_Info *info)
2459 {
2460         TRACE("%d\n", info->rtfParam);
2461         info->defFont = info->rtfParam;
2462 }
2463 
2464 
2465 static void
2466 DocAttr(RTF_Info *info)
2467 {
2468         TRACE("minor %d, param %d\n", info->rtfMinor, info->rtfParam);
2469 
2470         switch (info->rtfMinor)
2471         {
2472         case rtfAnsiCodePage:
2473                 info->codePage = info->ansiCodePage = info->rtfParam;
2474                 break;
2475         case rtfUTF8RTF:
2476                 info->codePage = info->ansiCodePage = CP_UTF8;
2477                 break;
2478         }
2479 }
2480 
2481 
2482 static void SpecialChar (RTF_Info *info)
2483 {
2484 	switch (info->rtfMinor)
2485 	{
2486 	case rtfOptDest:
2487 		/* the next token determines destination, if it's unknown, skip the group */
2488 		/* this way we filter out the garbage coming from unknown destinations */
2489 		RTFGetToken(info);
2490 		if (info->rtfClass != rtfDestination)
2491 			RTFSkipGroup(info);
2492 		else
2493 			RTFRouteToken(info); /* "\*" is ignored with known destinations */
2494 		break;
2495 	case rtfUnicode:
2496 	{
2497                 int i;
2498 
2499                 RTFPutUnicodeChar(info, info->rtfParam);
2500 
2501                 /* After \u we must skip number of character tokens set by \ucN */
2502                 for (i = 0; i < info->unicodeLength; i++)
2503                 {
2504 			RTFGetToken(info);
2505                         if (info->rtfClass != rtfText)
2506 		        {
2507                                 ERR("The token behind \\u is not text, but (%d,%d,%d)\n",
2508 				info->rtfClass, info->rtfMajor, info->rtfMinor);
2509                                 RTFUngetToken(info);
2510                                 break;
2511                         }
2512 		}
2513 		break;
2514 	}
2515 	case rtfLine:
2516             RTFFlushOutputBuffer(info);
2517             ME_InsertEndRowFromCursor(info->editor, 0);
2518             break;
2519 	case rtfPage:
2520 	case rtfSect:
2521 	case rtfPar:
2522                 RTFFlushOutputBuffer(info);
2523                 ME_SetSelectionParaFormat(info->editor, &info->fmt);
2524                 memset(&info->fmt, 0, sizeof(info->fmt));
2525                 info->fmt.cbSize = sizeof(info->fmt);
2526 		RTFPutUnicodeChar (info, '\r');
2527 		if (info->editor->bEmulateVersion10) RTFPutUnicodeChar (info, '\n');
2528 		break;
2529 	case rtfNoBrkSpace:
2530 		RTFPutUnicodeChar (info, 0x00A0);
2531 		break;
2532 	case rtfTab:
2533 		RTFPutUnicodeChar (info, '\t');
2534 		break;
2535 	case rtfNoBrkHyphen:
2536 		RTFPutUnicodeChar (info, 0x2011);
2537 		break;
2538 	case rtfBullet:
2539 		RTFPutUnicodeChar (info, 0x2022);
2540 		break;
2541 	case rtfEmDash:
2542 		RTFPutUnicodeChar (info, 0x2014);
2543 		break;
2544 	case rtfEnDash:
2545 		RTFPutUnicodeChar (info, 0x2013);
2546 		break;
2547         case rtfEmSpace:
2548                 RTFPutUnicodeChar (info, ' ');
2549                 break;
2550         case rtfEnSpace:
2551                 RTFPutUnicodeChar (info, ' ');
2552                 break;
2553 	case rtfLQuote:
2554 		RTFPutUnicodeChar (info, 0x2018);
2555 		break;
2556 	case rtfRQuote:
2557 		RTFPutUnicodeChar (info, 0x2019);
2558 		break;
2559 	case rtfLDblQuote:
2560 		RTFPutUnicodeChar (info, 0x201C);
2561 		break;
2562 	case rtfRDblQuote:
2563 		RTFPutUnicodeChar (info, 0x201D);
2564 		break;
2565         case rtfLTRMark:
2566                 RTFPutUnicodeChar (info, 0x200E);
2567                 break;
2568         case rtfRTLMark:
2569                 RTFPutUnicodeChar (info, 0x200F);
2570                 break;
2571         case rtfNoWidthJoiner:
2572                 RTFPutUnicodeChar (info, 0x200D);
2573                 break;
2574         case rtfNoWidthNonJoiner:
2575                 RTFPutUnicodeChar (info, 0x200C);
2576                 break;
2577 	}
2578 }
2579 
2580 
2581 static void
2582 RTFFlushUnicodeOutputBuffer(RTF_Info *info)
2583 {
2584         if (info->dwOutputCount)
2585         {
2586                 ME_InsertTextFromCursor(info->editor, 0, info->OutputBuffer,
2587                                         info->dwOutputCount, info->style);
2588                 info->dwOutputCount = 0;
2589         }
2590 }
2591 
2592 
2593 static void
2594 RTFPutUnicodeString(RTF_Info *info, const WCHAR *string, int length)
2595 {
2596         if (info->dwCPOutputCount)
2597                 RTFFlushCPOutputBuffer(info);
2598         while (length)
2599         {
2600                 int fit = min(length, sizeof(info->OutputBuffer) / sizeof(WCHAR) - info->dwOutputCount);
2601 
2602                 memmove(info->OutputBuffer + info->dwOutputCount, string, fit * sizeof(WCHAR));
2603                 info->dwOutputCount += fit;
2604                 length -= fit;
2605                 string += fit;
2606                 if (sizeof(info->OutputBuffer) / sizeof(WCHAR) == info->dwOutputCount)
2607                         RTFFlushUnicodeOutputBuffer(info);
2608         }
2609 }
2610 
2611 static void
2612 RTFFlushCPOutputBuffer(RTF_Info *info)
2613 {
2614         int bufferMax = info->dwCPOutputCount * 2 * sizeof(WCHAR);
2615         WCHAR *buffer = heap_alloc(bufferMax);
2616         int length;
2617 
2618         length = MultiByteToWideChar(info->codePage, 0, info->cpOutputBuffer,
2619                                      info->dwCPOutputCount, buffer, bufferMax/sizeof(WCHAR));
2620         info->dwCPOutputCount = 0;
2621 
2622         RTFPutUnicodeString(info, buffer, length);
2623         heap_free(buffer);
2624 }
2625 
2626 void
2627 RTFFlushOutputBuffer(RTF_Info *info)
2628 {
2629         if (info->dwCPOutputCount)
2630                 RTFFlushCPOutputBuffer(info);
2631         RTFFlushUnicodeOutputBuffer(info);
2632 }
2633 
2634 static void
2635 RTFPutUnicodeChar(RTF_Info *info, int c)
2636 {
2637 	if (info->dwCPOutputCount)
2638                 RTFFlushCPOutputBuffer(info);
2639         if (info->dwOutputCount * sizeof(WCHAR) >= ( sizeof info->OutputBuffer - 1 ) )
2640 		RTFFlushUnicodeOutputBuffer( info );
2641 	info->OutputBuffer[info->dwOutputCount++] = c;
2642 }
2643 
2644 static void
2645 RTFPutCodePageChar(RTF_Info *info, int c)
2646 {
2647         /* Use dynamic buffer here because it's the best way to handle
2648          * MBCS codepages without having to worry about partial chars */
2649         if (info->dwCPOutputCount >= info->dwMaxCPOutputCount)
2650         {
2651                 info->dwMaxCPOutputCount *= 2;
2652                 info->cpOutputBuffer = heap_realloc(info->cpOutputBuffer, info->dwMaxCPOutputCount);
2653         }
2654         info->cpOutputBuffer[info->dwCPOutputCount++] = c;
2655 }
2656