summaryrefslogtreecommitdiff
path: root/support/RTF-1_06a1/rtf2troff.c
blob: ee3edfa78a5b3ea349421ddd7d9d12ea25d34074 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
/*
	Trap positions should be flushed *before* the .ev, unfortunately,
	they might not be changed until after the .ev is already out!  Agh.

	Allow these options:
	-h	disable headers/footers
	+h	enable headers/footers (default)

	Bottom position should be written in terms of page length, which
	should be stored in a register.

	Need -me, -mm, -ms specific stuff.  Is there a way of detecting
	them from within troff?

	Allow header/footer suppression?  (for cases where the code written
	is just completely wrong.)

	Need to handle \titlepg.

	rtf2troff - read rtf input, write troff output

	Syntax: rtf2troff options file

	Options:
	-e		echo token information
	-me		write -me specific output
	-mm		write -mm specific output
	-ms		write -ms specific output
	-s		disable strikethrough
	+s		enable strikethrough (default)
	-t troff	specify troff version
	-u		disable underlining
	+u		enable underlining (default)

	This translator was written to be used with the Primate Center's
	troff-variant (xroff).  xroff dependencies are bracketed within
	"if (xroff) { ... }" constructions.

	29 Jan 91	Paul DuBois	dubois@primate.wisc.edu

	29 Jan 91 V1.0. Created.
	31 Jan 91 Made to work. :-)
	27 Feb 91 V1.01. Updated for distribution 1.05.
	28 Feb 91 V1.02. Began updating for distribution 1.06.

	--

	troff puts paragraph markers at the beginning of paragraphs,
	RTF puts 'em at end; the difference is a pain.

	"Small caps" is done by \s-1 ... \s+1 and capitalizing everything
	in between.

	Good underlining is hard to do in troff; each character is
	underlined individually, which generates an abysmal amount of
	output.  Strikethrough output is even worse.

	--

	Deficiencies are myriad.  They include, but are by no means
	limited to:

	No formula support.

	Poor table support.  Tabs within cells are botched.  Tables are
	always centered.  Tables often come out looking pretty ugly.

	space before, after and between don't appear to always work.

	troff has no decimal tabs; they are treated as (ugh) right justified
	tabs.

	Vertical lines ("bar tabs") are unsupported.

	Poor font support (only R, I, B, not even I+B).  Font table ignored.

	Line numbering ignored, for no good reason.

	Only normal and continuous underlining are supported.

	No outline or shadow, since troff can't do that.

	All-caps, small-caps doesn't work for non-ASCII characters (> 127)
	or special (mapped) characters.  (Of course, why should it?)

	Default space between lines should be "auto" - but how to do that?

	Mixing of leader characters within a paragraph is not supported.

	Is nTabs = 0 handled incorrectly?

	Mechanism for handling invisible text is inconsistent.
	It's hard to do correctly...particularly as not everything
	is ignored when \v is in effect, and I'm not sure exactly
	what governs whether something is ignored or not.
*/

# include	<stdio.h>
# include	<sys/types.h>
# include	<ctype.h>
# include	"rtf.h"
# include	"rtf2troff.h"


int	tvers = TROFF;
int	mvers = noMacros;


State		*is;		/* current internal state */
DocState	*ids;		/* internal document state */
SectState	*iss;		/* internal section state */
ParState	*ips;		/* internal paragraph state */
CharState	*ics;		/* internal character state */

State		*ws;		/* written state */
DocState	*wds;		/* written document state */
SectState	*wss;		/* written section state */
ParState	*wps;		/* written paragraph state */
CharState	*wcs;		/* written character state */


/*
	Whether internal state has been changed since written state
	was last synchronized to it.  Initially true so that internal
	state will be flushed when first content character is written.
*/

int	docStateChanged = 1;	/* document properties have changed */
int	sectStateChanged = 1;	/* section properties have changed */
int	parStateChanged = 1;	/* paragraph properties have changed */
int	charStateChanged = 1;	/* character properties have changed */


/*
	Indirection level for escape (\) processing.  Incremented
	during macro/diversion collection.
*/

int	indirectionLevel = 0;


