[comp.sys.mac] LONG MESSAGE: LaTeX & Scribe --> MS Word Documents

spohrer-james@yale.UUCP (11/07/87)

From: James Spohrer <spohrer-james>


Does anyone know of a way to convert Latex and/or Scribe documents
into MS Word documents so that all the text formatting commands
in Latex and Scribe get converted?

I have written a first pass version of a COMMON LISP program
that will do some of the necessary translation. It works by
first converting the LaTeX or Scribe document into RTF format
and then WORD can read in and translate the RTF format. It does
a far from perfect translation, but at least it is better than
editting out all the LaTex and Scribe formatting commands
by hand.

Here is the code -- about 3.5 pages:
;*************************************************************************
;***    LATEX to RTF (WORD) CONVERTER............Jim Spohrer 11/04/87
;***    Please send any improvements to: 51 Prospect St., New Haven, CT
;***    Or SPOHRER@YALE; Telephone (203) 432-1227.
;***
;***    This COMMON LISP code can be used to convert LATEX files (*.TEX)
;***    into RTF files (*.RTF). Since MicroSoft Word converts RTF files to
;***    normal word files, this program allows old Latex files to be moved
;***    to Word on the Macintosh.
;***
;(latex->rtf "JCS:Latex:test.tex" "JCS:Word:test.rtf")
(defun latex->rtf (latex-file rtf-file) (convert-file latex-file rtf-file))

(defun convert-file (in-file-string out-file-string)
  (close *IN-STREAM*) (close *RTF-STREAM*)
  (psetf *OPEN* nil *OBEY-CRLF* nil *INDENT-ENV* nil *CLOSE-CHAR* nil)
  (if (and (setf *IN-STREAM* (open in-file-string))
           (setf *RTF-STREAM* (open out-file-string :direction :output)))
    (progn (write-rtf-header) (translate))
    (format t "~%Could be: in-file not found, or out-file already exists"))
  (close *IN-STREAM*) (close *RTF-STREAM*))

(defun <-l () (read-line *IN-STREAM* nil))
(defun <-c () (read-char *IN-STREAM* nil))
(defun <-p () (peek-char nil *IN-STREAM* nil))
(defun ->s (l) 
  (apply #'format (if (stringp l) 
                    (list *RTF-STREAM* l) 
                    (cons *RTF-STREAM* l))))

(psetf *IN-STREAM* nil  *RTF-STREAM* nil)
(psetf *OPEN* nil *OBEY-CRLF* nil *INDENT-ENV* nil *CLOSE-CHAR* nil)
(setf *GROUPERS* '((#\{ #\})))

(setf *SKIP-LINE*
      '(|documentstyle| |oddsidemargin| |evensidemargin| |topmargin| |topsep| 
        |headheight| |headsep| |textheight| |textwidth| |footheight| |parskip|
        |footskip| |itemsep|))
(setf *C-EASY*
      '((|rm| "\\plain") (|tt| "\\f3") (|it| "\\i") (|em| "\\i") (|bf| "\\b")
        (|huge| "\\fs36") (|LARGE| "\\fs36") (|Large| "\\fs36")
        (|large| "\\fs36") (|normalsize| "\\fs28") (|small| "\\fs20")
        (|footnotesize| "\\fs18") (|tiny| "\\fs16")
        (|item| "\\par~%\\li630 \\'a5") (|\\| "\\par~%")
        (|flushleft| "\\pard~%") (|verbatim| "\\f3 ")
        (|abstract| "\\par  \\qc {\\b ABSTRACT}\\pard~%\\li630\\fs18")))
(setf *C-SOSO*
      '((|caption| "\\pard {\\fs18 CAPTION: ~%") 
        (|footnote| "{\\fs18 \\footnote ") (|section| "{\\b\\fs36 ~%")
        (|subsection| "{\\b\\fs36 ~%") (|paragraph| "{\\b\\fs36 ~%")))
(setf *C-SEMI* nil)
(setf *C-HARD* nil)
(setf *P-EASY*
    '((|abstract| "\\par  \\qc {\\b ABSTRACT\\par}\\pard~%\\fs20 \\li630 {")
      (|center| "\\qc {") (|footnote| "\\fs18 \\footnote {")
      (|itemize| "  {") (|enumerate|) (->s "  {")
      (|document| "\\pard {~%") (|flushleft| "\\pard {~%")))
