This is rtf2TeX.c in view mode; [Download] [Up]
/*
* An output filter to produce TeX using Paul DuBois' RTF reader
*
* Written and copyright (c) 1991 by Robert Lupton (rhl@astro.princeton.edu)
* Permission is granted to freely distribute and modify this code, providing:
* 1/ This copyright notice is preserved
* 2/ You send me a copy of any changes for inclusion in a future release
*/
#include <stdio.h>
#include <ctype.h>
#include "rtf.h"
#include "fonts.h"
#include "AA_version.h"
#ifdef __STDC__ /* Convert to a string */
# define STR(S) #S /* ANSI */
#else
# define STR(S) "S" /* often works... */
#endif
#define TW_TO_PT(I) ((I)/20.0) /* convert twips to points */
#define TW_TO_IN(I) ((I)/(72*20.0)) /* convert twips to inches. Note that*/
#define IN_TO_TW(I) ((float)(I*72*20)) /* RTF assumes 1" = 72pt, not 72.27 */
static void UnknownClass();
static void GroupClass();
static void TblAttr();
static void TextClass();
static void CharSet();
static void ControlClass();
static void Destination();
static void SpecialChar();
static void DocAttr();
static void SectAttr();
static void ParAttr();
static void CharAttr();
static void PictAttr();
static void FieldAttr();
static void TOCAttr();
static void PosAttr();
/*****************************************************************************/
/*
* The flag values for the RTF and TeX group stacks
*/
#define Undefined 0 /* TeX type */
#define Math 1
#define Font 2
#define Font_Num 3
#define Font_Size 4
#define Style 5
#define Par_Attr 6
#define Footnote 7
#define Start_Para 8
#define Sub_Super 9
#define Plain 1 /* RTF and TeX flags if(Font) */
#define Bold 2
#define Italic 3
#define Outlined 4
#define Shadow 5
#define SmallCaps 6
#define AllCaps 7
#define StrikeThru 8
#define Invisible 9
#define LeftAlign 01 /* RTF and TeX flags if(Par_Attr) */
#define RightAlign 02
#define Centred 04
#define Pageno_Decimal 1 /* pagenumber styles */
#define Pageno_LRoman 2
#define Pageno_URoman 3
/*
* First the TeX stack, used to remember what needs to be written as
* we close groups (including math groups)
*/
typedef struct tex_group {
char *str; /* what needs inserting */
long type; /* what type it is */
long flags; /* details of the type */
long saved; /* save the old value of something */
struct tex_group *prev;
} TEX_STACK;
static int pop_TeX_stack(); /* pop the TeX stack */
static void push_TeX_stack(); /* push the TeX stack */
static int top_TeX_flags(); /* return the flags of the stack top */
/*****************************************************************************/
/*
* And then RTF grouping stuff
*/
typedef struct {
long font; /* which font */
int FontNum;
int FontType;
int FontSize;
} CHAR_ATTR;
typedef struct {
long flags; /* centering, justification, etc. */
int parindent; /* \fi to RTF */
int leftskip,rightskip; /* \li/\ri to RTF */
int parskip; /* the usual TeX parskip */
int skip_before,skip_after; /* \sb/\sa to RTF */
int parskip_b,parskip_a; /* parts of skip_before/after
assigned to parskip */
} PAR_ATTR;
typedef struct status {
TEX_STACK *TeX_stack; /* stack of TeX stuff to output */
CHAR_ATTR char_attr; /* Character attributes */
PAR_ATTR par_attr; /* Paragraph attributes */
int style; /* current style (if relevant) */
int sub_super_height; /* vertical offset of text */
struct status *prev;
} RTF_STACK;
static CHAR_ATTR char_attr = {
Plain, /* font */
-1, /* FontNum */
0, /* FontType */
0, /* FontSize */
};
static PAR_ATTR par_attr = {
0,
0, /* parindent */
0, 0, /* left/rightskip */
0, 0, /* skip_before/after */
0, 0, /* parskip_b/a */
};
static RTF_STACK *rtf_ptr;
static RTF_STACK rtf_current; /* current values of things that
get pushed on rtf stack */
static RTF_STACK rtf_default; /* default values of things that
live on the rtf stack */
/*****************************************************************************/
/*
* Document attributes, not subject to grouping
*/
static int pageno = 1;
static int pageno_style = Pageno_Decimal;
static int pageno_x = 720; /* 0.5" */
static int pageno_y = 720; /* 0.5" */
static int lineno = 1;
static int paper_width = 12240; /* 8.5" */
static int paper_height = 15840; /* 11" */
static int left_margin = 1800; /* 1.25" */
static int right_margin = 1800; /* 1.25" */
static int top_margin = 1440; /* 1.5" */
static int bottom_margin = 1440; /* 1.5" */
/*****************************************************************************/
/*
* Tab things
*/
#define NTABS 20 /* maximum number of tabs */
#define TabLeft 0 /* values for type */
#define TabCentre 1
#define TabRight 2
#define TabDecimal 3
static struct {
int pos; /* positions of tabstops in twips */
int type; /* type of centering */
} tabstops[NTABS], /* the current values */
old_tabstops[NTABS]; /* and the old ones */
static int ignore_tabwidths = 0; /* ignore the positions of tabs */
static int ntabs = 0; /* number of tabstops set */
static int old_ntabs = 0; /* number of tabstops in old_tapstops*/
static tab_num = 0; /* the number of the next tab */
/*****************************************************************************/
static void end_para(); /* print the end-of-para string */
static void end_table(); /* end a table */
static void flush(); /* flush output()'s output */
static void msg_map_to(); /* treat one keyword as another */
static void msg_not_needed(); /* TeX doesn't need this keyword */
static void msg_not_supported(); /* TeX can't use this keyword */
static void msg_not_yet(); /* Not yet supported */
static void in_math(); /* output must be in math mode */
static void initialise(); /* initialise the document */
static void output(); /* the write-a-character function */
static void output_str(); /* output a string */
static void output_endline(); /* end a line if not at eol */
static void output_8bit(); /* output a char with 8th bit set */
static char *page_num(); /* a string giving the current pageno*/
static void pop_rtf_group(); /* pop the status group */
static void print_text(); /* print some text (char *text[]) */
static void push_rtf_group(); /* push the status group */
RTFFuncPtr default_read_font = NULL; /* default func to read style table */
static void read_font(); /* read the font table */
static void read_pict(); /* read an rtfPict destination */
RTFFuncPtr default_read_style = NULL; /* default func to read style table */
static void read_style(); /* read the style table */
static void send(); /* actually send a string to output */
static void set_font(); /* switch to a new font */
static void set_if_invalid(); /* set invalid elements from template*/
static void set_headfoot_lines(); /* set \head/footline */
static void start_para(); /* Called at the start of each para */
static void start_table(); /* start a table */
static int tabs_changed(); /* have the tabstops been changed? */
static char *TeX_name(); /* remove spaces from a word */
static void update_current(); /* update the current state */
static void usage(); /* print a helpful message */
static char buff[100]; /* temporary scratch space */
static int change_headfoot = 1; /* change the \head/footline? */
static int default_font = -1; /* The default font */
static int end_of_par = 0; /* we just saw rtfPar */
static int expand_styles = 1; /* allow the reader to expand \s#? */
static int footnote_num0 = 1; /* the starting footnote number */
static int footnotes_restart_each_page = 1; /* as it says */
static int halign_tables = 0; /* generate \halign tables */
static char *include_file = NULL; /* file of TeX definitions to include*/
static int in_table = 0; /* are we in a table? */
static int initialised = 0; /* have we called initialise() yet? */
static int math_mode = 0; /* In math mode? */
static int no_cleanup = 0; /* don't try to cleanup output */
static int no_grouping = 0; /* don't allow any TeX font grouping */
static int rtf_group = 0; /* level of RTF grouping */
static int text_out = 0; /* we are actually writing text */
static int TeX_group = 0; /* level of RTF grouping */
static int verbose = 0;
static int writing_defs = 0; /* are we writing macro definitions? */
/*****************************************************************************/
int
main(ac,av)
int ac;
char *av[];
{
RTF_STACK rtf_initial; /* initial values of things that
on rtf stack */
FILE *fil = NULL; /* input stream */
static char *header[] = {
"%",
"% Converted from RTF format using rtf2TeX",
"% Comments and bugs to Robert Lupton (rhl@astro.princeton.edu)",
"%",
NULL,
};
while(ac > 1 && (av[1][0] == '-' || av[1][0] == '+')) {
switch (av[1][1]) {
case 'c':
no_cleanup = (av[1][0] == '+' ? 1 : 0);
break;
case 'h':
usage();
exit(0);
case 'i': /* include a TeX file */
if(ac <= 1) {
fprintf(stderr,"You must specify a filename with -i\n");
usage();
exit(0);
}
include_file = av[2];
av++; ac--;
break;
case 's': /* don't do style expansion */
expand_styles = (av[1][0] == '+' ? 1 : 0);
break;
case 't': /* allow \halign tables */
halign_tables = (av[1][0] == '+' ? 1 : 0);
break;
case 'T': /* ignore given tab positions */
ignore_tabwidths = (av[1][0] == '-' ? 1 : 0);
break;
case 'v':
verbose = (av[1][2] == '\0' ? 1 : atoi(&av[1][2]));
break;
case 'V':
fprintf(stderr,"RTF: %s\n",version);
exit(0);
break;
default:
fprintf(stderr,"Unknown option %s\n",av[1]);
break;
}
ac--;
av++;
}
if(ac > 1) {
if((fil = fopen(av[1],"r")) == NULL) {
fprintf(stderr,"Can't open %s\n",av[1]);
exit(1);
}
RTFSetStream(fil);
}
rtf_default.TeX_stack = NULL; /* the rtf stack */
rtf_default.char_attr = char_attr;
rtf_default.par_attr = par_attr;
rtf_default.style = -1;
rtf_default.sub_super_height = 0;
rtf_default.prev = NULL;
rtf_current = rtf_initial = rtf_default;
rtf_ptr = &rtf_initial;
RTFInit();
print_text(header,stdout);
if(!expand_styles) {
default_read_style = RTFGetDestinationCallback(rtfStyleSheet);
RTFSetDestinationCallback(rtfStyleSheet,read_style);
}
default_read_font = RTFGetDestinationCallback(rtfFontTbl);
RTFSetDestinationCallback(rtfFontTbl,read_font);
(void)RTFSetClassCallback(rtfUnknown, UnknownClass);
(void)RTFSetClassCallback(rtfGroup, GroupClass);
(void)RTFSetClassCallback(rtfText, start_para);
(void)RTFSetClassCallback(rtfControl, ControlClass);
(void)RTFSetDestinationCallback(rtfPict, read_pict);
RTFRead();
if(rtf_group != 0) {
fprintf(stderr,"End of file is in an unclosed RTF group (level %d)\n",
rtf_group);
}
if(TeX_group != 0) {
fprintf(stderr,"End of file is in an unclosed TeX group (level %d)\n",
TeX_group);
}
if(in_table) {
output_str("}",'\n');
}
output_endline();
output_str("\n\\bye\n",0);
flush();
if(fil != NULL) fclose(fil);
return(0);
}
/*****************************************************************************/
/*
* Token class callbacks
*/
static void
UnknownClass()
{
fprintf(stderr,"Unknown Token: %s\n",rtfTextBuf);
}
/*****************************************************************************/
static void
GroupClass()
{
switch (rtfMajor) {
case rtfBeginGroup:
if(initialised && !text_out) {
start_para();
}
push_rtf_group();
rtf_group++;
break;
case rtfEndGroup:
if(--rtf_group == -1) {
fprintf(stderr,"Unbalanced group\n");
} else {
while(pop_TeX_stack()) continue;
pop_rtf_group();
if(end_of_par) {
end_para();
}
}
break;
}
}
/*****************************************************************************/
/*
* This should be called after the header material has all been read.
*/
static void
initialise()
{
if(include_file != NULL) {
sprintf(buff,"\\input %s",include_file);
output_str(buff,'\n');
}
if(ignore_tabwidths) {
output_str("\\tabskip=10pt plus 10pt minus 2pt",'\n');
}
output_str("%",'\n');
sprintf(buff,"\\hsize=%gin",
TW_TO_IN(paper_width - left_margin - right_margin));
output_str(buff,'\n');
sprintf(buff,"\\vsize=%gin",
TW_TO_IN(paper_height - top_margin - bottom_margin));
output_str(buff,'\n');
/* TeX takes the origin to be (1,1)" */
if(left_margin != IN_TO_TW(1.0)) {
sprintf(buff,"\\hoffset=%gin",TW_TO_IN(left_margin) - 1.0);
output_str(buff,'\n');
}
if(top_margin != IN_TO_TW(1.0)) {
sprintf(buff,"\\voffset=%gin",TW_TO_IN(top_margin) - 1.0);
output_str(buff,'\n');
}
output_str("\\parindent=0pt",'\n');
sprintf(buff,"\\newcount\\footnum\\footnum=%d",footnote_num0);
output_str(buff,'\n');
if(pageno != 1) {
sprintf(buff,"\\pageno=%d",pageno);
output_str(buff,'\n');
}
output_str("%",'\n');
initialised = 1; /* don't do it twice */
}
/*****************************************************************************/
static void
TextClass()
{
output(rtfMajor,1);
}
/*****************************************************************************/
/*
* Process control symbol.
*/
static void
ControlClass()
{
switch (rtfMajor) {
case rtfVersion:
if(verbose) fprintf(stderr,"RTF version %d\n",rtfParam);
break;
case rtfDefFont:
default_font = rtfParam; /* this may not be in the font table */
break;
case rtfCharSet:
CharSet ();
break;
case rtfDestination:
Destination();
break;
case rtfFontFamily: /* only occurs within font table */
fprintf(stderr,"You shouldn't see rtfFontType: minor %d\n",rtfMinor);
break;
case rtfColorName: /* only occurs within color table */
fprintf(stderr,"You shouldn't see rtfColorName: minor %d\n",rtfMinor);
break;
case rtfStyleAttr: /* only occurs within stylesheet */
switch (rtfMinor) {
case rtfBasedOn:
if(rtfParam != rtfNoParam && expand_styles) {
static int count = 0;
if(verbose > 1 || (verbose && count++ == 0)) {
msg_not_yet("sbasedon");
}
}
break;
case rtfNext:
if(rtfParam != rtfNoParam) {
static int count = 0;
if(verbose > 1 || (verbose && count++ == 0)) {
msg_not_yet("snext");
}
}
break;
default:
fprintf(stderr,"Illegal minor number for StyleAttr: %d\n",rtfMinor);
break;
}
break;
case rtfSpecialChar:
SpecialChar();
break;
case rtfDocAttr:
DocAttr();
break;
case rtfSectAttr:
SectAttr();
break;
case rtfTblAttr:
TblAttr();
break;
case rtfParAttr:
ParAttr();
break;
case rtfCharAttr:
CharAttr();
break;
case rtfPictAttr:
fprintf(stderr,"You shouldn't see rtfPictAttr: minor %d\n",rtfMinor);
break;
case rtfFieldAttr:
FieldAttr();
break;
case rtfTOCAttr:
TOCAttr();
break;
case rtfPosAttr:
PosAttr();
break;
}
}
/*****************************************************************************/
/*
* Control class major number handlers. Each one switches on the
* minor numbers that occur within the major number. rtfStyleSheet,
* rtfFontTbl, and rtfColorTbl are not in the switch because they're
* handled by the reader. rtfPict has its own callback.
*/
static void
CharSet()
{
switch (rtfMinor) {
case rtfAnsiCharSet:
break;
case rtfMacCharSet:
break;
case rtfPcCharSet:
break;
case rtfPcaCharSet:
break;
}
}
static void
Destination()
{
if(math_mode) { /* we were in math before beginning
this destination group */
RTF_STACK *save = rtf_ptr;
rtf_ptr = rtf_ptr->prev;
while(!top_TeX_flags(Math)) {
if(pop_TeX_stack() == 0) { /* stack is empty. */
if(verbose) {
fprintf(stderr,"Failed to find end of math group\n");
}
flush(); break;
}
}
pop_TeX_stack(); /* pop off the $ too */
save->TeX_stack = rtf_ptr->TeX_stack;
rtf_ptr = save;
}
switch (rtfMinor) {
case rtfFootnote:
output_str("\\footnote{}{",'\0');
push_TeX_stack("\\global\\advance\\footnum by 1}",Footnote,0);
break;
case rtfHeader:
msg_not_yet("header");
RTFSkipGroup();
RTFUngetToken();
break;
case rtfHeaderLeft:
break;
case rtfHeaderRight:
break;
case rtfHeaderFirst:
break;
case rtfFooter:
break;
case rtfFooterLeft:
break;
case rtfFooterRight:
break;
case rtfFooterFirst:
break;
case rtfFNSep:
break;
case rtfFNContSep:
break;
case rtfFNContNotice:
break;
case rtfInfo:
break;
case rtfField:
break;
case rtfFieldInst:
break;
case rtfFieldResult:
break;
case rtfIndex:
break;
case rtfIndexBold:
break;
case rtfIndexItalic:
break;
case rtfIndexText:
break;
case rtfIndexRange:
break;
case rtfTOC:
break;
case rtfBookmarkStart:
break;
case rtfBookmarkEnd:
break;
case rtfITitle:
break;
case rtfISubject:
break;
case rtfIAuthor:
break;
case rtfIOperator:
break;
case rtfIKeywords:
break;
case rtfIComment:
break;
case rtfIVersion:
break;
case rtfIDoccomm:
break;
}
}
static void
SpecialChar()
{
switch (rtfMinor) {
case rtfCurHeadPage:
output_str(page_num(),' ');
break;
case rtfCurFNote:
output_str("\\the\\footnum",' ');
break;
case rtfCurHeadPict:
msg_not_supported("chpict");
break;
case rtfCurHeadDate:
output_str("\\date",' ');
break;
case rtfCurHeadTime:
output_str("\\timestr",' ');
break;
case rtfFormula:
msg_not_yet("|");
break;
case rtfNoBrkSpace:
if(!text_out) start_para();
output_str("~",'\0');
break;
case rtfNoReqHyphen:
output_str("\\-",'\0');
break;
case rtfNoBrkHyphen:
if(!text_out) start_para();
output('-',1);
break;
case rtfPage:
end_para();
output_str("\\vfil\\eject",'\n');
break;
case rtfLine:
output_str("\\hfil\\break",'\n');
break;
case rtfPar: /* we may want to deal with this
elsewhere, so as to pop the stacks
before printing the newline */
RTFGetToken();
if(RTFCheckCM(rtfGroup,rtfEndGroup) ||
RTFCheckCMM(rtfControl,rtfParAttr,rtfParDef)) {
if(rtf_ptr->par_attr.skip_after != 0) {
sprintf(buff,"\\vskip%gpt",TW_TO_PT(rtf_ptr->par_attr.skip_after));
output_str(buff,' ');
}
end_of_par = 1;
} else if(in_table && RTFCheckCMM(rtfControl,rtfSpecialChar,rtfPar)) {
end_table(); /* two \par's in a row */
} else {
end_para();
}
RTFUngetToken();
break;
case rtfSect:
rtfMinor = rtfPar; /* pretend that it's just a para */
RTFUngetToken();
break;
case rtfTab:
if(!text_out) start_para();
if(!halign_tables || ntabs == 0) {
output_str("\\qquad",' ');
} else {
if(!in_table) {
start_table();
}
if(tab_num++ > 0) {
output_str(" &",' ');
}
}
break;
case rtfCell:
break;
case rtfRow:
break;
case rtfCurAnnot:
break;
case rtfAnnotation:
break;
case rtfAnnotID:
break;
case rtfCurAnnotRef:
break;
case rtfFNoteSep:
break;
case rtfFNoteCont:
break;
case rtfColumn:
break;
case rtfOptDest:
break;
case rtfIIntVersion:
break;
case rtfICreateTime:
break;
case rtfIRevisionTime:
break;
case rtfIPrintTime:
break;
case rtfIBackupTime:
break;
case rtfIEditTime:
break;
case rtfIYear:
break;
case rtfIMonth:
break;
case rtfIDay:
break;
case rtfIHour:
break;
case rtfIMinute:
break;
case rtfINPages:
break;
case rtfINWords:
break;
case rtfINChars:
break;
case rtfIIntID:
break;
}
}
static void
DocAttr()
{
switch (rtfMinor) {
case rtfPaperWidth:
paper_width = rtfParam;
break;
case rtfPaperHeight:
paper_height = rtfParam;
break;
case rtfLeftMargin:
left_margin = rtfParam;
break;
case rtfRightMargin:
right_margin = rtfParam;
break;
case rtfTopMargin:
top_margin = rtfParam;
break;
case rtfBottomMargin:
bottom_margin = rtfParam;
break;
case rtfFacingPage:
msg_not_yet("facingp");
break;
case rtfGutterWid:
msg_not_yet("gutter");
break;
case rtfDefTab:
msg_not_yet("deftab");
break;
case rtfWidowCtrl:
msg_not_yet("widowctrl");
break;
case rtfFNoteEndSect:
msg_not_yet("endnotes");
break;
case rtfFNoteEndDoc:
break;
case rtfFNoteBottom:
break;
case rtfFNoteText:
msg_not_yet("ftntj");
break;
case rtfFNoteStart:
if(footnote_num0 != rtfParam) {
footnote_num0 = rtfParam;
change_headfoot = 1;
}
break;
case rtfFNoteRestart:
if(footnotes_restart_each_page != 1) {
footnotes_restart_each_page = 1;
change_headfoot = 1;
}
break;
case rtfHyphHotZone:
break;
case rtfPageStart:
pageno = rtfParam;
break;
case rtfLineStart:
lineno = rtfParam;
break;
case rtfLandscape:
msg_not_supported("landscape");
break;
case rtfFracWidth:
break;
case rtfNextFile:
break;
case rtfTemplate:
break;
case rtfMakeBackup:
break;
case rtfRTFDefault:
break;
case rtfRevisions:
break;
case rtfMirrorMargin:
break;
case rtfRevDisplay:
break;
case rtfRevBar:
break;
}
}
/*****************************************************************************/
static void
SectAttr()
{
switch (rtfMinor) {
case rtfSectDef:
set_headfoot_lines();
break;
case rtfNoBreak:
break;
case rtfColBreak:
break;
case rtfPageBreak:
break;
case rtfEvenBreak:
break;
case rtfOddBreak:
break;
case rtfPageStarts:
break;
case rtfPageCont:
break;
case rtfPageRestart:
break;
case rtfPageDecimal:
if(pageno_style != Pageno_Decimal) {
pageno_style = Pageno_Decimal;
change_headfoot = 1;
}
break;
case rtfPageURoman:
if(pageno_style != Pageno_URoman) {
pageno_style = Pageno_URoman;
change_headfoot = 1;
}
break;
case rtfPageLRoman:
if(pageno_style != Pageno_LRoman) {
pageno_style = Pageno_LRoman;
change_headfoot = 1;
}
break;
case rtfPageULetter:
msg_not_yet("pgnucltr");
break;
case rtfPageLLetter:
msg_not_yet("pgnlcltr");
break;
case rtfPageNumLeft:
if(pageno_x != rtfParam) {
pageno_x = rtfParam;
change_headfoot = 1;
}
break;
case rtfPageNumTop:
if(pageno_y != rtfParam) {
pageno_y = rtfParam;
change_headfoot = 1;
}
break;
case rtfLineModulus:
if(rtfParam != 0) msg_not_supported("linemod");
break;
case rtfLineStarts:
break;
case rtfLineDist:
msg_not_supported("linex");
break;
case rtfLineRestart:
msg_not_supported("linerestart");
break;
case rtfLineRestartPg:
msg_not_supported("lineppage");
break;
case rtfLineCont:
msg_not_supported("linecont");
break;
case rtfHeaderY:
msg_not_yet("headery");
break;
case rtfFooterY:
msg_not_yet("footery");
break;
case rtfTopVAlign:
break;
case rtfBottomVAlign:
break;
case rtfCenterVAlign:
break;
case rtfJustVAlign:
break;
case rtfColumns:
if(rtfParam != 1) msg_not_yet("cols");
break;
case rtfColumnLine:
break;
case rtfColumnSpace:
msg_not_yet("colsx");
break;
case rtfENoteHere:
msg_not_supported("endnhere");
break;
case rtfTitleSpecial:
msg_not_supported("titlepg");
break;
}
}
static void
TblAttr()
{
switch (rtfMinor) {
case rtfCellBordBottom:
break;
case rtfCellBordTop:
break;
case rtfCellBordLeft:
break;
case rtfCellBordRight:
break;
case rtfRowDef:
break;
case rtfRowLeft:
break;
case rtfRowRight:
break;
case rtfRowCenter:
break;
case rtfRowGapH:
break;
case rtfRowHt:
break;
case rtfRowLeftEdge:
break;
case rtfCellPos:
break;
case rtfMergeRngFirst:
break;
case rtfMergePrevious:
break;
}
}
/*****************************************************************************/
static void
ParAttr()
{
switch (rtfMinor) {
case rtfParDef:
if(!initialised) { /* it's hard to know where to call it*/
initialise();
}
while(top_TeX_flags(Font) || top_TeX_flags(Font_Num) ||
top_TeX_flags(Font_Size) || top_TeX_flags(Style) ||
top_TeX_flags(Undefined)) {
(void)pop_TeX_stack();
}
if(end_of_par) {
end_para();
}
ntabs = 0;
rtf_default.TeX_stack = rtf_ptr->TeX_stack;
rtf_default.prev = rtf_ptr->prev;
*rtf_ptr = rtf_default;
break;
case rtfStyleNum:
if(expand_styles) {
RTFExpandStyle(rtfParam);
break;
}
rtf_ptr->style = rtfParam;
if(rtf_default.style == -1) { /* no default style is installed */
if(TeX_group == 0 && rtf_group <= 1) {
rtf_default.style = rtf_ptr->style;
}
}
if(!initialised) { /* a plausible place to try this */
initialise();
}
break;
case rtfQuadLeft:
{
static int count = 0;
if(verbose > 1 || (verbose && count++ == 0)) {
msg_not_yet("ql");
}
rtf_ptr->par_attr.flags |= LeftAlign;
}
break;
case rtfQuadRight:
{
static int count = 0;
if(verbose > 1 || (verbose && count++ == 0)) {
msg_not_yet("qr");
}
rtf_ptr->par_attr.flags |= RightAlign;
}
break;
case rtfQuadJust:
rtf_ptr->par_attr.flags &= ~(LeftAlign | Centred | RightAlign);
break;
case rtfQuadCenter:
rtf_ptr->par_attr.flags |= Centred;
break;
case rtfFirstIndent:
rtf_ptr->par_attr.parindent = rtfParam;
break;
case rtfLeftIndent:
rtf_ptr->par_attr.leftskip = rtfParam;
break;
case rtfRightIndent:
rtf_ptr->par_attr.rightskip = rtfParam;
break;
case rtfSpaceBefore:
if(TeX_group == 0 && rtf_group <= 1) { /* change parskip */
rtf_ptr->par_attr.parskip = rtfParam +
rtf_ptr->par_attr.skip_after + rtf_ptr->par_attr.parskip_a;
rtf_ptr->par_attr.parskip_b = rtfParam;
}
rtf_ptr->par_attr.skip_before = rtfParam - rtf_ptr->par_attr.parskip_b;
break;
case rtfSpaceAfter:
if(TeX_group == 0 && rtf_group <= 1) { /* change parskip */
rtf_ptr->par_attr.parskip = rtfParam +
rtf_ptr->par_attr.skip_before + rtf_ptr->par_attr.parskip_b;
rtf_ptr->par_attr.parskip_a = rtfParam;
}
rtf_ptr->par_attr.skip_after = rtfParam - rtf_ptr->par_attr.parskip_a;
break;
case rtfSpaceBetween:
break;
case rtfInTable:
break;
case rtfKeep:
break;
case rtfKeepNext:
break;
case rtfSideBySide:
break;
case rtfPBBefore:
break;
case rtfNoLineNum: /* ignored */
break;
case rtfBorderTop:
break;
case rtfBorderBottom:
break;
case rtfBorderLeft:
break;
case rtfBorderRight:
break;
case rtfBorderBar:
break;
case rtfBorderBox:
break;
case rtfBorderBetween:
break;
case rtfBorderSingle:
break;
case rtfBorderThick:
break;
case rtfBorderShadow:
break;
case rtfBorderDouble:
break;
case rtfBorderDot:
break;
case rtfBorderHair:
break;
case rtfBorderSpace:
break;
case rtfTabPos:
if(ntabs >= NTABS) {
if(ntabs == NTABS)
fprintf(stderr,"Attempt to set more than %d tabs\n",NTABS);
} else {
tabstops[ntabs++].pos = rtfParam;
tabstops[ntabs].type = TabLeft;
}
break;
case rtfTabRight:
if(ntabs < NTABS) {
tabstops[ntabs].type = TabRight;
}
break;
case rtfTabCenter:
if(ntabs < NTABS) {
tabstops[ntabs].type = TabCentre;
}
break;
case rtfTabDecimal:
if(ntabs < NTABS) {
tabstops[ntabs].type = TabDecimal;
}
break;
case rtfTabBar:
{
static int count = 0;
if(verbose > 1 || (verbose && count == 0))
msg_not_yet("tb");
}
break;
case rtfLeaderDot:
break;
case rtfLeaderHyphen:
break;
case rtfLeaderUnder:
break;
case rtfLeaderThick:
break;
}
}
static void PictAttr ()
{
switch (rtfMinor) {
case rtfMacQD:
break;
case rtfWinMetafile:
break;
case rtfWinBitmap:
break;
case rtfPicWid:
break;
case rtfPicHt:
break;
case rtfPicGoalWid:
break;
case rtfPicGoalHt:
break;
case rtfPicScaleX:
break;
case rtfPicScaleY:
break;
case rtfPicScaled:
break;
case rtfPicCropTop:
break;
case rtfPicCropBottom:
break;
case rtfPicCropLeft:
break;
case rtfPicCropRight:
break;
case rtfPixelBits:
break;
case rtfBitmapPlanes:
break;
case rtfBitmapWid:
break;
case rtfPicBinary:
break;
}
}
/*****************************************************************************/
static void
FieldAttr()
{
switch (rtfMinor) {
case rtfFieldDirty:
break;
case rtfFieldEdited:
break;
case rtfFieldLocked:
break;
case rtfFieldPrivate:
break;
}
}
/*****************************************************************************/
static void
TOCAttr()
{
switch (rtfMinor) {
case rtfTOCType:
break;
case rtfTOCLevel:
break;
}
}
/*****************************************************************************/
static void
PosAttr()
{
switch (rtfMinor) {
case rtfPosX:
break;
case rtfPosXCenter:
break;
case rtfPosXInside:
break;
case rtfPosXLeft:
break;
case rtfPosXOutSide:
break;
case rtfPosXRight:
break;
case rtfPosY:
break;
case rtfPosYInline:
break;
case rtfPosYTop:
break;
case rtfPosYCenter:
break;
case rtfPosYBottom:
break;
case rtfAbsWid:
break;
case rtfTextDist:
break;
case rtfRPosMargV:
break;
case rtfRPosPageV:
break;
case rtfRPosMargH:
break;
case rtfRPosPageH:
break;
case rtfRPosColH:
break;
}
}
/*****************************************************************************/
/*
* Deal with Pict destinations
*/
static void
read_pict()
{
float width,height; /* size of picture in pts. */
int pic_width = -1;
int pic_height = -1;
int pic_widthG = -1;
int pic_heightG = -1;
int num_byte = -1;
while(!(RTFGetToken() == rtfGroup && rtfMajor == rtfEndGroup)) {
switch (rtfClass) {
case rtfText:
break;
case rtfControl:
switch (rtfMajor) {
case rtfPictAttr:
switch (rtfMinor) {
case rtfPicWid:
pic_width = rtfParam;
break;
case rtfPicHt:
pic_height = rtfParam;
break;
case rtfPicGoalWid:
pic_widthG = rtfParam;
break;
case rtfPicGoalHt:
pic_heightG = rtfParam;
break;
case rtfMacQD:
break;
case rtfWinMetafile:
break;
case rtfWinBitmap:
break;
case rtfPicScaleX:
break;
case rtfPicScaleY:
break;
case rtfPicScaled:
break;
case rtfPicCropTop:
break;
case rtfPicCropBottom:
break;
case rtfPicCropLeft:
break;
case rtfPicCropRight:
break;
case rtfPixelBits:
break;
case rtfBitmapPlanes:
break;
case rtfBitmapWid:
break;
case rtfPicBinary:
break;
}
}
break;
default:
fprintf(stderr,"Unknown Token in a Pict: %s\n",rtfTextBuf);
break;
}
}
RTFUngetToken(); /* push the } back */
if(pic_heightG >= 0) {
height = TW_TO_PT(pic_heightG);
} else if(pic_height >= 0) {
height = pic_height; /* assume 1pt per pixel */
} else {
height = 10;
}
if(pic_widthG >= 0) {
width = TW_TO_PT(pic_widthG);
} else if(width >= 0) {
width = pic_width; /* assume 1pt per pixel */
} else {
width = 10;
}
sprintf(buff,"\\pict{%gpt}{%gpt}",width,height);
output_str(buff,' ');
}
/*****************************************************************************/
/*
* Deal with things like footnote numbering and pagenumbers; things
* that involve the headline or footline
*/
static void
set_headfoot_lines()
{
int pageno_top = (pageno_y < paper_height/2 ? 1 : 0);
if(!change_headfoot) return;
/*
* The headline first
*/
output_str("\\headline={\\tenrm ",'\0');
if(footnotes_restart_each_page) {
sprintf(buff,"\\footnum=%d",footnote_num0);
output_str(buff,' ');
}
if(pageno_top) {
sprintf(buff,"\\kern %gpt %s\\hfil",TW_TO_PT(pageno_x),page_num());
output_str(buff,' ');
}
output_str("}",'\n');
/*
* And now the foot
*/
output_str("\\footline={\\tenrm ",'\0');
if(!pageno_top) {
sprintf(buff,"\\kern %gpt %s\\hfil",TW_TO_PT(pageno_x),page_num());
output_str(buff,' ');
}
output_str("}",'\n');
change_headfoot = 0; /* we've done it */
}
/*****************************************************************************/
/*
* Convert the pageno to the desired form
*/
static char *
page_num()
{
switch (pageno_style) {
case Pageno_Decimal:
return("\\number\\pageno");
case Pageno_LRoman:
return("\\romannumeral\\pageno");
case Pageno_URoman:
return("\\uppercase\\expandafter{\\romannumeral\\pageno}");
default:
return("");
}
}
/*****************************************************************************/
/*
* This is called when we see the first text token after each \par
*/
#define CURRENT(WHAT) /* is WHAT up-to-date? */ \
(rtf_current.WHAT == rtf_ptr->WHAT)
#define SET_PAR_ATTR(PARAM) /* set PARAM if it needs it */ \
if(!CURRENT(par_attr.PARAM)) { \
sprintf(buff,"\\%s=%gpt",STR(PARAM),TW_TO_PT(rtf_ptr->par_attr.PARAM)); \
output_str(buff,' '); \
}
static void
start_para()
{
if(!initialised) { /* this could be the place */
initialise();
}
if(rtf_ptr->par_attr.skip_before != 0) {
sprintf(buff,"\\vskip%gpt",TW_TO_PT(rtf_ptr->par_attr.skip_before));
output_str(buff,' ');
}
if(!no_grouping && (TeX_group > 0 || rtf_group > 1) &&
!(CURRENT(par_attr.parindent) &&
CURRENT(par_attr.parskip) &&
CURRENT(par_attr.leftskip) &&
CURRENT(par_attr.rightskip) &&
CURRENT(par_attr.flags) &&
CURRENT(char_attr.FontSize) &&
CURRENT(style) &&
rtf_ptr->char_attr.font != Bold &&
rtf_ptr->char_attr.font != Italic)) {
output_str("{",'\0');
push_TeX_stack("}",Start_Para,1);
}
update_current();
if(in_table && tabs_changed()) {
end_table();
start_table();
}
/*
* install the _real_ text handler
*/
text_out = 1;
(void)RTFSetClassCallback(rtfText, TextClass);
if(rtfClass == rtfText) {
RTFUngetToken(); /* re-schedule the text */
}
}
/*
* End a paragraph
*/
static void
end_para()
{
while(rtf_ptr->TeX_stack != NULL && !top_TeX_flags(Start_Para)) {
(void)pop_TeX_stack();
}
if(in_table) {
output_str("\\cr",'\n');
tab_num = 0;
} else {
output_endline();
output('\n',1);
}
text_out = 0;
end_of_par = 0;
(void)RTFSetClassCallback(rtfText, start_para);
}
/*****************************************************************************/
/*
* Force an update of the current state, only emitting commands
* that actually change anything.
*/
static void
update_current()
{
RTFStyle *style;
SET_PAR_ATTR(parindent);
SET_PAR_ATTR(parskip);
if(!CURRENT(par_attr.flags) || !CURRENT(par_attr.leftskip) ||
!CURRENT(par_attr.rightskip)) {
output_endline();
if(rtf_ptr->par_attr.flags & Centred) {
sprintf(buff,"\\leftskip=%gpt plus 1fill",
TW_TO_PT(rtf_ptr->par_attr.leftskip));
output_str(buff,' ');
sprintf(buff,"\\rightskip=%gpt plus 1fill",
TW_TO_PT(rtf_ptr->par_attr.rightskip));
output_str(buff,'\n');
} else {
sprintf(buff,"\\leftskip=%gpt",
TW_TO_PT(rtf_ptr->par_attr.leftskip));
output_str(buff,' ');
sprintf(buff,"\\rightskip=%gpt",
TW_TO_PT(rtf_ptr->par_attr.rightskip));
output_str(buff,'\n');
}
}
if(rtf_current.char_attr.FontSize != rtf_ptr->char_attr.FontSize) {
sprintf(buff,"\\pointsize{%d}",rtf_ptr->char_attr.FontSize);
output_str(buff,' ');
}
if(rtf_ptr->char_attr.font == Bold) {
set_font(Bold,1,"\\bf","}");
} else if(rtf_ptr->char_attr.font == Italic) {
set_font(Italic,1,"\\it","\\/}");
}
if(rtf_current.style != rtf_ptr->style) {
if((style = RTFGetStyle(rtf_ptr->style)) == NULL) {
fprintf(stderr,"Unknown style: %d\n",rtf_ptr->style);
} else {
if(!no_grouping) {
output_str("{",'\0');
push_TeX_stack("}",Style,rtf_ptr->style);
}
sprintf(buff,"\\%s",TeX_name(style->rtfSName));
output_str(buff,' ');
}
}
rtf_current = *rtf_ptr;
rtf_current.TeX_stack = NULL;
rtf_current.prev = NULL;
}
/*****************************************************************************/
/*
* ALLDONE
*/
#define SET_FONT(FONT,START,END) /* set a Font */\
if(text_out) { \
set_font(FONT,(rtfParam == 0 ? 0 : 1),START,END); \
} \
rtf_ptr->char_attr.font = (rtfParam == 0 ? 0 : FONT);
static void
CharAttr()
{
switch (rtfMinor) {
case rtfPlain:
if(text_out) {
while(top_TeX_flags(Font) || top_TeX_flags(Font_Num)) {
(void)pop_TeX_stack();
}
if(rtf_ptr->char_attr.font != Plain) {
set_font(Plain,1,"\\rm","}");
}
}
rtf_ptr->char_attr.font = Plain;
break;
case rtfBold:
SET_FONT(Bold,"\\bf","}");
break;
case rtfItalic:
SET_FONT(Italic,"\\it","\\/}");
break;
case rtfStrikeThru:
case rtfOutline:
case rtfShadow:
case rtfSmallCaps:
case rtfAllCaps:
case rtfInvisible:
{
static int count = 0;
if(verbose > 1 || (verbose && count == 0))
msg_not_supported(
rtfMinor == rtfStrikeThru ? "strike" :
rtfMinor == rtfOutline ? "outl" :
rtfMinor == rtfShadow ? "shad" :
rtfMinor == rtfSmallCaps ? "scaps" :
rtfMinor == rtfAllCaps ? "caps" :
rtfMinor == rtfInvisible ? "v" : "Unknown");
count++;
}
break;
case rtfFontNum:
{
RTFFont *font;
if((font = RTFGetFont(rtfParam)) == NULL) {
fprintf(stderr,"NULL font returned for rtfParam = %d\n",rtfParam);
break;
}
if(verbose > 1) {
flush();
fprintf(stderr,"Param: %d, Font \"%s\" %s(type: %d)\n",
rtfParam,font->rtfFName,
(rtfParam == default_font ? "(default) " : ""),
font->rtfFFamily);
}
if(rtf_ptr->char_attr.FontNum != rtfParam) {
if(font->rtfFFamily == rtfFFTech) {
; /* we'll treat each char specially */
} else {
if(!no_grouping && (TeX_group > 0 || rtf_group > 1)) {
output_str("{",'\0');
push_TeX_stack("}",Font_Num,rtfParam);
} else {
rtf_ptr->char_attr.FontNum = rtfParam;
}
sprintf(buff,"\\%s",TeX_name(font->rtfFName));
output_str(buff,' ');
}
rtf_ptr->char_attr.FontNum = rtfParam;
rtf_ptr->char_attr.FontType = font->rtfFFamily;
if(rtf_default.char_attr.FontNum == -1) {
if(TeX_group == 0 && rtf_group <= 1) {
rtf_default.char_attr.FontNum = rtfParam;
}
}
}
}
break;
case rtfFontSize:
rtf_ptr->char_attr.FontSize = rtfParam/2;
break;
case rtfUnderline:
case rtfWUnderline:
{
static int count = 0;
if(verbose > 1 || (verbose && count++ == 0)) {
msg_map_to((rtfMinor == rtfUnderline ? "ul" : "ulw"),"i");
}
rtfMinor = rtfItalic;
CharAttr();
}
break;
case rtfDUnderline:
{
static int count = 0;
if(verbose > 1 || (verbose && count++ == 0)) msg_map_to("uld","i");
rtfMinor = rtfItalic;
CharAttr();
}
break;
case rtfDbUnderline:
{
static int count = 0;
if(verbose > 1 || (verbose && count++ == 0)) msg_map_to("uldb","b");
rtfMinor = rtfBold;
CharAttr();
}
break;
case rtfNoUnderline:
break;
case rtfSubScript:
case rtfSuperScript:
if(rtf_ptr->sub_super_height == rtfParam) break;
if(!text_out) start_para();
if(rtfParam == 0) { /* end of a sub/superscript */
while(!top_TeX_flags(Sub_Super)) {
if(pop_TeX_stack() == 0) {
if(verbose) {
fprintf(stderr,"Failed to find end of sub/superscript\n");
}
flush();
break;
}
}
pop_TeX_stack(); /* pop off the '}' */
if(top_TeX_flags(Math)) pop_TeX_stack(); /* and pop math too */
break;
}
if(!math_mode) {
output_str("$",'\0');
push_TeX_stack("$",Math,1);
}
output_str(rtfMinor == rtfSuperScript ? "^{" : "_{",'\0');
push_TeX_stack("}",Sub_Super,rtfParam);
break;
case rtfForeColor:
msg_not_supported("cf");
break;
case rtfBackColor:
break;
case rtfExpand:
msg_not_needed("expnd");
break;
case rtfRevised:
break;
}
}
/*****************************************************************************/
/*
* set a Font
*/
static void
set_font(font,turn_on,start,end)
int font; /* flag for font to set (e.g. Bold) */
int turn_on; /* should I start the font or end it?*/
char *end; /* strings to start and */
char *start; /* end the group that sets the font */
{
if(turn_on) {
if(rtf_current.char_attr.font != font) {
if(!no_grouping) {
output_str("{",'\0');
push_TeX_stack(end,Font,font);
}
output_str(start,' ');
rtf_ptr->char_attr.font = font;
}
} else {
if(rtf_current.char_attr.font == font) {
if(top_TeX_flags(Font) &&
rtf_ptr->TeX_stack->flags == font) { /* just close the group */
(void)pop_TeX_stack();
} else { /* We have to explicitly set \rm */
set_font(Plain,1,"\\rm","}");
}
}
}
}
/*****************************************************************************/
/*
* Write a character, filling the the output text as we go. Characters
* special to TeX are treated appropriately.
*/
#define LWIDTH 79 /* width of line */
#define FILL_COLUMN 70 /* column to fill to */
#define WSIZE 100 /* maximum length of a word */
static char word[WSIZE + 2]; /* +2: allow for '\\' and '\0' */
static char *wptr = word;
static int column = 0; /* current column */
static void
output(c,quote)
int c; /* The char to print */
int quote; /* quote TeX's special chars? */
{
char temp[25];
c &= '\377'; /* prevent sign extension */
if(isspace(c)) {
*wptr = '\0';
if(c == '\n') {
if(column + (wptr - word) > LWIDTH) {
send("\n");
}
send(word);
send("\n");
column = 0;
} else {
if(column + (wptr - word) > FILL_COLUMN) {
send("\n");
column = 0;
}
send(word);
send(" ");
column += wptr - word + 1;
}
wptr = word;
return;
}
if(wptr >= word + WSIZE) { /* too long. Ah well. */
*wptr = '\0';
if(verbose) {
fprintf(stderr,"\"%s\" is too long to fit nicely in a line\n",word);
}
send(word);
column = 0;
wptr = word;
}
if(quote) {
switch (rtf_ptr->char_attr.FontType) {
case rtfFFTech: /* A technical font */
{
char *str;
str = (c < 128) ? symbol[c] : symbol8[c & '\177'];
if(*str == '\0') {
if(verbose) {
fprintf(stderr,"Unknown Tech character: 0x%x\n",c);
}
sprintf(temp,"\\unknown{0x%x}",c);
str = temp;
}
in_math(str);
}
return;
default:
switch (c) { /* deal with various characters */
case '%': case '$':
case '#': case '&':
case '_':
*wptr++ = '\\';
break;
case '^':
output_str("\\caret",'\0');
c = '/';
break;
case '"':
if(wptr == word) {
*wptr++ = c = '`';
} else {
*wptr++ = c = '\'';
}
break;
case '<': case '>':
case '|':
sprintf(temp,"%c",c);
in_math(temp);
return;
case '{': case '}':
sprintf(temp,"\\%c",c);
in_math(temp);
return;
case '\\':
in_math("\\backslash");
return;
case '~':
in_math("\\sim");
c = ' ';
return;
default:
if(!isascii(c)) {
output_8bit(c);
return;
}
break;
}
}
}
*wptr++ = c;
}
/*****************************************************************************/
/*
* Output an entire word, without trying to quote any special characters.
* The character C is then output using output(), which allows proper page
* breaks.
*/
static void
output_str(str,c)
char *str;
int c;
{
int len = strlen(str);
if(wptr + len >= word + WSIZE) { /* too long. Ah well. */
*wptr = '\0';
if(verbose) {
fprintf(stderr,"\"%s\" is too long\n",word);
}
send("\n");
send(word);
column = wptr - word;
wptr = word;
}
strncpy(wptr,str,WSIZE - (wptr - word));
wptr += len;
if(c != '\0') {
if(isspace(c)) output(c,1);
else *wptr++ = c;
}
}
/*****************************************************************************/
/*
* Actually send a string to the output. We could replace this
* with a call to printf, except that I want to do some final cleanup
* of the output stream.
*/
#define BSIZE 100
#define OUTBUF(I) outbuf[(I)%BSIZE] /* outbuf is a circular buffer */
#define NSEQ 10 /* number of control sequences
to remember */
#define SEQSIZE 30 /* size of sequences */
static char outbuf[BSIZE];
static int in = 0, out = 0;
static void
send(str)
char *str;
{
register char c;
register int i;
int j;
static int nseq = 0; /* number of remembered sequences */
static char sequences[NSEQ][SEQSIZE + 1]; /* remembered control sequences */
char token[BSIZE],*tptr;
if(no_cleanup) {
printf("%s",str);
return;
}
while(*str != '\0') {
if(in - out >= BSIZE) {
switch (c = OUTBUF(out++)) {
case '$':
if(OUTBUF(out) == '$') { /* we can drop a $$ */
out++;
if(isalpha(OUTBUF(out))) {
putchar(' ');
}
continue;
} else {
for(i = out;i < in && OUTBUF(i++) == ' ';) continue;
if(OUTBUF(i - 1) == '$') { /* we can drop $ $ */
out = i;
continue;
}
}
break;
case '_': case '^':
if(OUTBUF(out) == '{') {
for(i = out + 1;isspace(OUTBUF(i));i++) ;
if(OUTBUF(i) == '}') { /* drop [^_]{ *} */
out = i + 1;
continue;
}
if(OUTBUF(out + 2) == '}') {
putchar(c);
putchar(OUTBUF(out + 1));
out += 3;
if(isalpha(OUTBUF(out))) {
putchar(' ');
} else if(OUTBUF(out) == c || /* ^{a}^{b} */
OUTBUF(out) == '$' && OUTBUF(out + 1) == '$' &&
OUTBUF(out + 2) == c) { /* ^{a}$$y^{b} */
putchar('{');
putchar('}');
}
continue;
} else if(OUTBUF(out + 1) == '\\') {
for(i = out + 2, tptr = token;i < in;tptr++) {
*tptr = OUTBUF(i++);
if(!isalpha(*tptr)) {
*tptr = '\0';
break;
}
}
for(i--;i < in && OUTBUF(i++) == ' ';) continue;
if(OUTBUF(i - 1) == '}') { /* "^{\word }" --> "^\word " */
putchar(c);
putchar('\\');
out = i;
for(tptr = token;*tptr != '\0';) putchar(*tptr++);
if(isalpha(OUTBUF(out))) {
putchar(' ');
}
continue;
}
}
}
break;
#if 0
case '}': /* see if we need a } { pair */
{
int different = 0; /* is this {} identical to the last? */
int newlines = 0; /* did we skip some newlines? */
int save_out = out;
int the_same; /* are 2 control sequences the same? */
while(isspace(OUTBUF(out))) {
if(OUTBUF(out++) == '\n') newlines++;
}
if(OUTBUF(out) == '{') out++;
while(isspace(OUTBUF(out))) {
if(OUTBUF(out++) == '\n') newlines++;
}
if(OUTBUF(out) != '\\') {
out = save_out;
nseq = 0;
break; /* just output the stuff */
}
for(j = 0;j < NSEQ && OUTBUF(out) == '\\';j++) {
out++;
for(i = 0;i < SEQSIZE && isalpha(c = OUTBUF(out++));i++) {
buff[i] = c;
}
out--; /* re-read c */
buff[i] = '\0';
the_same =
(j < nseq && strcmp(buff,sequences[j]) == 0 ? 1 : 0);
strcpy(sequences[j],buff);
if(!the_same) {
different = 1;
}
while(isspace(OUTBUF(out))) {
if(OUTBUF(out++) == '\n') newlines++;
}
}
if(!different) different = (j != nseq ? 1 : 0);
nseq = j;
if(different) {
c = '}';
out = save_out;
break;
}
if(newlines) {
while(--newlines > 0) putchar('\n');
c = '\n';
} else {
c = ' ';
}
}
break;
#endif
default:
break;
}
putchar(c);
}
outbuf[in++%BSIZE] = *str++;
}
}
/*****************************************************************************/
/*
* Flush the output
*/
static void
flush()
{
*wptr = '\0';
send(word); wptr = word;
while(in > out) {
putchar(outbuf[out++%BSIZE]);
}
fflush(stdout); /* DEBUG */
}
/*****************************************************************************/
/*
* Various ways of refusing to deal with a keyword
*/
/*
* Treat \from as \to (e.g. treat \ul as \i)
*/
static void
msg_map_to(from,to)
char *from, *to;
{
if(verbose && !writing_defs) {
fprintf(stderr,"I'm going to treat \\%s as \\%s\n",from,to);
}
}
/*
* Keyword is neither needed by TeX or supported by rtf2TeX
* For example, \expnd to fiddle with inter-character spacing.
*/
static void
msg_not_needed(name)
char *name;
{
if(verbose && !writing_defs) {
fprintf(stderr,"\\%s is neither needed nor supported\n",name);
}
}
/*
* Keyword isn't supported, and probably can't be
* For example, \cf to change colours
*/
static void
msg_not_supported(name)
char *name;
{
if(verbose && !writing_defs) {
fprintf(stderr,"rtf2TeX doesn't support \\%s; sorry\n",name);
}
}
/*
* Keyword will be supported, but I haven't done it yet
*/
static void
msg_not_yet(name)
char *name;
{
if(verbose && !writing_defs) {
fprintf(stderr,"rtf2TeX doesn't support \\%s yet; be patient\n",name);
}
}
/*****************************************************************************/
/*
* Stack stuff -- RTF and TeX grouping go on separate stacks
*
* pop the status stack, putting the free'd element on the free list
*/
static RTF_STACK *rtf_free_list = NULL;
static void
pop_rtf_group()
{
RTF_STACK *temp;
if(rtf_ptr->prev == NULL) {
fprintf(stderr,"Attempt to pop an empty stack\n");
abort();
}
temp = rtf_ptr->prev;
rtf_ptr->prev = rtf_free_list;
rtf_free_list = rtf_ptr;
rtf_ptr = temp;
}
/*
* push the RTF status stack
*/
static void
push_rtf_group()
{
char *malloc();
RTF_STACK *temp;
if(rtf_free_list != NULL) {
temp = rtf_free_list;
rtf_free_list = rtf_free_list->prev;
} else {
if((temp = (RTF_STACK *)malloc(sizeof(RTF_STACK))) == NULL) {
fprintf(stderr,"Can't allocate storage for stack\n");
exit(1);
}
}
memcpy((char *)temp,(char *)rtf_ptr,sizeof(RTF_STACK));
temp->prev = rtf_ptr;
rtf_ptr = temp;
rtf_ptr->TeX_stack = NULL;
}
/*****************************************************************************/
/*
* Make all members of an RTF_STACK element invalid
*/
static void
invalidate_current(elem)
RTF_STACK *elem;
{
if(elem->TeX_stack != NULL || elem->prev != NULL) {
fprintf(stderr,
"I can't invalidate an RTF stack item with non-NULL pointers\n");
return;
}
elem->char_attr.font = -1;
elem->char_attr.FontNum = -1;
elem->char_attr.FontType = -1;
elem->char_attr.FontSize = -1;
elem->par_attr.flags = -1;
elem->par_attr.parindent = -1;
elem->par_attr.leftskip = -1;
elem->par_attr.rightskip = -1;
elem->par_attr.parskip = -1;
elem->par_attr.skip_before = -1;
elem->par_attr.skip_after = -1;
elem->par_attr.parskip_b = -1;
elem->par_attr.parskip_a = -1;
elem->style = -1;
}
/*****************************************************************************/
/*
* Given am RTF stack element CHANGED that was invalidated, and then
* had some changes made, reset the untouched elements from TEMPLATE
*/
static void
set_if_invalid(changed,template)
RTF_STACK *changed; /* the element with some changes */
RTF_STACK *template; /* The element whose values we want */
{
if(changed->char_attr.font == -1) {
changed->char_attr.font = template->char_attr.font;
}
if(changed->char_attr.FontNum == -1) {
changed->char_attr.FontNum = template->char_attr.FontNum;
}
if(changed->char_attr.FontType == -1) {
changed->char_attr.FontType = template->char_attr.FontType;
}
if(changed->char_attr.FontSize == -1) {
changed->char_attr.FontSize = template->char_attr.FontSize;
}
if(changed->par_attr.flags == -1) {
changed->par_attr.flags = template->par_attr.flags;
}
if(changed->par_attr.parindent == -1) {
changed->par_attr.parindent = template->par_attr.parindent;
}
if(changed->par_attr.leftskip == -1) {
changed->par_attr.leftskip = template->par_attr.leftskip;
}
if(changed->par_attr.rightskip == -1) {
changed->par_attr.rightskip = template->par_attr.rightskip;
}
if(changed->par_attr.parskip == -1) {
changed->par_attr.parskip = template->par_attr.parskip;
}
if(changed->par_attr.skip_before == -1) {
changed->par_attr.skip_before = template->par_attr.skip_before;
}
if(changed->par_attr.skip_after == -1) {
changed->par_attr.skip_after = template->par_attr.skip_after;
}
if(changed->par_attr.parskip_b == -1) {
changed->par_attr.parskip_b = template->par_attr.parskip_b;
}
if(changed->par_attr.parskip_a == -1) {
changed->par_attr.parskip_a = template->par_attr.parskip_a;
}
if(changed->style == -1) {
changed->style = template->style;
}
}
/*****************************************************************************/
/*
* Now the TeX stacks. Use the stack in the current rtf_ptr frame
*/
static TEX_STACK *TeX_free_list = NULL;
static int set_flags();
/*
* Output the top of the stack; return 0 if it is empty
*/
static int
pop_TeX_stack()
{
char *str;
TEX_STACK *temp;
if(rtf_ptr->TeX_stack == NULL) {
return(0);
}
TeX_group--;
str = rtf_ptr->TeX_stack->str;
temp = rtf_ptr->TeX_stack->prev;
rtf_ptr->TeX_stack->prev = TeX_free_list;
TeX_free_list = rtf_ptr->TeX_stack;
(void)set_flags(rtf_ptr->TeX_stack->type,rtf_ptr->TeX_stack->saved);
rtf_ptr->TeX_stack = temp;
output_str(TeX_free_list->str,'\0');
return(1);
}
/*
* push the string STR onto the TeX stack, with attributes TYPE and FLAGS
*/
static void
push_TeX_stack(str,type,flags)
char *str; /* string to save */
int type; /* type of string */
long flags; /* and corresponding flags */
{
char *malloc();
TEX_STACK *temp;
TeX_group++;
if(TeX_free_list != NULL) {
temp = TeX_free_list;
TeX_free_list = TeX_free_list->prev;
} else {
if((temp = (TEX_STACK *)malloc(sizeof(TEX_STACK))) == NULL) {
fprintf(stderr,"Can't allocate storage for stack\n");
exit(1);
}
}
temp->prev = rtf_ptr->TeX_stack;
rtf_ptr->TeX_stack = temp;
rtf_ptr->TeX_stack->str = str;
rtf_ptr->TeX_stack->type = type;
rtf_ptr->TeX_stack->flags = flags;
rtf_ptr->TeX_stack->saved = set_flags(type,flags);
return;
}
/*****************************************************************************/
/*
* Return the flags of the proper type for the top element of the TeX stack
*/
static int
top_TeX_flags(type)
int type;
{
return((rtf_ptr->TeX_stack != NULL && rtf_ptr->TeX_stack->type == type) ?
1 : 0);
}
/*****************************************************************************/
static int
set_flags(type,value)
int type; /* type to test */
long value; /* associated value */
{
int ret = 1;
if(type == Font) {
ret = rtf_current.char_attr.font;
rtf_current.char_attr.font = rtf_ptr->char_attr.font = value;
} else if(type == Font_Num) {
ret = rtf_current.char_attr.FontNum;
rtf_current.char_attr.FontNum = rtf_ptr->char_attr.FontNum = value;
} else if(type == Font_Size) {
ret = rtf_current.char_attr.FontSize;
rtf_current.char_attr.FontSize = rtf_ptr->char_attr.FontSize = value;
} else if(type == Style) {
ret = rtf_current.style;
rtf_current.style = rtf_ptr->style = value;
} else if(type == Par_Attr) {
ret = rtf_ptr->par_attr.flags;
rtf_current.par_attr.flags = rtf_ptr->par_attr.flags = value;
} else if(type == Math) {
math_mode = !math_mode;
} else if(type == Sub_Super) {
ret = rtf_current.sub_super_height;
rtf_current.sub_super_height = rtf_ptr->sub_super_height = value;
}
return(ret);
}
/*****************************************************************************/
/*
* Print a string, ensuring that we are in math mode at the time
*/
static void
in_math(str)
char *str;
{
if(!math_mode) {
output('$',0);
}
output_str(str,'\0');
if(!math_mode) {
output('$',0);
}
}
/*****************************************************************************/
/*
* End a line, if not already at the end of a line
*/
static void
output_endline()
{
if(column > 0 || wptr > word) output('\n',1);
}
/*****************************************************************************/
/*
* Deal with special characters above \177.
*/
static void
output_8bit(c)
int c;
{
char *str;
if(*(str = times8[c & '\177']) == '\0') {
if(verbose) {
fprintf(stderr,"Unknown 8-bit character: 0x%x\n",c);
sprintf(buff,"\\unknown{0x%x}",c);
output_str(buff,' ');
}
} else {
if(*str == '$') {
in_math(str + 1);
} else {
output_str(str,'\0');
}
}
}
/*****************************************************************************/
/*
* Called after the font table is read
*/
static void
read_font()
{
int save_ng = no_grouping;
if(default_read_font == NULL) {
fprintf(stderr,"No default font table reader is installed\n");
exit(-1);
}
(*default_read_font)();
if(default_font == -1) { /* we never saw a \deff */
return;
}
if(RTFGetFont(default_font) == NULL) {
if(verbose) {
fprintf(stderr,"The default font (%d) is not defined\n",default_font);
}
return;
}
rtfClass = rtfControl;
rtfMajor = rtfCharAttr;
rtfMinor = rtfFontNum;
rtfParam = default_font;
no_grouping = 1;
RTFRouteToken();
no_grouping = save_ng;
}
/*****************************************************************************/
/*
* Called after the style table is read if we are handling styles ourselves
*/
static void expand_style();
static void
read_style()
{
int save_no_grouping = no_grouping;
RTF_STACK save_current;
RTFStyle *style;
if(default_read_style == NULL) {
fprintf(stderr,"No default style table reader is installed\n");
exit(-1);
}
(*default_read_style)();
/*
* We must start by defining all the styles as TeX macros
*/
save_current = rtf_current;
writing_defs = no_grouping = 1;
output_str("% Style Sheet:\n%",'\n');
push_rtf_group(); /* we want our own TeX stack */
for(style = RTFGetStyle(-1);style != NULL;style = style->rtfNextStyle) {
sprintf(buff,"\\def\\%s{",TeX_name(style->rtfSName));
output_str(buff,'\0');
invalidate_current(&rtf_current);
expand_style(style);
set_if_invalid(&rtf_current,&save_current);
update_current(); /* actually write definition */
while(pop_TeX_stack()) continue;
output_str("}",'\n');
}
pop_rtf_group();
output_str("%",'\n');
writing_defs = 0;
rtf_current = save_current;
no_grouping = save_no_grouping;
rtf_ptr->style = 0; /* set the default style */
}
static void
expand_style(style)
RTFStyle *style;
{
RTFStyle *base;
RTFStyleElt *list; /* list of style words */
static int depth = 0; /* depth of recursion */
if(++depth > 10) {
fprintf(stderr,"Style nesting too deep: %d (%s)\n",
style->rtfSNum,TeX_name(style->rtfSName));
depth--;
return;
}
if(style->rtfSBasedOn >= 0 && style->rtfSBasedOn != rtfBasedOnNone) {
if((base = RTFGetStyle(style->rtfSBasedOn)) == NULL) {
if(verbose) {
fprintf(stderr,"Can't expand style %d\n",style->rtfSBasedOn);
}
} else {
expand_style(base);
}
}
for(list = style->rtfSSEList;list != NULL;list = list->rtfNextSE) {
rtfClass = list->rtfSEClass;
rtfMajor = list->rtfSEMajor;
rtfMinor = list->rtfSEMinor;
rtfParam = list->rtfSEParam;
RTFRouteToken();
}
depth--;
}
/*****************************************************************************/
/*
* Convert a string to a form that TeX can handle
*
* Remove spaces and capitalise the following letter,
* and converted digits to letters (1 --> A etc., 0 --> O)
*/
static char *
TeX_name(str)
char *str;
{
static char temp[50];
char *ptr;
for(ptr = temp;*str != '\0';str++) {
if(isspace(*str)) {
for(str++;isspace(*str);str++) continue;
if(*str == '\0') break;
*str = islower(*str) ? toupper(*str) : *str;
str--; continue; /* reprocess the character */
} else if(isdigit(*str)) {
if(*str == '0') *ptr++ = 'O';
else *ptr++ = *str + 'A' - '1';
} else {
*ptr++ = *str;
}
}
*ptr = '\0';
return(temp);
}
/*****************************************************************************/
/*
* Start a table
*/
static void
start_table()
{
char *glue; /* glue to use for filling */
int i;
int width; /* width of table entry (twips) */
if(in_table) {
end_table();
}
for(i = 0;i < ntabs;i++) {
old_tabstops[i] = tabstops[i];
}
old_ntabs = ntabs;
glue = ignore_tabwidths ? "\\hfill" : "\\hss";
output_endline();
output_str("\\halign{%",'\n');
for(i = 0;i < ntabs;i++) {
width = tabstops[i].pos;
if(i > 0) {
width -= tabstops[i - 1].pos;
}
if(!ignore_tabwidths) {
sprintf(buff,"\\hbox to %gpt{",TW_TO_PT(width));
output_str(buff,'\0');
}
if(tabstops[i].type == TabRight || tabstops[i].type == TabCentre) {
output_str(glue,' ');
}
output_str("#",'\0');
if(tabstops[i].type == TabLeft) {
output_str(glue,'\0');
}
if(!ignore_tabwidths) {
output_str("}",'\0');
}
if(i == ntabs - 1) {
output_str("\\cr",'\n');
} else {
output_str("&",'\0');
}
if(0 && i == ntabs - 2) { /* survive extra tabs without dying */
output_str("&",'\0');
}
output(' ',1);
}
in_table = 1;
}
static void
end_table()
{
if(!in_table) {
fprintf(stderr,"Attempt to end a table when not in one\n");
return;
}
output_str("}",'\n');
old_ntabs = in_table = 0;
}
/*****************************************************************************/
/*
* Are the tabstops changed? Set the the old values to the new while
* we are about it
*/
static int
tabs_changed()
{
int i;
for(i = 0;i < ntabs;i++) {
if(i > old_ntabs || tabstops[i].pos != old_tabstops[i].pos ||
tabstops[i].type != old_tabstops[i].type) {
return(1);
}
}
return(0);
}
/*****************************************************************************/
static void
usage()
{
static char *msg[] = {
"Usage: rtf2TeX [options] [RTF-file]",
"Your options are:",
" {+-}c Cleanup TeX output (+, default) or off (-)",
" -h This message",
" -i file A file of TeX definitions to include",
" {+-}s Turn style expansion on (+, default) or off (-)",
" {-+}t Don't try to generate \\haligns (-, default) or do (+)",
" {+-}T Ignore widths in tables (-) or keep them (+, default)",
" -v[#] Turn on verbose messages; # defaults to one",
" -V Print the version number",
"If you omit the filename rtf2TeX will read standard input.",
NULL,
};
print_text(msg,stderr);
}
/*****************************************************************************/
/*
* print some text MSG to a stream FIL
*/
static void
print_text(msg,fil)
char *msg[];
FILE *fil;
{
char **line;
for(line = msg;*line != NULL;line++) {
fprintf(fil,"%s\n",*line);
}
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.