static int	haveTables = 0;	/* non-zero if any seen */
static TblState	tableState;
TblState	*its = &tableState;
int		inTable = 0;	/* whether in table cell or not */


/*
	Default output stream
*/

FILE	*f = stdout;

static int	allowUnderline = 1;
static int	allowStrikeThru = 1;

static char	*usage = "Usage: rtf2troff [-e] [-me|-mm|-ms] [-s|+s] [-t troff] [-u|+u] file";


static void	TokenEcho ();

static void	Unknown ();
static void	Text ();
static void	Group ();
static void	Control ();
static void	BeginDestination ();
static void	EndDestination ();
static void	Destination ();
static void	CharSet ();
static void	SpecialChar ();
static void	DocAttr ();
static void	SectAttr ();
static void	ParAttr ();
static void	CharAttr ();


static void	SetNextTabType ();
static void	SetNextTabPos ();
static void	SetTabChar ();


int main (argc, argv)
int	argc;
char	**argv;
{
char	*troff = "troff";
char	*macros = NULL;

	RTFInit ();

	/*
		Process arguments.  The -t and -me|-mm|-ms arguments
		must be remembered and applied *in that order* after
		argument processing, or char maps may not be selected
		properly.
	*/
	--argc;
	++argv;
	while (argc > 1 && **argv == '-')
	{
		if (strcmp ("-e", *argv) == 0)
			RTFSetReadHook (TokenEcho);
		else if (strcmp ("-s", *argv) == 0)
			allowStrikeThru = 0;
		else if (strcmp ("+s", *argv) == 0)
			allowStrikeThru = 1;
		else if (strcmp ("-u", *argv) == 0)
			allowUnderline = 0;
		else if (strcmp ("+u", *argv) == 0)
			allowUnderline = 1;
		else if (strcmp ("-t", *argv) == 0)
		{
			if (argc < 2)
			{
				fprintf (stderr, "%s\n", usage);
				exit (1);
			}
			--argc;
			++argv;
			troff = *argv;
			if (strcmp (troff, "troff") == 0)
				tvers = TROFF;
			else if (strcmp (troff, "xroff") == 0)
				tvers = XROFF;
			else if (strcmp (troff, "pstroff") == 0)
				tvers = PSTROFF;
		}
		else if (strcmp ("-me", *argv) == 0)
		{
			macros = *argv;
			mvers = meMacros;
		}
		else if (strcmp ("-mm", *argv) == 0)
		{
			macros = *argv;
			mvers = mmMacros;
		}
		else if (strcmp ("-ms", *argv) == 0)
		{
			macros = *argv;
			mvers = msMacros;
		}
		else
		{
			fprintf (stderr, "Unknown option: %s\n", *argv);
			fprintf (stderr, "%s\n", usage);
			exit (1);
		}
		--argc;
		++argv;
	}
	SelectFormatterMaps (troff);
	if (macros != NULL)
		SelectMacPackMaps (macros);

	/* not clever; only allows stdin or one named file to be read */

	if (argc > 0)
	{
		if (freopen (argv[0], "r", stdin) == NULL)
		{
			fprintf (stderr, "Can't open \"%s\"\n", argv[0]);
			exit (1);
		}
	}


/*
	Install writer callbacks into reader and initialize state
	(sets up pointers into internal state 0, and equates initial
	written state to internal state).
*/

	RTFSetClassCallback (rtfText, Text);
	RTFSetClassCallback (rtfGroup, Group);
	RTFSetClassCallback (rtfControl, Control);
	RTFSetClassCallback (rtfUnknown, Unknown);

	InitState ();

	/*
		If macro package needs to be told special characters
		might be used, do so.
	*/
	if (mvers == meMacros)
		fprintf (f, ".sc\n");
	else if (mvers == msMacros)
		fprintf (f, ".AM\n");

	/*
		Process the input stream.  Make sure the first intoken is
		a "{" so a state push will occur before anything else
		(need to preserve state 0 intact for section, paragraph,
		character default restoration).
	*/

	(void) RTFGetToken ();
	if (!RTFCheckCM (rtfGroup, rtfBeginGroup))
	{
		fprintf (stderr, "malformed rtf file - ");
		fprintf (stderr, "does not begin with \"{\"\n");
		exit (1);
	}
	RTFRouteToken ();	/* send "{" through router */
	RTFRead ();		/* read everything else */
	Flush ();

	/* some diagnostic stuff */
	CheckFinalState ();

	if (haveTables)
		fprintf (stderr, "Output contains tables (run through tbl)\n");

	exit (0);
}