(setf *P-SOSO*
      '((|figure| "\\pard {\\box~%") (|verbatim| "\\f3 {")))
(setf *P-SEMI* nil)

(defun write-rtf-header ()
(->s "{\\rtf0\\mac {\\fonttbl{\\f0\\fswiss Chicago;}{\\f2\\froman New 
York;}{\\f3\\fswiss Geneva;}{\\f4\\fmodern Monaco;}{\\f5\\fscript 
Venice;}{\\f20\\froman Times;}{\\f21\\fswiss Helvetica;}{\\f22\\fmodern 
Courier;}}{\\stylesheet{\\sbasedon222\\snext0~%Normal;}}\\widowctrl\\ftnbj 
\\sectd \\linemod0\\linex0\\cols1\\colsx0\\endnhere \\pard\\plain {\\b\\fs36
LATEX to RTF\\par~%}\\pard"))

(defun translate ()
  (loop
    (let ((ch (<-c)))
      (if (null ch) (return (and *open* (->s "\\par }~%")))
        (case ch
          ((#\\) (handle-command))
          ((#\%) (<-l))
          ((#\newline)
           (and *OBEY-CRLF* (->s " \\par ")) 
           (if (eq #\newline (<-p))
             (progn (<-c)
                    (if *open*
                         (->s "\\par~% }\\pard {\\par ~%")
                         (progn (->s "{\\par ~%") (setf *open* t))))
             (->s " ~%")))
          ((#\})
           (if (eq #\newline (<-p)) 
             (->s "\\par } \\pard ~%") 
             (->s (list "~a" ch))))
          (t (->s (list "~a" ch))))))))
          
(defun handle-command ()
  (let ((command (read-command)) (easy nil) (soso nil) (semi nil) (hard nil))
    (cond ((memq command *SKIP-LINE*) (<-l))
          ((setf easy (cadr (assq command *C-EASY*))) (->s easy))
          ((setf soso (cadr (assq command *C-SOSO*))) 
           (read-to-next-open) (->s soso))
          ((setf semi (cadr (assq command *C-SEMI*))) nil)
          ((setf hard (cadr (assq command *C-HARD*)))
           (->s (list hard (read-parameter))))
          ((eq command '|end|) 
           (let ((parameter (read-parameter)))
             (and *OBEY-CRLF* (assq parameter *P-SOSO*) 
                  (setf *OBEY-CRLF* nil))
             (and *INDENT-ENV* (eq parameter (car *INDENT-ENV*)) 
                  (pop *INDENT-ENV*))
             (->s "\\par~%} \\pard~%")))
          ((eq command '|begin|) 
           (let ((parameter (read-parameter)))
             (cond ((setf easy (cadr (assq parameter *P-EASY*))) (->s easy))
                   ((setf soso (cadr (assq parameter *P-SOSO*)))
                    (setf *OBEY-CRLF* t) (->s soso))
                   ((setf semi (cadr (assq parameter *P-SEMI*)))
                    (push parameter *INDENT-ENV*) (->s semi))
                   (t (->s (list " { ~a" parameter))))))
          (t (->s (list "\\par \\pard ~%~a \\par \\pard ~%" command))))))

(defun read-command ()
    (list->symbol (get-alpha-char-list (list (<-c)))))

(defun read-parameter () 
  (and (open-p (<-p)) (push (open-p (<-c)) *CLOSE-CHAR*)
       (prog1 (list->symbol (get-alpha-char-list nil)) 
         (read-to-next-close))))

(defun list->symbol (l) (intern (coerce l 'string)))
(defun get-alpha-char-list (l) 
  (loop (if (alphanumericp (<-p)) (push (<-c) l) (return (reverse l)))))
(defun read-to-next-open () 
  (if (open-p (<-p)) (push (open-p (<-c)) *CLOSE-CHAR*) (->s "}")))
(defun read-to-next-close ()
  (loop (if (null (<-p)) (return nil)
            (and (eq (car *CLOSE-CHAR*) (<-c)) 
                 (return (pop *CLOSE-CHAR*))))))
(defun open-p (c) (cadr (assq c *GROUPERS*)))

;****************************************************************************
;*** SCRIBE to RTF (WORD) Converter.............Jim Spohrer 11/04/87
;***
;*** To Convert Scribe Files (*.MSS) instead of Latex (*.TEX), make
;*** the following substitutions into the above code.
;***
;(scribe->rtf "JCS:Scribe:test.mss" "JCS:Word:test.rtf")
;(defun scribe->rtf (scribe-file rtf-file) (convert-file scribe-file
rtf-file))
;
;(setf *GROUPERS* '((#\{ #\}) (#\( #\)) (#\[ #\])))
;
;(setf *SKIP-LINE*
;      '(|make| |define| |device| |libraryfile| |pageheading| |newpage| 
;        |blankspace|))
;(setf *C-EASY* nil)
;(setf *C-SOSO*
;      '((|r| "{\\plain ") (|t| "{\\f3 ") (|sb| "{\\b\\fs20 ") (|i| "{\\i ") 
;  (|center| "\\qc {") (|flushleft| "\\pard {~%") (|section| "{\\b\\fs36 ~%")
;  (|subsection|"{\\b\\fs36 ~%") (|paragraph| "{\\b\\fs36 ~%") 
;  (|appendix| "{\\b\\fs36 ~%") (|Section| "{\\b\\fs36 ~%") (|b| "{\\b ") 
;  (|Subsection| "{\\b\\fs36 ~%") (|Paragraph| "{\\b\\fs36 ~%")
;  (|Appendix| "{\\b\\fs36 ~%") (|caption| "\\pard {\\fs18 CAPTION: ~%")
;  (|footnote| "{\\fs18 \\footnote ") (|verbatim| "\\f3 ")
;  (|abstract| "\\par  \\qc {\\b ABSTRACT}\\pard~%\\li630\\fs18")))
;(setf *C-SEMI*
;      '((|qt| t)))
;(setf *C-HARD*
;      ' ((|tag| "{ \\b TAG: ~a } ") (|ref| "{ \\b REF: ~a } ")))
;(setf *P-EASY*
;      '((|center| "\\qc {") (|flushleft|  "\\pard {~%") 
;        (|footnote|  "\\fs18 \\footnote {")
;        (|abstract| "\\par  \\qc {\\b ABSTRACT}\\par~%\\fs20 \\li630 {")))
;(setf *P-SOSO*
;      '((|verbatim| "\\f3 { ") (|figure| "\\pard {\\box~%") 
;        (|fullpagefigure| "\\pard {\\box~%")))
;(setf *P-SEMI*
;      '((|itemize| "  { \\li630 \\'a5 ") (|enumerate| "  { \\li630 \\'a5 ")
;        (|new| "  { \\li630 \\'a5 ")))
;
;(defun translate ()
;  (loop
;    (let ((ch (<-c)))
;      (if (null ch) (return (and *OPEN* (->s "\\par }~%")) )
;        (case ch
;          ((#\@) (handle-command))
;;*** COMMENT CHARACTER FOR SCRIBE (WHAT IS IT?)         ((#\%) (<-l))
;          ((#\newline)
;           (and *OBEY-CRLF* (->s " \\par ")) 
;           (if (or (eq #\@ (<-p)) (eq #\newline (<-p)))
;             (progn (and (eq #\newline (<-p)) (<-c))
;                    (if *INDENT-ENV*
;                      (if *OPEN*
;                        (->s  "\\par~%}\\pard \\li630 {\\par \\'a5 ~%")
;                        (progn
;                          (->s  " \\li630 {\\par \\'a5 ~%") 
;                          (setf *OPEN* t)))
;                      (if *OPEN*
;                        (->s  "\\par~%}\\pard {\\par ~%")
;                        (progn
;                          (->s  "{\\par ~%") (setf *OPEN* t)))))
;             (->s " ~%")))
;          (t (if (and *CLOSE-CHAR* (eq (car *CLOSE-CHAR*) ch))
;               (progn
;                 (pop *CLOSE-CHAR*)
;                 (if (or (eq #\@ (<-p)) (eq #\newline (<-p)))
;                   (->s  "\\par } \\pard ~%")
;                   (->s "}")))
;               (->s (list "~a" ch)))))))))
;***************************************************************************
-------