home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #18 / NN_1992_18.iso / spool / comp / text / frame / 2628 < prev    next >
Encoding:
Text File  |  1992-08-18  |  34.8 KB  |  1,457 lines

  1. Newsgroups: comp.text.frame
  2. Path: sparky!uunet!europa.asd.contel.com!darwin.sura.net!convex!convex!connolly
  3. From: connolly@convex.com (Dan Connolly)
  4. Subject: mif2rtf.pl: here it is.
  5. Summary: perl script to convert FrameMaker MIF files to MS Word RTF format
  6. Sender: usenet@news.eng.convex.com (news access account)
  7. Message-ID: <1992Aug18.202733.21362@news.eng.convex.com>
  8. Date: Tue, 18 Aug 1992 20:27:33 GMT
  9. Nntp-Posting-Host: pixel.convex.com
  10. Organization: Engineering, CONVEX Computer Corp., Richardson, Tx., USA
  11. Keywords: MIF RTF perl Rich Text Format filter
  12. X-Disclaimer: This message was written by a user at CONVEX Computer
  13.               Corp. The opinions expressed are those of the user and
  14.               not necessarily those of CONVEX.
  15. Lines: 1440
  16.  
  17. I have had a number of requests for this beast over the last
  18. year or so. I don't use the perl script any more -- the version
  19. under development uses XLisp in stead.
  20.  
  21. But a lot of folks have had good luck with it. It converts
  22. basic character and paragraph formatting. It does pretty
  23. much what you'd expect for letters and memo's. Somebody
  24. had good luck with a 90 page document. (Chalk one up
  25. for perl! It scales well.)
  26.  
  27. I have one request: would somebody archive this thing on
  28. an FTP server and let me know where you put it?
  29.  
  30. Anyway, here it is:
  31.  
  32. # This is a shell archive.  Remove anything before this line,
  33. # then unpack it by saving it in a file and typing "sh file".
  34. #
  35. # Wrapped by pixel!connolly on Tue Aug 18 15:21:58 CDT 1992
  36. # Contents:  mif2rtf.pl
  37.  
  38. echo x - mif2rtf.pl
  39. sed 's/^@//' > "mif2rtf.pl" <<'@//E*O*F mif2rtf.pl//'
  40. #!/usr/local/bin/perl
  41. # $Id$
  42. #
  43. # USE
  44. #
  45. $Usage = "
  46. $0 -d [mif_file]... >out.rtf
  47.    -d               turn on debugging
  48.    -info TAG GROUP  Put all text from TAG paragraphs into info group GROUP
  49. ";
  50. #
  51. # Code marked @@ needs fixing.
  52. # Code marked @# is an improvisation
  53. # Code marked HEURISTIC is not exact.
  54. #
  55. # COPYRIGHT
  56. #
  57. # Copyright 1992 by Convex Computer Corporation, Richardson, Texas.
  58. #
  59. #                        All Rights Reserved
  60. #
  61. # Permission to use, copy, modify, and distribute this software and its 
  62. # documentation for any purpose and without fee is hereby granted, 
  63. # provided that the above copyright notice appear in all copies and that
  64. # both that copyright notice and this permission notice appear in 
  65. # supporting documentation, and that the name Convex not be
  66. # used in advertising or publicity pertaining to distribution of the
  67. # software without specific, written prior permission.  
  68. # CONVEX DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  69. # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
  70. # CONVEX BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
  71. # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  72. # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  73. # ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  74. # SOFTWARE.
  75. #
  76. # BUGS
  77. #
  78. # @#Footnote paragraph formats not right
  79. # @#should convert page margins (frame BRect->margr etc.)
  80. # @#No way to make TOC entries
  81. # @#master pages
  82. # @# Not all document, section, or table properties are converted.
  83. # @#Hard problems: reference page items
  84. #   Maybe use command line options for those.
  85. # @# LineSpacing doesn't translate
  86. #
  87. # IDEAS
  88. # absolute positioned object: frame brect etc.
  89. #
  90.  
  91. #########
  92. #
  93. # main
  94. #
  95.  
  96. local($Debug,
  97.       # 
  98.       # %TagGroup maps MIF paragraph tags to RTF info groups
  99.       # %RTFInfo holds the text of the RTF info groups
  100.       # so that after &convert_pages(),
  101.       # $RTFInfo{$TagGroup{pgftag}} contains all text tagged pgftag
  102.       # 
  103.       # @@ idea: extend this to character tags
  104.       # 
  105.       %TagGroup
  106.       );
  107. @@ARGV = &parse_args(@ARGV);
  108.  
  109.  
  110. local(%State,
  111.       # 
  112.       # %State maps MIF and RTF codes to their current settings
  113.       # 
  114.       %CharacterConversions
  115.       );
  116. &initialize_state();
  117.  
  118. local($Document,
  119.       # $Document is the RTF code to get from the default
  120.       # document format to what's in %State
  121.       # NOTE: some MIF document attrs are RTF Section attrs
  122.       # ASSUME: MIF document defaults are RTF defaults
  123.  
  124.       %Typeface, $Typeface,
  125.       # %Typeface maps FFamilys to RTF font numbers
  126.  
  127.       %Tag, $Tag,
  128.       # %Tag maps PgfTags to RTF style numbers
  129.  
  130.       %PgfCatalog, %FontCatalog,
  131.  
  132.       @BodyPage,
  133.       %TextRect, %TextFlow,
  134.       %AFrame,
  135.       %Tbl
  136.       );
  137.  
  138. $Document = "\\ftnbj "; # This is the default in Frame, but not in Word.
  139.  
  140. local($PageType, $TextRectID, $ID, $TblID,
  141.       $XRefName, $VaraibleName);
  142.  
  143. &read_whole_file
  144.      ('DPageSize', '$Document. = &change_dims($data, "paperw", "paperh")',
  145.       'DStartPage', '$Document .= &change_attr("pgnstarts", $data)',
  146.       'DPageNumStyle', '$Document .= &select_attr($data, "PageNumStyle",
  147.                       "Arabic", "pgndec",
  148.                      "UCRoman", "pgnucrm",
  149.                      "LCRoman", "pgnlcrm",
  150.                      "UCALpha", "pgnucltr",
  151.                      "LCAlpha", "pgnlcltr")',
  152.       'DTwoSides', '$Document .= &change_attr("facingp", 1)',
  153.       'DFNoteRestart', '$Document .= &select_attr($data, "FNoteRestart",
  154.                      "PerPage", "ftnrestart")',
  155.       'DFNoteStartNum', '$Document .= &change_attr("fntstart", $data)',
  156.       'DAutoChBars', '$Document .= &change_attr("revisions", 1)',
  157.       
  158.       'FFamily', '&intern(*Typeface, $data)',
  159.       'PgfTag', '&intern(*Tag, $data)',
  160.       
  161.       'PgfCatalog', '&convert_paragraph_catalog($here)',
  162.       'FontCatalog', '&convert_character_catalog($here)',
  163.       
  164.       'PageType', '$PageType = $data',
  165.       'Page', 'push(@BodyPage, $here) if $PageType eq "BodyPage"',
  166.  
  167.       'TextRectID',
  168.       '$TextRectID ? &debug("Extra TextRect: $data") : ($TextRectID = $data)',
  169.  
  170.       'TextFlow', '@TextRect{$TextRectID} = $here;
  171.                    &debug("flow", %TextRect); $TextRectID = ""',
  172.  
  173.       'Frame', '$AFrame{&data_search($here, "ID", 1)} = $here',
  174.  
  175.       'TblID', '$TblID = $data',
  176.       'Tbl', '$Tbl{$TblID} = $here',
  177.  
  178.       'XRefName', '$XRefName = &convert_string($data)',
  179.       'XRefDef', '$Definition{"XRef", $XRefName} = $data',
  180.       
  181.       'VarialbeName', '$VariableName = &convert_string($data)',
  182.       'VariableDef', '$Definition{"Variable", $VariableName} = $data'
  183.       );
  184.  
  185. print STDOUT &rtf_begin_doc;
  186. print STDOUT &rtf_font_table(%Typeface);
  187. print STDOUT &rtf_color_table(('0 0 0 Black', # These are Frame's colors
  188.                    '1 1 1 White',
  189.                    '1 0 0 Red',
  190.                    '0 1 0 Green',
  191.                    '0 0 1 Blue',
  192.                    '0 1 1 Cyan',
  193.                    '1 0 1 Magenta',
  194.                    '1 1 0 Yellow'));
  195.  
  196. print STDOUT $Document, "\n";
  197.  
  198. #&debug("body pages: ", @BodyPage);
  199.  
  200. local(@Document);
  201.  
  202. foreach $page (@BodyPage){
  203. #    &debug("converting page:\n", &expand_mif_statement($page));
  204.     push(@Document, &convert_frame($page));
  205. }
  206.  
  207. if(!defined($StyleSheet) || $StyleSheet) {
  208.     print STDOUT &rtf_style_sheet(%Tag);
  209. }
  210. print STDOUT &rtf_info(%RTFInfo);
  211. print STDOUT @Document;
  212. print STDOUT &rtf_end_doc;
  213.  
  214. print STDERR "\n"; # after all the .....'s
  215.  
  216. ######
  217.  
  218. sub parse_args{
  219.     # 
  220.     # USE : @ARGV = &parse_args(@ARGV);
  221.     # SETS: $Debug, %TagGroup
  222.     #
  223.     local(@files);
  224.  
  225.     while($_ = shift){
  226.         if(/^-d/){
  227.             $Debug = 'debug';
  228.         }
  229.     elsif(/^-info/){
  230.         $TagGroup{shift} = shift;
  231.     }
  232.     elsif(/^-nostyles/){
  233.         $StyleSheet = 0;
  234.         }else{
  235.             push(@files, $_);
  236.         }
  237.     }
  238.     @files;
  239. }
  240.  
  241. #
  242. #
  243. ##########
  244.  
  245.  
  246. ##########
  247. #
  248. # Mif Parsing Routines
  249. #
  250. # I've finally found a perl representation of a mif file that I'm happy with.
  251. # It's pretty memory intensive and not real zippy, but that's the nature
  252. # of the beast with MIF.
  253. sub read_whole_file{
  254.     local(%Callbacks) = @_;
  255.     # global(@Parts);
  256.     @Parts = (0); # put one element in it so there's no statement 0.
  257.  
  258.     1 while(&read_mif_statement());
  259. }
  260.  
  261.  
  262.  
  263. sub read_mif_statement{
  264.     # 
  265.     # Returns an index in @Parts so that
  266.     # $Parts[$index] is the statement type
  267.     # and @Parts[$index+1, $index+2, ...] are the parts of the statement.
  268.     # For compound statement, @Parts[$index, ...]  looks like
  269.     # ('<', token, index1, index2, index3, ...)
  270.     # where indexN are the substatements
  271.     # 
  272.     # for each statment, $Callbacks{$token} is evaluated
  273.     #   with $token, $data, and $here set
  274.     #
  275.  
  276.     local($_, $type, $token, $data, @parts, $here, $line);
  277.     # global(@Parts);
  278.  
  279.     while(<>){
  280.     $line = $.;
  281.     $type = '<>', $token = $1, $data = $2, last
  282.         if /^\s*<(\w+)\s+(.*\S)?\s*>/;
  283.     $type = '<', $token = $1, last
  284.         if /^\s*<(\w+)/;
  285.     return 0 if /^\s*>/;
  286.     $type = '=', $token = $1, $data = &read_mif_inset(), last
  287.         if /^=(\w+)/;
  288.     }
  289.     return 0 unless $type;
  290.  
  291.     local($callback);
  292.     if($type eq '<>' || $type eq '='){
  293.     $here = $#Parts+1;
  294.     push (@Parts, $type, $token, $data);
  295.     eval $callback if $callback = $Callbacks{$token};
  296.     die "$@ $callback" if $@;
  297.     }else{
  298.     while($_ = &read_mif_statement()){
  299.         push(@parts, $_);
  300.     }
  301.     $here = $#Parts+1;
  302.     push(@Parts, '<', $token, @parts);
  303.     eval $callback if $callback = $Callbacks{$token};
  304.         die "$@ $callback" if $@;
  305.     }
  306.  
  307.     $Lines{$here} = $line; # for debugging purposes
  308.     $here;
  309. }
  310.  
  311. sub read_mif_inset{
  312.     # Read until a line starts with "=EndInset"
  313.     # and return all the lines concatenated
  314.     local($inset);
  315.     while(<>){
  316.     $inset .= $_;
  317.     last if /^=EndInset/;
  318.     }
  319.     $inset;
  320. }
  321.  
  322.  
  323. sub new_statement{
  324.     local($s) =    $#Parts + 1;
  325.     push(@Parts, @_);
  326.     return $s;
  327. }
  328.  
  329. sub type{
  330.     $Parts[$_[0]];
  331. }
  332.  
  333. sub token{
  334.     $Parts[$_[0]+1];
  335. }
  336.  
  337. sub data{
  338.     $Parts[$_[0]+2];
  339. }
  340.  
  341. sub parts{
  342.     local($i) = @_;
  343.     if(&type($_[0]) eq '<'){
  344.     local($s) = $i+2;
  345.     for(; $Parts[$s] > 0; $s++){
  346.     }
  347.     return @Parts[$i+2 .. $s-1];
  348.     }else{
  349.     return @Parts[$i+2]; #data
  350.     }
  351. }
  352.  
  353. sub do_statement{
  354.     # global(@Parts);
  355.     local($__s, %__callbacks) = @_;
  356.     local($here, $__cb);
  357.  
  358.     foreach $here (&parts($__s)){
  359.     local($token) = &token($here);
  360.     local($data) = &data($here);
  361.  
  362.     eval $__cb if $__cb = $__callbacks{$token};
  363.     warn "Error executing: $__cb \n-->$@" if $@;
  364.     warn "IGNORED: $token line:", $Lines{$here}
  365.         if (&type($here) ne '<>') && !$__cb;
  366.  
  367.     &assert('$token =~ /\w+/');
  368.     }
  369. }
  370.  
  371.  
  372. sub expand_mif_statement{
  373.     # global(@Parts);
  374.     local($i, $leader) = @_;
  375.     local($_) = &type($i);
  376.  
  377.     if($_ eq '<>'){
  378.     return (sprintf("%s<%s %s>\n", $leader, @Parts[$i+1, $i+2]));
  379.     }elsif($_ eq '='){
  380.     return (sprintf("=%s\n%s", @Parts[$i+1, $i+2]));
  381.     }elsif($_ eq '<'){
  382.     local(@lines);
  383.     @lines = (sprintf("%s<%s\n", $leader, &token($i)));
  384.     foreach (&parts($i)){
  385.         push (@lines, &expand_mif_statement($_, "  $leader"));
  386.     }
  387.     push(@lines, "$leader>\n");
  388.     return @lines;
  389.     }
  390. }
  391.  
  392.  
  393. sub data_search{
  394.     #
  395.     # use $tag = &data_search($para, 'PgfTag', 2)
  396.     # to search 2 levels of the $para statement,
  397.     # and return the data of the first PgfTag statement found.
  398.     # 
  399.     local($s, $t, $levels) = @_;
  400.     foreach (&parts($s)){
  401.     if(&type($_) eq '<'){
  402.         if($levels>1){
  403.         $s = &data_search($_, $t, $levels-1);
  404.         return $s if $s ne '';
  405.         }
  406.     }else{
  407.         return &data($_) if &token($_) eq $t;
  408.     }
  409.     }
  410.     return '';
  411. }
  412.  
  413. sub token_search{
  414.     #
  415.     # use &token_search($compound_statement, "token", 3)
  416.     # to search the next three levels of hairy_statement for
  417.     # "token" statements.
  418.     # 
  419.     local($s, $t, $levels) = @_;
  420.  
  421.     local(@matches, $_);
  422.  
  423.     foreach (&parts($s)){
  424.     push(@matches, $_) if &token($_) eq $t;
  425.     push(@matches, &token_search($_, $t, $levels-1))
  426.         if &type($_) eq '<' && $levels>1;
  427.     }
  428.     return @matches;
  429. }
  430.  
  431.  
  432. #
  433. #
  434. ########
  435.  
  436. ############
  437. #
  438. # %State manipulation
  439. #
  440.  
  441. sub change_attr{
  442.     local($attr, $val) = @_;
  443.  
  444. #    &debug("change attr: $attr ($State{$attr}) -> [$val]");
  445.     $State{$attr} = $val
  446.     , return "\\$attr$val "
  447.         unless $State{$attr} eq $val;
  448.     '';
  449. }
  450.  
  451. sub change_dims{
  452.     local($dims, @attrs) = @_;
  453.     local($_, $r);
  454.  
  455.     foreach (&rtf_dimensions($dims)){
  456.     local($attr) = shift(@attrs);
  457.     last unless $attr;
  458.     $attr =~ s-/(\d+)-- && ($_ = int($_/$1)); #HACK
  459.     $r .= &change_attr($attr, $_);
  460.     }
  461.     $r;
  462. }
  463.  
  464. sub select_attr{
  465.     local($key, $s, %attrs) = @_;
  466.  
  467.     if($attrs{$key}){
  468. #    &debug("select attr: '$s=$key' from ", join(",", %attrs)),
  469.     $State{$s} = $attrs{$key}, return "\\$attrs{$key} "
  470.         unless $State{$s} eq $attrs{$key};
  471.     }
  472.     $State{$s} = '';
  473. }
  474.  
  475. #############
  476. #
  477. # conversions
  478. #
  479.  
  480. sub convert_paragraph_catalog{
  481.     local($s) = @_;
  482.     #global(%State, %PgfCatalog);
  483.  
  484.     local($tag);
  485.     &do_statement($s, 'Pgf', '
  486.           $tag = &convert_string(&data_search($here, "PgfTag"));
  487.           $PgfCatalog{"Statement", $tag} = $here;'
  488.           );
  489. }
  490.  
  491. sub convert_character_catalog{
  492.     local($s) = @_;
  493.     #global(%State, %FontCatalog);
  494.  
  495.     local($tag);
  496.     &do_statement($s,'Font', '
  497.           $tag = &convert_string(&data_search($here, "FTag"));
  498.           $FontCatalog{"Statement", $tag} = $here;'
  499.           );
  500. }
  501.  
  502. sub convert_pgf_format{
  503.     local($s) = @_;
  504.     #
  505.     # Returns RTF to change from what's in %State to what's in $s
  506.     # EXCEPT TABS! use ¤t_tabs() to get them.
  507.     # 
  508.  
  509.     local($rtf, $_);
  510.  
  511.     local($lindent) = &data_search($s, 'PgfLIndent');
  512.     local($li);
  513.     if($lindent gt ''){
  514.     ($li) = &rtf_dimensions($lindent);
  515.     $State{'LIndent'}=$lindent;
  516.     }else{
  517.     ($li) = &rtf_dimensions($State{'LIndent'});
  518.     }
  519.  
  520.     local($findent) = &data_search($s, 'PgfFIndent');
  521.     local($fi);
  522.     if($findent gt ''){
  523.     ($fi) = &rtf_dimensions($findent);
  524.     $State{'FIndent'}=$findent;
  525.     }else{
  526.     ($fi) = &rtf_dimensions($State{'FIndent'});
  527.     }
  528.     $rtf .= &change_attr("fi", $fi - $li);
  529.     $rtf .= &change_attr("li", $li);
  530.     &debug("findent: $findent fi: $fi lindent: $findent li: $li");
  531.  
  532.     &do_statement
  533.     ($s,
  534.      'PgfNumberFont', '$State{"NumberFont"} = &convert_string($data)',
  535.      'PgfNumFormat', '$State{"NumFormat"} = &convert_string($data)',
  536.      'PgfNextTag',
  537.      '$rtf .= &change_attr("snext", &intern(*Tag, $data))',
  538.      'PgfNumTabs', 'undef($State{"TabStops"})',
  539.      'TabStop', '&TabStop($here)',
  540.      'PgfAlignment', '$rtf .= &select_attr($data, "Alignment",
  541.                     "Left", "ql",
  542.                     "Center", "qc",
  543.                     "Right", "qr",
  544.                     "LeftRight", "qj")',
  545.      # RTF & MIF are different about tabs and first indents
  546.      'PgfRIndent', '$rtf .= &change_dims($data, "ri")',
  547.  
  548.      'PgfTopSeparator',
  549.      '$rtf .= &change_attr("brdrt", 1) if $data ne "`\'"', #@#
  550.      'PgfBotSeparator',
  551.      '$rtf .= &change_attr("brdrb", 1) if $data ne "`\'"', #@#
  552.  
  553.      'PgfPlacement', '$rtf .= &select_attr($data, "Placement",
  554.                     "ColumnTop", "pagebb", #@#
  555.                     "PageTop", "pagebb",
  556.                     "LPageTop", "pagebb", #@#
  557.                     "RPageTop", "pagebb")', #@#
  558.      'PgfSpBefore', '$rtf .= &change_dims($data, "sb")',
  559.      'PgfSpAfter', '$rtf .= &change_dims($data, "sa")',
  560.      
  561.      #@# withprev
  562.      'PgfWithNext', '$rtf .= &select_attr($data, "WithNext",
  563.                       "Yes", "keepn", "No", "keepn0")',
  564.      'PgfBlockSize', '$rtf .= &change_attr("keep", 1) unless $data <2',
  565.      'PgfLeading', '$rtf .= &change_dims($data, "sl")',
  566.      'PgfFont', '$rtf .= &convert_char_format($here)'
  567.      );
  568.  
  569. #    &debug("converted $State{'PgfTag'} -> $rtf");
  570.     $rtf;
  571. }
  572.  
  573. sub TabStop{
  574.     local($s) = @_;
  575.  
  576.     #@# tab alignment ignored!
  577.     $State{'TabStops'} .= " " . &data_search($s, 'TSX');
  578. }
  579.  
  580. sub convert_char_format{
  581.     local($rtf);
  582.     &do_statement
  583.     ($_[0],
  584.      'FFamily', '$State{"Family"} = &convert_string($data);
  585.                      $rtf .= &change_attr("f", &intern(*Typeface, $data))',
  586. #@#     'FVar', 'warn "IGNORED: Variation $data" unless $data =~ /regular/i',
  587.      'FWeight',  '$rtf .= &select_attr(&convert_string($data),"Weight",
  588.                   "Bold", "b", "Regular", "b0")',
  589.      'FAngle', '$rtf .= &select_attr(&convert_string($data), "Angle",
  590.                  "Italic", "i", "Oblique", "i2",
  591.                  "Regular", "i0")',
  592.      'FSize', '$rtf .= &change_dims($data, "fs/10")',
  593.      'FUnderline', '$rtf .= &select_attr($data, "Underline",
  594.                      "Yes", "ul", "No", "ulnone")',
  595.      'FStrike',  '$rtf .= &select_attr($data, "Strike",
  596.                   "Yes", "strike", "No", "strike0")',
  597.      'FSupScript',  '$rtf .= &select_attr($data, "SupScript",
  598.                   "Yes", "up6", "No", "up0")',
  599.      'FSubScript',  '$rtf .= &select_attr($data, "SubScript",
  600.                   "Yes", "dn6", "No", "dn0")',
  601.      'FChangeBar',  '$rtf .= &select_attr($data, "ChangeBar",
  602.                   "Yes", "revised", "No", "revised0")',
  603.      'FOutline',  '$rtf .= &select_attr($data, "Outline",
  604.                   "Yes", "outl", "No", "outl0")',
  605.      'FShadow',  '$rtf .= &select_attr($data, "Shadow",
  606.                   "Yes", "shad", "No", "shad0")',
  607.      'FSeparation',  '$rtf .= &change_attr("cf", $data)'
  608.      );
  609. #    &debug("char format: $rtf");
  610.     $rtf;
  611. }
  612.  
  613. ########
  614.  
  615. sub convert_frame{
  616.     local($s) = @_;
  617. #@@ local(@BRect) = @BRect;
  618.     local(@ret);
  619.  
  620.     &debug("convert frame: $s");
  621.     &do_statement
  622.     ($s,
  623.      'TextRect',     'push(@ret, &convert_textrect($here))',
  624.      'Frame',        'push(@ret, &convert_frame($here))',
  625.      'ImportObject', 'push(@ret, &convert_picture($here))',
  626.      'BRect', 'push(@ret, &change_dims($data, "posx", "posy", "absw"))',
  627.      'FrameType', 'push(@ret, &select_attr($data, "FrameType",
  628.                     "Inline", "posyil",
  629.                     "Top", "posyt",
  630.                     "Bottom", "posyb",
  631.                     "Left", "posxl",
  632.                     "Right", "posxr",
  633.                     "Near", "posxi",
  634.                     "Far", "posxo"))'
  635.      );
  636.     @ret;
  637. }
  638.  
  639. sub convert_textrect{
  640.     local($tr) = @_;
  641.     local(@ret);
  642.  
  643.     &do_statement
  644.     ($tr,
  645.      'ID',
  646.      'local($_) = $TextRect{$data};
  647.      $_ && !$TextFlow{$_}++ ? push(@ret, &convert_flow($_))
  648.      : &debug("no flow or repeated flow:$data line: ",$Lines{$_});'
  649.      );
  650.     @ret;
  651. }
  652.  
  653. #
  654. # this is one alternative...
  655. #
  656. sub old_convert_textrect{
  657.     local($tr) = @_;
  658.     local($id, @ret);
  659.  
  660.     &debug("convert textrect $tr");
  661.     for($id = &data_search($tr, 'ID', 1); $id;
  662.     $id = &data_search($tr, 'TRNext', 1)){
  663.     local($s) = $TextRect{$id};
  664.     last if $TextRect[$s]++;
  665.     &debug("TextRectID = $id");
  666.     push(@ret, &convert_flow($s));
  667.     }
  668.     @ret;
  669. }
  670.  
  671. sub convert_table{
  672.     local($s) = @_;
  673.     local(@r);
  674.     local($rows) = 'push(@r, &convert_rows($here))';
  675.  
  676.     &do_statement
  677.     ($s,
  678.      'TblTitleContent', 'push(@r, &convert_flow($here))'
  679. #@@ for AVS, we need the table title content, but we captured
  680. #   all the rows as bitmaps.
  681. #@@     'TblH', $rows,
  682. #@@     'TblBody', $rows,
  683. #@@     'TblF', $rows
  684.      );
  685.  
  686.     @r;
  687. }
  688.  
  689. sub convert_rows{
  690.     local($s) = @_;
  691.     local(@r);
  692.  
  693.     &do_statement
  694.     ($s,
  695.      'Row', 'push(@r, "\\\\trowd ", &convert_cells($here), "\\\\row\n")'
  696.      );
  697.     @r;
  698. }
  699.  
  700. sub convert_cells{
  701.     local($s) = @_;
  702.     local(@r);
  703.  
  704.     &do_statement
  705.     ($s,
  706.      'Cell', 'push(@r, &convert_cell($here))'
  707.      );
  708.     @r;
  709. }
  710.  
  711. sub convert_cell{
  712.     local($s) = @_;
  713.     local(@r);
  714.  
  715.     #@@ cell formatting!
  716.     &do_statement
  717.     ($s,
  718.      'CellContent',
  719.      'push(@r, &convert_flow($here, "\\\\intbl "), "\\\\cell")'
  720.      );
  721.     @r;
  722. }
  723.  
  724. sub convert_picture{
  725.     local($s) = @_;
  726.  
  727.     local(@r, $color, $unixpath, $dipath, $epsi, $image, $brect);
  728.  
  729.     &do_statement
  730.     ($s,
  731.      'Separation', '$color = $data',
  732.      'ImportObFile', '$unixpath = $data',
  733.      'ImportObFileDI', '$dipath = $data',
  734.      'EPSI', '$epsi = $data',
  735.      'FrameImage', 'push(@r, "\\\\frameimage\n", $data)',
  736.      'BRect', '$brect = $data'
  737.      );
  738.  
  739.     &debug("picture: color($color) file($unixpath,$dipath) brect($brect)");
  740.  
  741.     if($brect){
  742.     local($l, $t, $h, $w) = &rtf_dimensions($brect);
  743.     unshift(@r, "\\picwGoal$w\\pichGoal$h\n");
  744.     }
  745.  
  746.     undef($unixpath) if $unixpath =~ /internal inset/;
  747.  
  748.     local($path);
  749.     if($dipath){
  750.     local($_) = &convert_string($dipath);
  751.     if(/<U>([^<]+)/){
  752.         $path = $1;
  753.     }else{
  754.         if($unixpath){
  755.         $path = &convert_string($unixpath);
  756.         }else { warn "no path in: $dipath"; }
  757.     }
  758.     }
  759.  
  760.     if($path){
  761.     push(@r, "\\xwdfile $path");
  762.     }
  763.     if($epsi){
  764.     $epsi =~ s/.*&%v\s*&//;
  765.     $epsi =~ s/\n&//g;
  766.     push(@r, "\\epsi\n", $epsi);
  767.     }
  768.  
  769.     @r = ("\\qc{\\pict\n", @r, "}\\par\n"); # put it in a centered paragraph @#
  770.     @r = ("{\\cf$color", @r, "}") if $color;
  771.     @r;
  772. }
  773.  
  774. sub convert_flow{
  775.     local($s, $Container) = @_;
  776.     local(@r, %Notes);
  777.  
  778.     &do_statement
  779.     ($s,
  780.      'Notes', '&save_notes($here)',
  781.      'Para', 'push(@r, &convert_paragraph($here))'
  782.      );
  783.     @r;
  784. }
  785.  
  786. sub save_notes{
  787.     local($s) = @_;
  788.     local($_, $n);
  789.  
  790.     foreach (&parts($s)){
  791.     $n = &data_search($_, 'ID', 1);
  792. #    &debug("note: $n is:", &expand_mif_statement($_));
  793.     $Notes{$n} = $_;
  794.     }
  795. }
  796.  
  797.  
  798. sub convert_paragraph{
  799.     local($s) = @_;
  800.     local(@fmt, @lines, @pre, @post, $HyperGroup);
  801.     # global($PgfTag);
  802.  
  803.     &do_statement($s,
  804.           'PgfTag',
  805.           'push(@fmt, &change_style(&convert_string($data)))',
  806.           'Pgf',
  807.           'push(@fmt, &convert_pgf_format($here))',
  808.           'PgfNumString',
  809.           'push(@lines, &convert_numstring($data))',
  810.           'ParaLine',
  811.           'push(@lines, &convert_paraline($here, *pre, *post), "\n")'
  812.           );
  813.  
  814.     &debug("convert_paragraph: ",$State{'PgfTag'});
  815.     print STDERR '.'; #@# status
  816.  
  817.     push(@lines, $HyperGroup), undef($HyperGroup) if $HyperGroup;
  818.     
  819.     local($group);
  820.     $RTFInfo{$group} .= join('', @lines)."\\par\n" #@#lines is arbitrary
  821.     if $group = $TagGroup{$State{'PgfTag'}};
  822.     &debug(%TagGroup);
  823.     &debug("tag: ", $State{'PgfTag'}, " group: ", $group);
  824.  
  825.     # $Container is, e.g. '\intbl ' (for tables)
  826.     return(@pre, @fmt, $Container, ¤t_tabs(),
  827.        @lines, "\\par ", @post, "\n");
  828. }
  829.  
  830. sub current_tabs{
  831.     local($rtf, $_);
  832.     
  833.     local($li) = &rtf_dimensions($State{'LIndent'});
  834.  
  835.     foreach (&rtf_dimensions($State{'TabStops'})){
  836.     $_ -= $li; #@# MIF to RTF mindset
  837.     $rtf .= "\\tx$_ ";
  838.     }
  839.     $rtf;
  840. }
  841.  
  842. sub convert_numstring{
  843.     local($string) = @_;
  844.     local(%State) = %State; # don't clobber font
  845.     local($numfont) = $State{'NumberFont'};
  846.     local($font) = $numfont ? &change_char_style($numfont) : '';
  847.     local($form) = $State{'NumFormat'};
  848.  
  849.     $string = &convert_string($string, $State{'Family'} ne 'Symbol');
  850.  
  851.     &debug("numstring($string) [font($numfont): $font form: $form]");
  852.     local($tabs) = ¤t_tabs();
  853.     "{\\field{\\fldinst PgfNumFormat $form}{\\fldrslt $font$tabs$string}}";
  854. }
  855.  
  856. sub change_style{
  857.     local($tag) = @_;    
  858.  
  859.     local($style, $_);
  860.  
  861.     if($PgfCatalog{"Style", $tag}){
  862.     &set_paragraph_format($tag);
  863.     $style = $PgfCatalog{"Style", $tag};
  864.     }else{
  865.     local($pgf_fmt) = $PgfCatalog{'Statement', $tag};
  866.     if($pgf_fmt){
  867.         &reset_paragraph_format();
  868.         &reset_character_format();
  869.         $style = &convert_pgf_format($pgf_fmt);
  870.         $PgfCatalog{"Style", $tag} = $style;
  871.         &save_paragraph_format($tag);
  872.     }else{
  873.         warn "No catalog entry for '$tag'";
  874.     }
  875.     }
  876.  
  877.     $State{'PgfTag'} = $tag;
  878.  
  879.     join('', "\\pard\\plain\\s", &intern(*Tag, $tag), " ", $style);
  880. }
  881.  
  882. sub change_char_style{
  883.     local($tag) = @_;    
  884.     local($style, $_);
  885.  
  886.     &reset_character_format();
  887.     $State{'FTag'} = $tag;
  888.  
  889.     local($s) = $FontCatalog{'Statement', $tag};
  890.     $s ? "\\plain" . &convert_char_format($s)
  891.     : '';
  892. }
  893.  
  894. sub convert_paraline{
  895.     local($s, *pre, *post) = @_;
  896.     #@# empty_hyper gets around the: <marker><font>hot-text<font> bug.
  897.     local(@text, $empty_hyper);
  898.  
  899.     &do_statement
  900.     ($s,
  901.      'String',
  902.      'push(@text, &convert_string($data, $State{"Family"} ne "Symbol"));
  903.           $empty_hyper=0;',
  904.      'Char',
  905.      'push(@text, &convert_character($data))',
  906.  
  907.      'ATbl',
  908.      'local(%State) = %State;
  909.           local(@tbl) = &convert_table($Tbl{$data});
  910.           $State{"TblPlacement"} =~ /Top|Left|Right|Near|Far/
  911.             ? push(@pre, "{", @tbl, "}") : push(@post, "{", @tbl, "}")',
  912.  
  913.      'AFrame',
  914.      'local(%State) = %State;
  915.           &debug("converting AFrame $data");
  916.           local(@af) = &convert_frame($AFrame{$data});
  917.           $State{"FrameType"} =~ /Top|Left|Right|Near|Far/
  918.             ? push(@pre, "{", @af, "}") : push(@post, "{", @af, "}")',
  919.  
  920.      'FNote',
  921.      'push(@text, &convert_footnote($Notes{$data}))',
  922.  
  923.      'XRefEnd',
  924.      'push(@text, &convert_xref_end($data))',
  925.  
  926.      #@@ there could be a problem with tagged fonts here:
  927.      # convert_char_format ignores tags. Catalog lookup occurs
  928.      # in its caller (e.g. convert_character_catalog).
  929.      # And this is its caller.
  930.      'Font',
  931.      'push(@text, $HyperGroup), undef($HyperGroup)
  932.         if $HyperGroup && !$empty_hyper;
  933.       push(@text, &convert_char_format($here))',
  934.  
  935.      'Marker',
  936.      'push(@text, &convert_marker($here)); $empty_hyper=1',
  937.  
  938.      'Variable',
  939.      'push(@text, &convert_definition("Variable",
  940.                        &data_search("VariableName", $here)))',
  941.      'XRef',
  942.      'push(@text, &convert_xref($here))'
  943.      );
  944.     @text;
  945. }
  946.  
  947. sub convert_string{
  948. # Change a MIF String datum (without newlines) to a rtf string
  949.   local($_, $do_hex) = @_;
  950.  
  951.   # put backslashes out of band
  952.   s/\\\\/\n\n/g;
  953.  
  954.   # undo MIFisms
  955.   s/^`//;
  956.   s/'.*//;
  957.   s/\\q/'/g;
  958.   s/\\Q/`/g;
  959.   s/\\</</g;
  960.   s/\\>/>/g;
  961.  
  962.   # convert hex stuff
  963.   $do_hex ? s/\\x(\w\w) /$FrameCode[hex($1)]/ge :
  964.       s/\\x(\w\w) /pack('C', hex($1))/ge;
  965.  
  966.   # protect RTFisms
  967.   s/\{/\n\{/;
  968.   s/\}/\n\}/;
  969.   s/\\t/\t/;
  970.   s/\\/\n\n/;
  971.   warn "@@unexpected backslash: $_" if /\\/;
  972.  
  973.   # change backslashes back
  974.   s/\n/\\/g;
  975.   $_;
  976. }
  977.  
  978. sub convert_character{
  979.     local($name) = @_;
  980.     $CharacterConversions{$name};
  981. }
  982.  
  983. sub convert_footnote{
  984.     return ("\\chftn{\\footnote\n", &convert_flow($_[0]), "}");
  985. }
  986.  
  987. sub convert_xref{
  988.     local($s) = @_;
  989.  
  990.     $XRefSrcText = &convert_string(&data_search($s, 'XRefSrcText'));
  991.     $XRefSrcFile = &convert_string(&data_search($s, 'XRefSrcFile'));
  992.  
  993.     return "{\\field{\\fldrslt ";
  994. }
  995.  
  996. sub convert_xref_end{
  997.     return "}{\\fldinst XRefSrcFile=$XRefSrcFile XRefSrcText=$XRefSrcText " .
  998.     "XRefFormat=" . $Definition{'XRef', $XRefName} . "}}";
  999. }
  1000.  
  1001. sub convert_definition{
  1002.     local($type, $name) = @_;
  1003.     #@# converts Frame variables literally to RTF
  1004.     return &convert_string($Definition{$type, $name});
  1005. }
  1006.  
  1007. sub convert_other{
  1008.     local($token, $text, $invisible) = @_;
  1009.     local($fldpriv) = $invisible ? '\fldpriv\v' : '';
  1010.     "{\\field$fldpriv{\\fldinst $token}{\\fldrslt $text}}";
  1011. }
  1012.  
  1013. sub convert_marker{
  1014.     local($type) = &data_search($_[0], 'MType', 1);
  1015.     local($text) = &convert_string(&data_search($_[0], 'MText', 1));
  1016.  
  1017.     if($type == 0 || $type == 1){
  1018.     #@@ these actually occur in pairs.
  1019.     return &convert_other('FrameHeaderFooter ' . ($type+1), $text, 1); #@#
  1020.     }elsif($type == 2){
  1021.     return "{\\v{\\xe $text}}"; #@# text needs to be interpreted
  1022.     }elsif($type == 3){
  1023.     $RTFInfo{'comment'} .= "$text\n";
  1024.     }elsif($type == 4){
  1025.     $RTFInfo{'subject'} .= "$text\n";
  1026.     }elsif($type == 5){
  1027.     $RTFInfo{'author'} .= "$text\n";
  1028.     }elsif($type == 6){
  1029.     $RTFInfo{'keywords'} .= "$text\n"; #@# glossary, really
  1030.     }elsif($type == 7){
  1031.     return '{\|}'; #@# equation marker->formula character
  1032.     }elsif($type == 8){
  1033.     $HyperGroup = "}{\\fldinst FrameHypertext $text}}";
  1034.     return "{\\field{\\fldrslt ";
  1035.     }elsif($type == 9){
  1036.     return &convert_other('FrameXRefMarker ', $text, 1); #@#
  1037.     }
  1038.     return &convert_other('FrameMarker ' . ($type+1), $text, 1); #@#
  1039. }
  1040.  
  1041. #
  1042. # Output routines
  1043. #
  1044.  
  1045. sub debug{
  1046.     print STDERR @_, " @$.\n" if $Debug;
  1047. }
  1048.  
  1049. ##############
  1050. #
  1051. #
  1052. sub initialize_state{
  1053.     @FrameCode = 
  1054.     ('',      '',      '',      '',      '\-',    '',      '\_',    '',
  1055.      '\tab',  '\line', "\266",  "\247",  '',      '',      '',      '',
  1056.      " ",     "\240",  " ",     " ",     " ",     "\255",  '',      '',
  1057.      '',      '',      '',      '',      '',      '',      '',      '',
  1058.      ' ',     '!',     '"',     '#',     "\$",    '%',     '&',     "\'",
  1059.      '(',     ')',     '*',     '+',     ',',     '-',     '.',     '/',
  1060.      '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
  1061.      '8',     '9',     ':',     ';',     '<',     '=',     '>',     '?',
  1062.      '@',     'A',     'B',     'C',     'D',     'E',     'F',     'G',
  1063.      'H',     'I',     'J',     'K',     'L',     'M',     'N',     'O',
  1064.      'P',     'Q',     'R',     'S',     'T',     'U',     'V',     'W', 
  1065.      'X',     'Y',     'Z',     '[',     "\\",    ']',     '^',     '_',
  1066.      "\`",    'a',     'b',     'c',     'd',     'e',     'f',     'g',
  1067.      'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
  1068.      'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
  1069.      'x',     'y',     'z',     '{',     '|',     '}',     '~',     '',
  1070. # 0x80
  1071.      "\304",  "\305",  "\307",  "\311",  "\321",  "\326",  "\334",  "\341",
  1072. # 0x88
  1073.      "\340",  "\342",  "\344",  "\343",  "\345",  "\347",  "\351",  "\350",
  1074. # 0x90
  1075.      "\352",  "\353",  "\355",  "\354",  "\356",  "\357",  "\361",  "\363",
  1076. # 0x98
  1077.      "\362",  "\364",  "\366",  "\365",  "\372",  "\371",  "\373",  "\374",
  1078. # 0xa0
  1079.      "**",    "\260",  "\242",  "\243",  "\247",  "\267",  "\266",  "\337",
  1080. # 0xa8
  1081.      "\256",  "\251",  "(tm)",  "\264",  "\250",  "",      "\306",  "\330",
  1082. # 0xb0
  1083.      "",      "",      "",      "",      "\225",  "",      "",      "",
  1084. # 0xb8
  1085.      "",      "",      "",      "\252",  "\272",  "",      "\346",  "\370",
  1086. # 0xc0
  1087.      "\277",  "\241",  "\254",  "",      "f",     "",      "",      "\253",
  1088. # 0xc8
  1089.      "\273",  "...",   "",      "\300",  "\303",  "\325",  "OE",    "oe",
  1090. # 0xd0
  1091.      "\255",  "--",    "``",    "''",    "`",     "'",     "",      "",
  1092. # 0xd8
  1093.      "\377",  "Y",     "/",     "\244",  "<",     ">",     "fi",    "fl",
  1094. # 0xe0
  1095.      "***",   "\267",  ",",     ",,",    "%.",    "\302",  "\312",  "\301",
  1096. # 0xe8
  1097.      "\313",  "\310",  "\315",  "\316",  "\317",  "\314",  "\323",  "\324",
  1098. # 0xf0
  1099.      "",      "\322",  "\332",  "\333",  "\331",  "i",     "^",     "~",
  1100. # 0xf8
  1101.      "\256",  "\257",  ".",     "\260",  "\270",  "''",    ",");
  1102.  
  1103.     %CharacterConversions =
  1104.     ('Tab', '\tab ',
  1105.      'HardSpace', '\~ ',
  1106.      'SoftHypen', '\_ ', #@#
  1107.      'DiscHypen', '\- ',
  1108.      'Cent', "\242", # from ISO8859-1
  1109.      'Pound', "\243",
  1110.      'Yen', "\245",
  1111.      'EnDash', "\255",
  1112.      'EmDash', '-', #@#
  1113.      'Dagger', '**', #@#
  1114.      'DoubleDagger', '***', #@#
  1115.      'Bullet', "\267",
  1116.      'HardReturn', '\line ',
  1117.      'EndOfPara', "\266",
  1118.      'EndOfFlow', "\247",
  1119.      'NumberSpace', ' ', #@#
  1120.      'ThinSpace', ' ', #@#
  1121.      'EnSpace', ' ',
  1122.      'EmSpace', ' '
  1123.      );
  1124.  
  1125.     local(%doc_defaults) =
  1126.     ('paperw', 12240,
  1127.      'paperh', 15840,
  1128.      'margl', 1800,
  1129.      'margr', 1800,
  1130.      'margt', 1440,
  1131.      'margb', 1440,
  1132.      'facingp', '',
  1133.      'TwoSides', 'No',
  1134.      'gutter', 0,
  1135.      'deftab', 720,
  1136.      'widowctrl', '',
  1137.      'hyphhotz', '',
  1138.      'fntsep', '',
  1139.      'ftnsepc', '',
  1140.      'ftncn', '',
  1141.      'endnotes', 1,
  1142.      'enddoc', 0,
  1143.      'ftntj', 0,
  1144.      'ftnbj', 1,
  1145.      'ftnstart', 1,
  1146.      'pgnstart', 1,
  1147.      'linestart', 1,
  1148.      'landscape', 0,
  1149.      'fracwidth', 0,
  1150.      'nextfile', '',
  1151.      'template', '',
  1152.      'makeback', '',
  1153.      'defformat', '',
  1154.      'revisions', '',
  1155.      'margmirror', '',
  1156.      'revprop', 3,
  1157.      'revbar', 3);
  1158.  
  1159.     local(%para_defaults) =
  1160.     ('snext', '',
  1161.      'sbasedon', '',
  1162.      'pard', '',
  1163.      's', '',
  1164.      'ql', '',
  1165.      'qr', '',
  1166.      'qj', '',
  1167.      'qc', '',
  1168.      'Alignment', 'ql',
  1169.      'LIndent', '0',
  1170.      'FIndent', '',
  1171.      'NumberFont', '',
  1172.      'fi', 0,
  1173.      'li', 0,
  1174.      'ri', 0,
  1175.      'sb', 0,
  1176.      'sa', 0,
  1177.      'sl', '',
  1178.      'intbl', '',
  1179.      'keep', '',
  1180.      'keepn', '',
  1181.      'WithNext', 'keep0',
  1182.      'sbys', '',
  1183.      'pagebb', '',
  1184.      'Placement', 'Anywhere',
  1185.      'noline', '',
  1186.      'TabStops', '',
  1187.      'tx', '',
  1188.      'tqr', '',
  1189.      'tqc', '',
  1190.      'tqdec', '',
  1191.      'tb', '',
  1192.      'brdrt', '',
  1193.      'brdrl', '',
  1194.      'brdrr', '',
  1195.      'box', '',
  1196.      'brdrs', '',
  1197.      'brdrth', '',
  1198.      'brdrsh', '',
  1199.      'brdrdb', '',
  1200.      'brdrdot', '',
  1201.      'brdrhair', '',
  1202.      'brsp', '',
  1203.      'tldot', 1,
  1204.      'tlhyph', '',
  1205.      'tlul', '',
  1206.      'tlth', '');
  1207.  
  1208.     local(%char_defaults) =
  1209.     ('f', '',
  1210.      'Family', '',
  1211.      'b', '',
  1212.      'Weight', 'b0',
  1213.      'i', '',
  1214.      'Angle', 'i0',
  1215.      'strike', '',
  1216.      'Strike', 'strike0',
  1217.      'outl', '',
  1218.      'Outline', 'outl0',
  1219.      'shad', '',
  1220.      'Shadow', 'shad0',
  1221.      'scaps', '',
  1222.      'caps', '',
  1223.      'v', '',
  1224.      'fn', '',
  1225.      'fs', '24',
  1226.      'expnd', 0,
  1227.      'ul', '',
  1228.      'ulw', '',
  1229.      'uld', '',
  1230.      'uldb', '',
  1231.      'ulnone', '',
  1232.      'Underline', 'ulnone',
  1233.      'up', '',
  1234.      'SupScript', 'up0',
  1235.      'dn', '',
  1236.      'SubScript', 'dn0',
  1237.      'revised', '',
  1238.      'ChangeBar', 'revised0',
  1239.      'cf', 0
  1240.      );
  1241.  
  1242.     local($_);
  1243.     @DocumentAttrs = keys(%doc_defaults);
  1244.     foreach (@DocumentAttrs){
  1245.     push(@DocumentDefaults, $doc_defaults{$_});
  1246.     }
  1247.     @State{@DocumentAttrs} = @DocumentDefaults;
  1248.  
  1249.     @ParagraphAttrs = keys(%para_defaults);
  1250.     foreach (@ParagraphAttrs){
  1251.     push(@ParagraphDefaults, $para_defaults{$_});
  1252.     }
  1253.     @State{@ParagraphAttrs} = @ParagraphDefaults;
  1254.  
  1255.     @CharacterAttrs = keys(%char_defaults);
  1256.     foreach (@CharacterAttrs){
  1257.     push(@CharacterDefaults, $char_defaults{$_});
  1258.     }
  1259.     @State{@CharacterAttrs} = @CharacterDefaults;
  1260. }
  1261.  
  1262. sub reset_paragraph_format{
  1263.     @State{@ParagraphAttrs} = @ParagraphDefaults;
  1264.     '';
  1265. }
  1266.  
  1267. sub set_paragraph_format{
  1268.     local($tag) = @_;
  1269.     local($_);
  1270.     &reset_paragraph_format();
  1271.     &reset_character_format();
  1272.     grep($State{$_} = $PgfCatalog{$_, $tag},
  1273.      @ParagraphAttrs, @CharacterAttrs);
  1274. }
  1275.  
  1276. sub save_paragraph_format{
  1277.     local($tag) = @_;
  1278.     local($_);
  1279.     grep($PgfCatalog{$_, $tag} = $State{$_},
  1280.      @ParagraphAttrs, @CharacterAttrs);
  1281. }
  1282.  
  1283. sub reset_character_format{
  1284.     @State{@CharacterAttrs} = @CharacterDefaults;
  1285.     '';
  1286. }
  1287.     
  1288. ###############
  1289. #
  1290. #
  1291.  
  1292. sub rtf_begin_doc{
  1293.     return "{\\rtf1\\ansi\n";
  1294. }
  1295.  
  1296. sub rtf_end_doc{
  1297.     return "}\n";
  1298. }
  1299.  
  1300. sub rtf_info{
  1301.     local(%groups) = @_;
  1302.     local(@r, $_);
  1303.  
  1304.     push(@r, "{\\info\n");
  1305.     foreach (keys %groups){
  1306.     push(@r, "{\\$_ ", $groups{$_}, "}\n");
  1307.     }
  1308.     push(@r, "}\n");
  1309.     return @r;
  1310. }
  1311.  
  1312. sub intern{
  1313.     local(*arr, $_) = @_;
  1314.     s/^\`//;
  1315.     s/\'.*//;
  1316.     $arr{$_} || ($arr{$_} = ++$arr);
  1317. #    $_ = $arr{$_} || ($arr{$_} = ++$arr);
  1318. #    &debug("intern: $_[1] -> $_");
  1319. #    $_;
  1320. }
  1321.  
  1322. sub rtf_font_table{
  1323.     #
  1324.     # HEURISTIC: uses \fnil for unrecognized fonts.
  1325.     # recognizes all FrameMaker 2.1 fonts
  1326.     #
  1327.     local(%fonts) = @_;
  1328.     local(@r);
  1329.  
  1330.     local($n, $family, $_);
  1331.     foreach (keys %fonts){
  1332.     $family = 'nil';
  1333.     $n = $fonts{$_};
  1334.     $family = 'roman' if /serif/ || /times/i || /palatino/i
  1335.         || /bookman/i || /newcenturysch/;
  1336.     $family = 'swiss' if /sans/ || /helvetica/i || /avantgarde/i;
  1337.     $family = 'modern' if /courier/i;
  1338.     $family = 'script' if /cursive/i;
  1339.     $family = 'decor' if /zapfchancery/i;
  1340.     $family = 'tech' if /symbol/i;
  1341.  
  1342.     push(@r, "{\\f$n\\f$family $_;}\n");
  1343.     }
  1344.     return("{\\fonttbl\n", @r, "}\n");
  1345. }
  1346.  
  1347.  
  1348. sub rtf_style_sheet{
  1349.     local(%styles) = @_;
  1350.     local(@r);
  1351.  
  1352.     local($n, $style, $_);
  1353.     foreach (keys %styles){
  1354.     $n = &intern(*Tag, $_);
  1355.     #@# all styles mentioned in the document will appear in the
  1356.     # stylesheet, but only styles used in the document will be defined!
  1357.     $style = $PgfCatalog{'Style', $_};
  1358.     push(@r, "{\\s$n $style$_;}\n");
  1359.     }
  1360.     return("{\\stylesheet\n", @r, "}\n");
  1361. }
  1362.  
  1363.  
  1364. sub rtf_color_table{
  1365.     local(@colors) = @_;
  1366.  
  1367.     local($red, $green, $blue, $_);
  1368.     foreach (@colors){
  1369.     ($red, $green, $blue) = split(/\s+/, $_);
  1370.     $red = 255 * $red;
  1371.     $green = 255 * $green;
  1372.     $blue = 255 * $blue;
  1373.     push(@r, "\\red$red\\green$green\\blue$blue;\n");
  1374.     }
  1375.     return("{\\colortbl\n", @r, "}\n");
  1376. }
  1377.  
  1378. sub rtf_dimensions{
  1379.   local($_, $twips) = @_;
  1380.   local(@trect);
  1381. #
  1382. # convert all dimensions to twips
  1383. # didot@@ cicero@@
  1384. #
  1385.   while($_){
  1386.     if( s/\s*(-?\d+(\.\d*)?)\s*//){
  1387.       $twips = $1 * 1440;
  1388.     }else{
  1389.       warn "@@Bad dimensions: $_\n";
  1390.       return ();
  1391.     }
  1392.     s/"//;
  1393.     s/in//;
  1394.     s/pt// && ($twips = $twips/72);
  1395.     s-cm-- && ($twips = $twips/2.54);
  1396.     s-mm-- && ($twips = $twips/25.4);
  1397.     push (@trect, int($twips));
  1398.   }
  1399.   @trect;
  1400. }
  1401.  
  1402.  
  1403. ##############
  1404. #
  1405. # tchrists' assert stuff
  1406. #
  1407.  
  1408. sub assert {
  1409.     &panic("ASSERTION BOTCHED: $_[0]",$@) unless eval $_[0];
  1410.  
  1411. sub panic {
  1412.  
  1413.     select(STDERR);
  1414.  
  1415.  
  1416.     print "\npanic: @_";
  1417.  
  1418.     exit 1 if $] <= 4.003;  # caller broken
  1419.  
  1420.     # stack traceback stolen from perl debugger
  1421.  
  1422.     local($i,$_);
  1423.     local($p,$f,$l,$s,$h,$a,@a,@sub);
  1424.     for ($i = 1; ($p,$f,$l,$s,$h,$w) = caller($i); $i++) {
  1425.     @a = @DB'args;
  1426.     for (@a) {
  1427.         if (/^StB\000/ && length($_) == length($_main{'_main'})) {
  1428.         $_ = sprintf("%s",$_);
  1429.         }
  1430.         else {
  1431.         s/'/\\'/g;
  1432.         s/([^\0]*)/'$1'/ unless /^-?[\d.]+$/;
  1433.         s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg;
  1434.         s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg;
  1435.         }
  1436.     }
  1437.     $w = $w ? '@ = ' : '$ = ';
  1438.     $a = $h ? '(' . join(', ', @a) . ')' : '';
  1439.     push(@sub, "$w&$s$a from file $f line $l\n");
  1440.     last if $signal;
  1441.     }
  1442.     for ($i=0; $i <= $#sub; $i++) {
  1443.     last if $signal;
  1444.     print $sub[$i];
  1445.     }
  1446.     kill 'TERM', -$Start_Pid;
  1447.     exit 1;
  1448. @//E*O*F mif2rtf.pl//
  1449. chmod u=rwx,g=rx,o=rx mif2rtf.pl
  1450.  
  1451. exit 0
  1452.  
  1453.