/*
	Token echo function to implement -e
*/

static void TokenEcho ()
{
	fprintf (f, "%d\t%d\t%d\t%d\t\"%s\"\n",
			rtfClass, rtfMajor, rtfMinor, rtfParam, rtfTextBuf);
}



/* ---------------------------------------------------------------------- */

/*
	Token class handlers
*/

/*
	Echo any unknown tokens.  This helps know where translator
	needs to be extended.
*/

static void Unknown ()
{
	fprintf (stderr, "Unknown symbol %s\n", rtfTextBuf);
}


/*
	Group operator.  Push or pop internal state level.

	Before a pop, check whether the destination needs any shutdown.
*/

static void Group ()
{
	switch (rtfMajor)
	{
	case rtfBeginGroup:		/* push */
		PushIState ();
		break;
	case rtfEndGroup:		/* pop */
		/* do end-of-destination procecssing, then pop state */
		if (is->destination != rtfNoDestination)
			EndDestination ();
		PopIState ();
		break;
	}
}


/*
	Have a text char, write it out.  Perform special char mapping
	for chars > 127, do escapes for backslashes.

	For normal characters, perform to-upper processing.
*/

static void Text ()
{
char	buf[2], *p;

	if (rtfMajor > 127)		/* non-ASCII, map to troff equiv. */
		p = CharMapping (rtfMajor);
	else if (rtfMajor == '\\')	/* escape; escape it */
		p = "\\e";
	else				/* regular unmapped, unescaped char */
	{
		if (ics->charStyle & (styleAllCaps | styleSmallCaps))
		{
			/*
				OK to use islower()/toupper() because char
				is known to be <= 127
			*/
			if (islower (rtfMajor))
				rtfMajor = toupper (rtfMajor);
		}
		buf[0] = rtfMajor;
		buf[1] = '\0';
		p = buf;
	}
	PutString (p);
}

/*
	The char sets, special chars and destinations do not involve
	a state change; most other control things do.
*/

static void Control ()
{
	switch (rtfMajor)
	{
	case rtfCharSet:
		CharSet ();
		break;
	case rtfSpecialChar:
		SpecialChar ();
		break;
	case rtfDestination:
		BeginDestination ();
		break;
	case rtfDocAttr:
		DocAttr ();
		break;
	case rtfSectAttr:
		SectAttr ();
		break;
	case rtfParAttr:
		ParAttr ();
		break;
	case rtfCharAttr:
		CharAttr ();
		break;
	case rtfTblAttr:
		TblAttr ();
		break;
	}
}


static void CharSet ()
{
	SelectCharSetMaps (rtfMinor);
}

/*
	The hyphen and dash control things are treated as though they
	were rtfText here.

	An extra level of indirection is added to the page number register.
*/

static void SpecialChar ()
{
char	buf[rtfBufSiz];

	switch (rtfMinor)
	{
	case rtfCurHeadPage:
		PutString ("\\\\n%");	/* reference page number register */
		break;
	case rtfCurHeadDate:
		/* unimplemented */
		break;
	case rtfCurHeadTime:
		/* unimplemented */
		break;
	case rtfNoBrkSpace:
		PutString ("\\ ");
		break;
	case rtfNoReqHyphen:
		PutString ("\\%");
		break;
	case rtfNoBrkHyphen:
		PutString ("\\-");
		break;
	case rtfSect:
		Sect ();
		break;
	case rtfRow:		/* end of cell, and of row/table */
		EndCell ();
		EndTbl ();
		break;
	case rtfLine:
		Par ();
		break;
	case rtfPar:
		Par ();
		break;
	case rtfCell:
		EndCell ();		/* end current cell */
		BeginCell ();		/* begin next cell */
		break;
	case rtfTab:
		PutString ("\t");
		break;
	case rtfPage:
		Par ();
		fprintf (f, ".bp\n");
		break;
	}
}


