summaryrefslogtreecommitdiff
path: root/support/RTF-1_06a1/rtf.ms
blob: 2b060bb202ebef2aec0c1e323689bdeecd8e967e (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
.\"
.\" tbl % | xroff -ms | lpr
.\"
.\" revision date - change whenever this file is edited
.ds RD 5 April 1991
.nr PO 1.2i	\" page offset 1.2 inches
.nr PD .7v	\" inter-paragraph distance
.\"
.EH 'RTF Processing Tool'- % -'Distribution 1.06a1'
.OH 'Distribution 1.06a1'- % -'RTF Processing Tool'
.OF 'Revision date:\0\0\*(RD''Printed:\0\0\n(dy \*(MO 19\n(yr'
.EF 'Revision date:\0\0\*(RD''Printed:\0\0\n(dy \*(MO 19\n(yr'
.\"
.\" subscript strings
.ds < \s-2\v'.4m'
.ds > \v'-.4m'\s+2
.\"
.\" I - italic font (taken from -ms and changed)
.de I
.nr PQ \\n(.f
.if t \&\\$3\\f2\\$1\\fP\&\\$2
.if n .if \\n(.$=1 \&\\$1
.if n .if \\n(.$>1 \&\\$1\c
.if n .if \\n(.$>1 \&\\$2
..
.de IS	\" interface routine description header start
.DS L
.ta .8i
.ft B
..
.de IE	\" interface routine description header end
.DE
.ft R
..
.TL
A Tool for RTF Processing
.sp .5v
Version 1.06a1
.AU
Paul DuBois
dubois@primate.wisc.edu
.AI
Wisconsin Regional Primate Research Center
Revision date:\0\0\*(RD
.NH
Introduction
.LP
This document describes a general purpose tool for processing RTF
files\*-an RTF reader which may be configured in a
well-defined manner to allow it to be used with a variety of writers
generating different output formats.
This provides a method for generating RTF-to-\fIXXX\fR translators.
In theory.
.LP
I assume that you have some familiarity with
RTF syntax and semantics, and that you're willing to study the source
code of the RTF distribution described here.
If you don't have the RTF specification, you can get it from the FTP site
listed under ``Distribution Availability.''
References to ``the specification'' refer to this document.
.LP
If you use this tool and find that you have an RTF file that won't pass
through the sample translator
.I rtf2null ,
or for which
.I rtf2null
announces unknown symbols, please contact me so the tool can
be improved.
It is best if you can supply the RTF file for which this behavior is observed.
.NH
Theory of Operation
.NH 2
Translator Architecture
.LP
There are three components to an RTF translator (at least as conceived
here): reader code, writer code, and setup code.
These break down as follows.
.IP reader\0\0
Responsible for peeling tokens out of the input stream,
classifying them, and causing the writer to process them.
.IP writer\0\0
Responsible for translating tokens from the input
stream into the required output format.
.IP setup\0\0
Responsible for making sure the reader and writer are initialized, and
for calling the reader, to cause translation to occur.
.LP
This architecture allows the reader to remain constant, so that different
translaters can be built by supplying different writer and setup code.
.LP
In practice, to build a new translator, you supply a
.I main()
function and the writer code, and link in the RTF reader.
.I main()
includes the setup code and is responsible to see that the following are done:
.IP \(bu
.nr PD 0v
Process command-line arguments
.IP \(bu
Configure the reader, which may involve:
.RS
.IP \(bu
Reset the input stream if necessary
.IP \(bu
Configure other reader behavior, such as whether to
process the font and color tables internally.
.IP \(bu
Install writer callbacks into the reader so it knows what functions to
call when various kinds of tokens occur
.RE
.IP \(bu
Initialize the writer
.IP \(bu
Call the reader to process input stream
.IP \(bu
.nr PD .7v
Terminate the writer
.LP
The minimal translator looks something like this:
.LP
.DS
# include	<stdio.h>
# include	"rtf.h"

int main ()
{
	RTFInit ();
	RTFRead ();
	exit (0);
}
.DE
.LP
This initializes the reader, and calls it to read
.I stdin .
The writer portion is null (i.e., there is no writer), so all that happens
is that the reader tokenizes the input and discards it.
That isn't very interesting;
most of the sample translators are examples of more elaborate translators.
.NH 2
Reader Operation
.LP
Tokens are classified using up to three numbers: token class, and major and
minor numbers.
The class number can be:
.LP
.DS
.ta 1.5i
rtfUnknown	unrecognized token
rtfGroup	``{'' or ``}''
rtfText	plain text character
rtfControl	token beginning with ``\e''
rtfEOF	fake class number; indicates end of input stream
.DE
.LP
There are some exceptions.
A few tokens beginning with ``\e'' actually belong to other classes,
a tab character is treated like ``\etab'',
and unrecognized tokens are put in class
.I rtfUnknown
no matter what they look like.
.LP
Within a class, tokens are assigned a major number, and perhaps a
minor number.
For the
.I rtfText
class, the major number is the value of the character (0..255), and there
is no minor number.
For the
.I rtfControl
class, most tokens have both a major and minor number.
For instance, all paragraph attribute control symbols have major number
.I rtfParAttr
and a minor number indicating which property, such as
.I rtfLeftIndent
or
.I rtfSpaceBefore .
A few oddball control tokens have no minor number.
.LP
A ``plain text'' character can be a literal character, a character specified
in hex notation (``\e\`\fIxx\fR'') or one of the special escaped characters
(``\e{'', ``\e}'', ``\e\e'').
The sequence ``\e:'' is treated as a plain text colon.
This is arguably wrong; the rationale is given later under the
description of the
.I RTFGetToken()
function.
.LP
Ideally, there should never be any tokens in the
.I rtfUnknown
class, but as the RTF standard continues to develop, unknown tokens are
inevitable.
.LP
To write a translator, you'll need to familiarize yourself with
the token classification scheme by reading
.I rtf.h .
A skeleton translator
.I rtfskel.c
is included with the distribution and may be used as a basis for new
translators.
.LP
Each time a token is read, several global variables are set.
.I rtfClass ,
.I rtfMajor ,
and
.I rtfMinor
indicate the token class, and major and minor numbers.
(The major and minor numbers may be meaningless depending on the kind
of token.)
Control symbols may have a parameter value, e.g., ``\emargr720''
specifies a right margin (in units of 720 twentieths of a point).
The parameter value is stored in
.I rtfParam .
The text of the token (including the parameter text) is placed in
.I rtfTextBuf
and its length in
.I rtfTextLen .
.LP
If no parameter value is given,
.I rtfParam
is 0, which is indistinguishable from an explicitly specified
parameter of ``0''.
If you need to tell the difference, examine
.I rtfTextBuf[rtfTextLen-1]
to see if it's a digit or not.
.LP
The reader assumes a 7-bit character set.
The specification indicates that character values \(>= 128 may be encoded with
the ``\e'\fIxx\fR'' sequence.
If the reader sees a character with the high bit set, it prints a message
and exits.
.LP
Generally, a translator will configure the RTF reader
to call particular writer
functions when certain kinds of tokens are encountered in the input
stream.
These functions are known as
.I "class callbacks" .
Writer callbacks can be registered with the reader using
.I RTFSetClassCallback()
for each token class.
.LP
The reader reads each token, classifies it, and sends it to a token routing
function
.I RTFRouteToken() ,
tries to find a writer callback function to process the token.
Tokens in a given
class are ignored if no callback is registered for the class.
.LP
Class callbacks make it quite easy to receive notification when
certain types of tokens occur in the input.
For instance, a crude RTF text extractor could be written by
installing a callback function for the
.I rtfText
class.\**
.FS
The reasons this is a crude translator are that:
(i) some text characters occur in contexts where the characters
are not intended to be output, e.g., font tables, stylesheets; (ii)
character values greater than 127 probably should be translated into the normal
ASCII range; (iii) some control symbols like ``\etab'' represent
output text characters.
The sample translator
.I rtf2text
addresses these problems in a (slightly) more sophisticated manner.
.FE
Whenever the function is invoked,
.I rtfMajor
will contain a value in the range 0..255 representing the character value.
.LP
.DS
# include	<stdio.h>
# include	"rtf.h"

void TextCallback ()
{
	putchar (rtfMajor);
}

int main ()
{
	RTFInit ();
	RTFSetClassCallback (rtfText, TextCallback);
	RTFRead ();
	exit (0);
}
.DE
.LP
Callbacks for the
.I rtfControl
and
.I rtfGroup
classes
typically operate by selecting on the token major number to determine
the action to take.
A callback for the
.I rtfGroup
class usually will do something like this:
.LP
.DS
void BraceCallback ()
{
	switch (rtfMajor)
	{
	case rtfBeginGroup:
		\fI...push state...\fR
		break;
	case rtfEndGroup:
		\fI...pop state...\fR
		break;
	}
}
.DE
.NH 2
Destination Readers
.LP
Grouping in RTF documents occurs within braces ``{'' and ``}''.
One kind of group is the
.I destination .
The token immediately following the opening brace is a destination
control symbol.
These indicate such things as headers, footers, footnotes, etc.
.LP
Three destinations which specify information for internal use (i.e.,
information which affects
output but isn't itself written) are the font table, color table and
stylesheet.
Since these three destinations occur so commonly and have a special syntax,
the RTF reader by default gobbles them up itself when it recognizes them.
The functions which do this are called
.I "destination readers"
and are probably the nearest thing in the reader to what might be
called parsers.
They are installed by default so that translators can be written
without the burden of understanding the syntax or digesting the
contents of these destinations.
Each of them constructs a list of the entries specified in the
destination and the reader includes functions providing access to
these lists.
.LP
Translators can turn off or override these defaults with
.I RTFSetDestinationCallback()
if necessary.
To override one, pass the address of a different destination reader
function.
To turn one off, pass NULL.
.LP
Destination callbacks may be called for any destination, not just
.I rtfFontTbl ,
.I rtfColorTbl
and
.I rtfStyleSheet .
Destinations for which no callback is registered are not treated
specially.
.LP
Other destinations for which there is a default
reader are the information (``\einfo'') and picture (``\epict'')
destinations; all they do is skip to the end of the group.
.NH 3
Using the Built-in Destination Readers
.LP
The font table, color table and stylesheet information is maintained
internally, and the reader either acts on that information itself, or
allows itself to be queried by the writer about it, as described
below.
These descriptions do not apply if the translator shuts off or
overrides the default destination readers, of course.
.LP
\fBStylesheet\*-\fRThe reader acts on this itself.
When the stylesheet destination is encountered, the style contents are
remembered.
Thereafter, whenever the writer receives notification that a style number
control symbol (``\es\fInnn\fR'') has occurred, it can call
.I RTFExpandStyle(rtfParam)
to cause the style to be expanded.
The reader consults contents of the stylesheet and each
token in the style definition is routed in turn back to the writer.
This effects a sort of macro expansion.
.LP
If the writer doesn't care about style expansion, it simply refrains
from calling
.I RTFExpandStyle() .
.LP
If the writer wants information about a style, it can call
.I RTFGetStyle() .
.LP
\fBFont table\*-\fRFor each entry in the font table, the font number,
type and name are maintained by the reader.
The writer finds out that a font number has been specified in the input
when its control class callback is invoked and
.I rtfMajor
\(eq
.I rtfCharAttr
and
.I rtfMinor
\(eq
.I rtfFontNum .
To obtain a pointer to the appropriate
.I RTFFont
structure, the reader function
.I RTFGetFont(rtfParam)
may be called.
.LP
\fBColor table\*-\fRFor each entry in the color table, the color number
is maintained along with the red, green and blue values.
The writer finds out that a color number has been specified in the input
when its control class callback is invoked and
.I rtfMajor
\(eq
.I rtfCharAttr
and
.I rtfMinor
\(eq
.I rtfColorNum .
To obtain a pointer to the appropriate
.I RTFColor
structure, the reader function
.I RTFGetColor(rtfParam)
may be called.
.LP
One subtle point about the built-in destination readers:
destinations cannot be recognized until
.I after
the occurrence of the ``{'' symbol that begins the destination.
This means the writer, if it maintains a state stack, will already
have pushed a state.
In order to allow the writer to properly pop that state in response
to the ``}'', these
destination readers feed the ``}'' back into the token router after
they pull it from the input stream.
What the writer actually sees is a ``{'' followed immediately by a
``}''.
.LP
Applications that maintain a state stack may find it necessary to do
something similar if they supply their own destination readers.
.NH
Programming Interface
.LP
Source files using the RTF reader should #include
.I rtf.h .
.I reader.c
should be compiled to produce
.I reader.o ,
which should be part of the final application link.
.LP
The best way to learn how these source files work is to study the sample
translators, which vary in complexity from very simple (e.g.,
.I rtf2text ,
.I rtfwc ),
to wretchedly messy (e.g.,
.I rtf2troff ).
You should be aware that one implication of the way the translators
are built (callbacks and switch statements)
is that it's quite easy to build them incrementally.
You can start with a very bare-bones model, and start plugging in
callbacks as you progress.
Within the callbacks, your switch statements can progressively handle
more cases.
.LP
An alternative approach is to start with a copy of
.I rtfskel.c ,
which includes a full set of class callbacks and complete switch statements
for all tokens.
Each case is empty; you simply add code for those cases you want to handle.
You can also rip out the code for the cases you know you'll never care about.
.NH 2
Global variables
.LP
The global RTF reader variables are:
.LP
.DS
.ta .6i 2i
int	rtfClass;	token class
int	rtfMajor;	token major number
int	rtfMinor;	token minor number
int	rtfParam;	parameter value for control symbols
char	rtfTextBuf[rtfBufSiz];	token text
int	rtfTextLen;	length of token text
.DE
.LP
These variables always apply to the token with which the writer should
be concerned.
This may be either the last token read or the current token within a
style which is being reprocessed.
.NH 2
Functions
.IS
void RTFInit ()
.IE
Initialize the RTF reader.
This is the first RTF routine that should be called.
It performs some initialization such as computing hash values for the
token lookup table and installation of the built-in destination readers.
.LP
.I RTFInit()
may be called multiple times.
Each invocation resets the reader's state completely, except that the input
stream is not disturbed.
.IS
void RTFRead ()
.IE
.I RTFRead()
calls
.I RTFGetToken()
to tokenize the input stream
and
.I RTFRouteToken()
to process each token, until input is exhausted.
When
.I RTFRead()
returns, input has been completely read and the writer can perform any
cleanup or termination needed.
.LP
If you want to read multiple files per invocation of your translator,
you should do all your setup prior to each call to
.I RTFRead() .
That is, you should call
.I RTFInit() ,
install callbacks, etc., then call
.I RTFRead() .
.IS
void RTFRouteToken ()
.IE
This routine decides what to do with the current token and routes it
to the correct place for processing.
Usually this is directly to the writer via a class callback.
The token is
.I not
passed to the writer (i.e., the class callback is bypassed) when it
is a destination token for which a reader callback is installed.
.LP
By default, built-in readers are installed
for font table, color table, stylesheet and information and picture
group destinations.
The built-in readers can be disabled
if the writer wants to see all tokens directly.
.IS
int RTFGetToken ()
.IE
Reads one token from the input stream, classifies it, sets the global
variables, and returns the class number.
If the class is
.I rtfEOF
the end of the input stream has been reached.
Newlines (``\en'') and carriage returns (``\er'')
are silently discarded by
.I RTFGetToken() ,
as they have no meaning.
Both are passed to the token hook if one is installed, however.
.LP
The sequence ``\e:'' is treated as a plain text character, with
.I rtfClass
set to
.I rtfText
and
.I rtfMajor
set to the colon ASCII code.
Strictly speaking, ``\e:'' is the control word for an index
subentry, but some versions of Microsoft Word write out plain text
colons with a preceding backslash, while others don't.
This unfortunate ambiguity results in an ugly dilemma.
It seems the lesser burden to require translators to recognize
that plain text colons should ``really'' be treated as index subentry
indicators
while inside of an index entry destination, than to recognize that an
index subentry control word should ``really'' be treated as a plain
text colon everywhere else.
.LP
Writers probably should not need to use
.I RTFGetToken()
directly unless they install their own destination readers.
One reason you might want to call it is to implement a ``peek at next token''
capability.
Call
.I RTFGetToken()
and examine the global variables.
Then call
.I RTFRouteToken()
to cause the symbol to be processed normally.
This way you get to look at the token before it goes through the usual routing
mechanism.
.IS
void RTFSetToken (class, major, minor, param, text)
int	class, major, minor, param;
char	*text;
.IE
It is sometimes useful to construct a fake token and run it through the
token router to cause the effects of the token to be applied.
.I RTFSetToken()
allows you to do this, by setting the reader's global variables to the
values supplied.
If
.I param
is non-negative, the token text
.I rtfTextBuf
is constructed from
.I text
and
.I param ,
otherwise
.I rtfTextBuf
is just copied from
.I text .
.IS
void RTFSetReadHook (f)
void	(*f) ();
.IE
Install a function to be called by
.I RTFGetToken()
after each token is read from the input stream.
The function takes no arguments and returns no value.
Within the function,
information about the current token can be obtained from the global
variables.
This function is for token examination purposes only, and should not
modify those variables.
.IS
void (*RTFGetReadHook ()) ()
.IE
Returns a pointer to the current read hook, or NULL if there isn't one.
.IS
void RTFSkipGroup ()
.IE
This function can be called to skip to the end of the current group (including
any subgroups).
It's useful for explicitly ignoring ``\e*\e\fIdest\fR'' groups, where
.I dest
is an unrecognized destination, or for causing groups that you don't want
to deal with to effectively ``disappear'' from the input stream.
.LP
Calling this function in the middle of expanding a style may cause problems.
However, it is typically called when you have just seen a destination symbol,
which won't happen during a style expansion\*-I think.
.LP
Be careful with this function if your writer maintains a state stack,
because you will already have pushed a state when the opening group
brace was seen.
After
.I RTFSkipGroup()
returns, the group closing brace has been read, and you'll need to pop
a state.
All global token variables will still be set to the closing brace, so
you may only need to call
.I RTFRouteToken()
to cause the state to be unstacked.
.IS
void RTFExpandStyle (num)
int	num;
.IE
Performs style expansion of the given style number, or does nothing
if there is no such style.
The writer should call this when it notices that the current token
is a style number indicator.
.IS
void RTFSetStream (stream)
FILE	*stream;
.IE
Redirects the RTF reader to the given stream.
This should be called before any reading is done.
The default input stream is
.I stdin .
An alternative to
.I RTFSetStream()
is to simply
.I freopen()
the input file on
.I stdin
(that's what all the sample translators do).
.LP
The input stream is
.I not
modified by
.I RTFInit() .
.IS
void RTFSetClassCallback (class, callback)
int	class;
void	(*callback) ();
.IE
Installs a writer callback function for the given token class.
The first argument is a class number, the second is the function to
call when tokens from that class are encountered in the input stream.
This will cause
.I RTFRouteToken()
to invoke the callback when it encounters a token in the class.
If
.I callback
is NULL (which is the default for all classes),
tokens in the class are ignored, i.e., discarded.
.LP
The callback should take no arguments and return no value.
Within the callback,
information about the current token can be obtained from the global
variables.
.LP
Installing a callback for the
.I rtfEOF
``class'' is silly and has no effect.
.IS
void (*RTFGetClassCallback (class)) ()
int	class;
.IE
Returns a pointer to the callback function for the given token class,
or NULL if there isn't one.
.IS
void RTFSetDestinationCallback (dest, callback)
int	dest;
void	(*callback) ();
.IE
Installs a callback function for the given destination
.I dest "" (
is a token minor number).
When
.I RTFRouteToken()
sees a token with class
.I rtfControl
and major number
.I rtfDestination ,
it checks whether there is a callback for the destination indicated by
the minor number.
If so, it invokes it.
If
.I callback
is NULL, the given destination is not treated specially (the control
class callback is invoked as usual).
By default, destination callbacks are installed for the font table, color
table, stylesheet, and information and picture group.
.LP
The callback should take no arguments and return no value.
When the functon is invoked, the current token will be the destination
token following the destination's initial opening brace ``{''.
(For optional destinations, the destination token follows the ``\e*''
symbol.)
.IS
void (*RTFGetDestinationCallback (dest)) ()
int	dest;
.IE
Returns a pointer to the callback function for the given token class,
or NULL if there isn't one.
.IS
RTFStyle *RTFGetStyle (num)
int	num;
.IE
Returns a pointer to the
.I RTFStyle
structure for the given style number.
The ``Normal'' style number is 0.
Pass \-1 to get a pointer to the first style in the list.
Styles are not stored in any particular order.
.LP
Be sure to check the result; it might be NULL.
.LP
This function is meaningless if the default stylesheet destination
reader is overridden.
.IS
RTFFont *RTFGetFont (num)
int	num;
.IE
Returns a pointer to the
.I RTFFont
structure for the given font number.
Pass \-1 to get a pointer to the first font in the list.
Fonts are not stored in any particular order.
.LP
Be sure to check the result; it might be NULL.
In particular, you might think that passing the number specified with
the ``\edeff'' (default font) control symbol would always yield a
valid font structure, but that's not true.
The default font might not be listed in the font table.
.LP
This function is meaningless if the default font table destination
reader is overridden.
.IS
RTFColor *RTFGetColor (num)
int	num;
.IE
Returns a pointer to the
.I RTFColor
structure for the given color number.
(I think black is 0.)
Pass \-1 to get a pointer to the first color in the list.
Colors are not stored in any particular order.
If the color values in the entry are \-1, the default color should be used.
The default color is writer-dependent.
.LP
Be sure to check the result; it might be NULL.
I think this means you should use the default color.
.LP
This function is meaningless if the default color table destination
reader is overridden.
.IS
int RTFCheckCM (class, major)
int	class, major;
.IE
Returns non-zero if
.I rtfClass
and
.I rtfMajor
are equal to
.I class
and
.I major ,
respectively, zero otherwise.
.IS
int RTFCheckCMM (class, major, minor)
int	class, major, minor;
.IE
Returns non-zero if
.I rtfClass ,
.I rtfMajor
and
.I rtfMinor
are equal to
.I class ,
.I major
and
.I minor ,
respectively, zero otherwise.
.IS
int RTFCheckMM (major, minor)
int	major, minor;
.IE
Returns non-zero if
.I rtfMajor
and
.I rtfMinor
are equal to
.I major
and
.I minor ,
respectively, zero otherwise.
.IS
char *RTFAlloc (size)
int	size;
.IE
Returns a pointer to a block of memory
.I size
bytes long, or NULL if insufficient memory was available.
.IS
char *RTFStrSave (s)
char	*s;
.IE
Allocates a block of memory big enough for a copy of the given string
(including terminating null byte), copies the string into it, and returns
a pointer to the copy.
Returns NULL if insufficient memory was available.
.IS
void RTFFree (p)
char	*p;
.IE
Frees the block of memory pointed to by
.I p ,
which should have been allocated by
.I RTFAlloc()
or
.I RTFStrSave() .
It is safe to pass NULL to this routine.
.NH
Distribution Availability
.LP
This software may be redistributed without restriction and used for
any purpose whatsoever.
.LP
The RTF distribution is available for anonymous 
.I ftp
access in the
.I ~ftp/pub/RTF
directory on host
.I indri.primate.wisc.edu
(Internet address 128.104.230.11).
Updates appear there as they become available.
.LP
A version of the RTF specification is available in this directory,
as either a binhex'ed Word for Macintosh document, or in RTF format.
It is known to have a few errors, as it's a scanned version of a paper
copy.
Some of these errors have been fixed, but others remain (see, for instance,
the example table text on page 17).
The document is not quite as up to date as the one sent out by Microsoft,
but it is much more complete
than the one beginning ``Specification for RTF'' that may be found on
some other archive sites.
.LP
If you do not have Internet access, send a request to one of the following:
.DS
.ta 1.2i
Internet:	software-request@primate.wisc.edu
UUCP:		rhesus!software-request
.DE
Bug reports and questions should be sent to one of these addresses as
well.
.LP
If you use this software as the basis for a translater not included in
the current collection, please consider contributing it for inclusion
in a future distribution.
In particular, an RTF-to-LaTeX translator seems to be an item of interest.
I don't use LaTeX myself and am unlikely to write one, but it would probably
be fairly popular.