/* * 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 #include #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); } }