/*
	Begin/End destination don't try to do anything except dump
	out comments delimiting the destination.  Something "real"
	should be done, but at least the comment is better than nothing.

	The switch explicitly lists those destinations for which something
	intelligent should be done.  (But nothing is, yet.)  Everything
	else falls under the default case and is simply skipped anonymously.
	
	When a destination is skipped, the "}" is fed back into the router
	so the group state gets popped by Group().
*/

static void BeginDestination ()
{
	Destination (1);
}


static void EndDestination ()
{
	Destination (0);
}


static void Destination (startDest)
int	startDest;
{
char	*dp = NULL;	/* destination name */
char	*mp = NULL;	/* macro name */
char	*rp = NULL;	/* register name */
char	*sp = NULL;	/* skipped destination name */
int	reset = 0;	/* need reset to defaults? */

	/* if beginning destination, set dest type */
	if (startDest)
		is->destination = rtfMinor;
	/* switch on destination type */
	switch (is->destination)
	{
	case rtfFootnote:
		/*
			Don't skip, but don't start diversion: effect is to
			leave footnote text in main document body.  Incorrect,
			but better than losing it.  Eventually, footnotes
			should be caught in diversions.
		*/
		dp = "footnote";
		break;
	case rtfHeader:
		dp = "header";
		mp = mHeaderAll;
		rp = rHeaderAll;
		++reset;
		break;
	case rtfHeaderLeft:
		dp = "left header";
		mp = mHeaderLeft;
		rp = rHeaderLeft;
		++reset;
		break;
	case rtfHeaderRight:
		dp = "right header";
		mp = mHeaderRight;
		rp = rHeaderRight;
		++reset;
		break;
	case rtfHeaderFirst:
		dp = "first page header";
		mp = mHeaderFirst;
		rp = rHeaderFirst;
		++reset;
		break;
	case rtfFooter:
		dp = "footer";
		mp = mFooterAll;
		rp = rFooterAll;
		++reset;
		break;
	case rtfFooterLeft:
		dp = "left footer";
		mp = mFooterLeft;
		rp = rFooterLeft;
		++reset;
		break;
	case rtfFooterRight:
		dp = "right footer";
		mp = mFooterRight;
		rp = rFooterRight;
		++reset;
		break;
	case rtfFooterFirst:
		dp = "first page footer";
		mp = mFooterFirst;
		rp = rFooterFirst;
		++reset;
		break;
	case rtfFNSep:
		sp = "footnote separator";
		break;
	case rtfFNContSep:
		sp = "continued footnote separator";
		break;
	case rtfFNContNotice:
		sp = "continued footnote notice";
		break;
	case rtfField:			/* don't ignore, but don't capture */
		dp = "field";
		break;
	case rtfFieldInst:		/* ignore */
		sp = "field instruction";
		break;
	case rtfFieldResult:		/* don't ignore, but don't capture */
		dp = "field result";
		break;
	default:
		sp = rtfTextBuf;
		break;
	}

	if (dp != NULL && startDest)
		Comment ("begin %s", dp);
	if (mp != NULL)			/* begin a capture macro */
	{
		Flush ();
		if (startDest)
		{
			FlushInitialState ();	/* make sure this is out */
			FlushSectState ();	/* flush trap positions */
			if (rp != NULL)		/* set a register */
				fprintf (f, ".nr %s 1\n", rp);
			BeginDiversion (mp);
			if (reset)
			{
				/* reset paragraph, char defaults */
				/* (fake a \pard and \plain) */
				RTFSetToken (rtfControl, rtfParAttr,
						rtfParDef, -1, "\\pard");
				RTFRouteToken ();
				RTFSetToken (rtfControl, rtfCharAttr,
						rtfPlain, -1, "\\plain");
				RTFRouteToken ();
			}
		}
		else
		{
			EndDiversion ();
		}
	}
	if (sp != NULL)			/* skip a group */
	{
		if (startDest)
		{
			Comment ("SKIPPING %s group", sp);
			RTFSkipGroup ();
			RTFRouteToken ();	/* feed "}" back into router */
		}
		else
			Comment ("end skipped group");
	}
	if (dp != NULL && startDest == 0)
		Comment ("end %s", dp);
}


static void DocAttr ()
{
double	inch = (double) rtfParam / (double) rtfTpi;

	switch (rtfMinor)
	{
	case rtfPaperWidth:
		ids->pageWidth = inch;
		break;
	case rtfPaperHeight:
		ids->pageHeight = inch;
		break;
	case rtfLeftMargin:
		ids->leftMargin = inch;
		break;
	case rtfRightMargin:
		ids->rightMargin = inch;
		break;
	case rtfTopMargin:
		ids->topMargin = inch;
		/* pre-emptive strike */
		if (iss->headerPos >= ids->topMargin)
			iss->headerPos = ids->topMargin / 2;
		break;
	case rtfBottomMargin:
		ids->bottomMargin = inch;
		if (iss->footerPos >= ids->bottomMargin)
			iss->footerPos = ids->bottomMargin / 2;
		break;
	case rtfDefTab:
		ids->tabWidth = inch;
		break;
	case rtfLandscape:
		ids->landscape = 1;
		break;
	}
	++docStateChanged;
}


static void SectAttr ()
{
double	inch = (double) rtfParam / (double) rtfTpi;

	switch (rtfMinor)
	{
	case rtfSectDef:
		RestoreSectDefaults ();
		break;
	case rtfNoBreak:
	case rtfColBreak:
	case rtfPageBreak:
	case rtfEvenBreak:
	case rtfOddBreak:
		iss->breakType = rtfMinor;
		break;
	case rtfPageStarts:
		iss->pageStart = rtfParam;
		break;
	case rtfPageCont:
		iss->pageRestart = 0;
		break;
	case rtfPageRestart:
		iss->pageRestart = 1;
		break;
	case rtfPageDecimal:
		Flush ();
		fprintf (f, ".af %% 1\n");
		break;
	case rtfPageURoman:
		Flush ();
		fprintf (f, ".af %% I\n");
		break;
	case rtfPageLRoman:
		Flush ();
		fprintf (f, ".af %% i\n");
		break;
	case rtfPageULetter:
		Flush ();
		fprintf (f, ".af %% A\n");
		break;
	case rtfPageLLetter:
		Flush ();
		fprintf (f, ".af %% a\n");
		break;
	case rtfPageNumLeft:
		break;
	case rtfPageNumTop:
		break;
	case rtfHeaderY:
		iss->headerPos = inch;
		if (iss->headerPos >= ids->topMargin)
			iss->headerPos = ids->topMargin / 2;
		break;
	case rtfFooterY:
		iss->footerPos = inch;
		if (iss->footerPos >= ids->bottomMargin)
			iss->footerPos = ids->bottomMargin / 2;
		break;
	case rtfLineModulus:
		break;
	case rtfLineDist:
		break;
	case rtfLineStarts:
		break;
	case rtfLineRestart:
		break;
	case rtfLineRestartPg:
		break;
	case rtfLineCont:
		break;
	case rtfTopVAlign:
		break;
	case rtfBottomVAlign:
		break;
	case rtfCenterVAlign:
		break;
	case rtfJustVAlign:
		break;
	case rtfColumns:
		break;
	case rtfColumnSpace:
		break;
	case rtfColumnLine:
		break;
	case rtfENoteHere:
		break;
	case rtfTitleSpecial:
		iss->titleSpecial = 1;
		break;
	}
	++sectStateChanged;
}


/*
	Paragraph defaults are restored by using the state 0 values,
	they applying the "Normal" style (style 0).

	For the rtfStyleNum, the tab flag is reset before expanding the
	style so any inherited tabs will be overridden by tabs in the
	style, and reset after expansion so any tabs in the paragraph
	itself will override inherited or style tabs.

	The "unimplemented" cases below are those which are currently
	ignored, but for which something might be done someday, i.e.,
	they're reminders.
*/

static void ParAttr ()
{
double	inch = (double) rtfParam / (double) rtfTpi;

	switch (rtfMinor)
	{
	case rtfParDef:
		RestoreParDefaults ();
		break;
	case rtfStyleNum:
		ips->tabFlag = 0;
		RTFExpandStyle (rtfParam);
		ips->tabFlag = 0;
		break;
	case rtfQuadLeft:
	case rtfQuadRight:
	case rtfQuadJust:
	case rtfQuadCenter:
		ips->justification = rtfMinor;
		break;
	case rtfFirstIndent:
		ips->firstIndent = inch;
		break;
	case rtfLeftIndent:
		ips->leftIndent = inch;
		break;
	case rtfRightIndent:
		ips->rightIndent = inch;
		break;
	case rtfSpaceBefore:
		ips->spaceBefore = inch;
		break;
	case rtfSpaceAfter:
		ips->spaceAfter = inch;
		break;
	case rtfSpaceBetween:
		ips->spaceBetween = inch;
		break;
	case rtfInTable:
		haveTables = 1;
		/*
			If first cell of row, set temp indent to left edge
			of table.  (Actually, this is done incorrectly; tables
			are always centered.)
			Subsequent cells are begun when \cell is seen.
		*/
		if (its->tableHeader == 0)	/* first cell; need */
		{				/* table prolog */
			BeginTbl ();
			BeginCell ();
		}
		break;
	case rtfNoLineNum:
		/* unimplemented */
		break;
	case rtfTabPos:
		SetNextTabPos (inch);
		break;
	case rtfTabRight:
	case rtfTabCenter:
	case rtfTabDecimal:
		SetNextTabType (rtfMinor);
		break;
	case rtfTabBar:
		/* unimplemented */
		break;
	case rtfBorderTop:
		ips->borderFlags |= borderTop;
		break;
	case rtfBorderBottom:
		ips->borderFlags |= borderBottom;
		break;
	case rtfBorderLeft:
		/* unimplemented */
		break;
	case rtfBorderRight:
		/* unimplemented */
		break;
	case rtfBorderBar:
		/* unimplemented */
		break;
	case rtfBorderBox:
		/* unimplemented */
		break;
	case rtfBorderBetween:
		/* unimplemented */
		break;
	case rtfBorderSingle:
	case rtfBorderThick:
	case rtfBorderShadow:
	case rtfBorderDouble:
	case rtfBorderDot:
	case rtfBorderHair:
		ips->borderType = rtfMinor;
		break;
	case rtfBorderSpace:
		/* unimplemented */
		break;
	case rtfLeaderDot:
	case rtfLeaderHyphen:
	case rtfLeaderUnder:
	case rtfLeaderThick:
		SetTabChar (rtfMinor);
		break;
	}
	++parStateChanged;
}


/*
	Several of the attributes can be turned off with param value
	of zero (e.g., \b vs. \b0), but since the value of rtfParam
	is 0 if no param is given, test the text of the token directly.
	to find out if there's a zero at the end of it.

	\plain is like \pard but for characters, i.e, it restores all
	character defaults.
*/

static void CharAttr ()
{
int	turnOn = (rtfTextBuf[rtfTextLen-1] != '0');

	switch (rtfMinor)
	{
	case rtfPlain:
		RestoreCharDefaults ();
		break;
	case rtfBold:
		if (turnOn)
			ics->charStyle |= styleBold;
		else
			ics->charStyle &= ~styleBold;
		break;
	case rtfItalic:
		if (turnOn)
			ics->charStyle |= styleItalic;
		else
			ics->charStyle &= ~styleItalic;
		break;
	case rtfStrikeThru:
		if (allowStrikeThru)
		{
			if (turnOn)
				ics->charStyle |= styleStrikeThru;
			else
				ics->charStyle &= ~styleStrikeThru;
		}
		break;
	case rtfOutline:
		if (turnOn)
			ics->charStyle |= styleOutline;
		else
			ics->charStyle &= ~styleOutline;
		break;
	case rtfShadow:
		if (turnOn)
			ics->charStyle |= styleShadow;
		else
			ics->charStyle &= ~styleShadow;
		break;
	case rtfSmallCaps:
		if (turnOn)
			ics->charStyle |= styleSmallCaps;
		else
			ics->charStyle &= ~styleSmallCaps;
		break;
	case rtfAllCaps:
		if (turnOn)
			ics->charStyle |= styleAllCaps;
		else
			ics->charStyle &= ~styleAllCaps;
		break;
	case rtfInvisible:
		if (turnOn)
			ics->charStyle |= styleInvisible;
		else
			ics->charStyle &= ~styleInvisible;
		break;
	case rtfFontNum:
		/* unimplemented */
		break;
	case rtfFontSize:
		/* sizes are in half-points, convert to whole points */
		ics->fontSize = (int) (rtfParam / 2);
		if (ics->fontSize <= 0)
			++ics->fontSize;	/* don't play with fire */
		break;
	case rtfExpand:
		/* unimplemented */
		break;
	case rtfUnderline:
		if (allowUnderline)
		{
			if (turnOn)
				ics->charStyle |= styleUnderline;
			else
				ics->charStyle &= ~styleUnderline;
		}
		break;
	case rtfWUnderline:
		if (allowUnderline)
		{
			if (turnOn)
				ics->charStyle |= styleWUnderline;
			else
				ics->charStyle &= ~styleWUnderline;
		}
		break;
	case rtfDUnderline:
		/* unimplemented */
		break;
	case rtfDbUnderline:
		/* unimplemented */
		break;
	case rtfNoUnderline:
		ics->charStyle &= ~styleUnderline;
		ics->charStyle &= ~styleWUnderline;
		break;
	case rtfSuperScript:
		/* superscripts are in half-points, convert to points */
		ics->superScript = rtfParam / 2;
		break;
	case rtfSubScript:
		/* subscripts are in half-points, convert to points */
		ics->subScript = rtfParam / 2;
		break;
	case rtfRevised:
		/* unimplemented */
		break;
	case rtfForeColor:
		/* unimplemented */
		break;
	case rtfBackColor:
		/* unimplemented */
		break;
	}
	++charStateChanged;
}


/* ---------------------------------------------------------------------- */

/*
	Tab handling routines
*/

void InitTabSet ()
{
int	i;

	ips->nTabs = 0;
	/*
		Set all tabs to be left-justified; that will then be used if
		no other tab type is specified.  This is done because the
		position is specified *after* the type.
	*/
	for (i = 0; i < maxTab; i++)
		ips->tabType[i] = rtfTabLeft;
}


static void SetNextTabPos (pos)
double	pos;
{
	if (ips->tabFlag != 0 && ips->nTabs >= maxTab)
		fprintf (stderr, "maximum tabstop count (%d) exceeded\n",
							maxTab);
	else
	{
		/* if no tab info has been set for this state, reinit them */
		if (ips->tabFlag == 0)
		{
			InitTabSet ();
			ips->tabFlag = 1;
		}
		ips->tab[ips->nTabs++] = pos;
	}
}


/*
	Tab types are specified *before* the position to which they apply
	is given, so set the next available slot in anticipation of the
	position's being specified next.
*/

static void SetNextTabType (type)
int	type;
{
	if (ips->tabFlag != 0 && ips->nTabs >= maxTab)
		fprintf (stderr, "maximum tabstop count (%d) exceeded\n",
							maxTab);
	else
	{
		/* if no tab info has been set for this state, reinit them */
		if (ips->tabFlag == 0)
		{
			InitTabSet ();
			ips->tabFlag = 1;
		}
		ips->tabType[ips->nTabs] = type;
	}
}


static void SetTabChar (leader)
int	leader;
{
	if (ips->tabFlag != 0 && ips->nTabs >= maxTab)
		fprintf (stderr, "maximum tabstop count (%d) exceeded\n",
							maxTab);
	else
	{
		/* if no tab info has been set for this state, reinit them */
		if (ips->tabFlag == 0)
		{
			InitTabSet ();
			ips->tabFlag = 1;
		}
		ips->tabChar = rtfMinor;
	}
}