Thursday, April 3, 2014

Emacs Word-to-TeX and TeX-to-Word macros

Grad students: tired of manually fixing up your professors' Word documents so you can incorporate them into your LaTeX papers?  Professors: tired of reading students' LaTeX drafts and wish you could just edit them in Word?

If you use Emacs, the below customizations can help.  Place them in your .emacs file somewhere, and run:

  • M-x texify-region to convert content pasted from Word into TeX-friendly content (It does the right thing for characters outside TeX's input set such as curly quotes, em- and en-dashes, and so on)

  • M-x wordify-region to go the other way

  • M-x empty-region to convert TeX paragraphs in the region (newline after each line, blank lines separate paragraphs) into Word-friendly text (one paragraph == one newline)

  • M-x copy-region-as-empty to do the above nondestructively, i.e. leaving your original TeX markup intact but copying a Word-friendly version to the kill ring (which doubles as the clipboard on non-broken PC & Mac implementations of Emacs)

  • M-x word-outline-to-latex  to convert a numbered headings outline (e.g. pasted from Word's outline mode view) into \section and \subsection hierarchy for LaTeX use

Have fun.

;;; turn the region into something suitable for pasting into Word or other
;;; non-ascii word processors

(defun change-many (change-list)
(dolist (subst change-list)
(goto-char (point-min))
(while (re-search-forward (car subst) nil t)
(replace-match (cadr subst) nil nil)))))
(provide 'change-many)

(defun wordify-region ()
"Nondestructively convert region of TeX and text-filled source for
pasting into MS Wurd, and leave converted region on kill ring"
(let ((buf (get-buffer-create "*wordify-temp*")))
(set-buffer buf)
(copy-to-buffer buf (point) (mark))
(set-buffer buf)
(change-many '(("\n\n" "~@@~")
("\n" " ")
("~@@~" "\n")
("``" "Ò")
("''" "Ó")
("\s-+" " "))
(copy-region-as-kill (point-min) (point-max))))))

(defun texify-region ()
"Destructively convert region of pasted-in Wurd text to be TeX-friendly."
(narrow-to-region (point) (mark))
(change-many '(("\\([^\\]\\)\\$" "\\1\\\\$")
("^%" "~@@~") ("\\([^\\]\\)%" "\\1\\\\%") ("~@@~" "%")
("’" "'")
("‘" "`")
("“" "``")
("”" "''")
("…" "\\\\ldots{}")
("\\.\\.\\." "\\\\ldots{}")
("\"\\([^\"]+\\)\"" "``\\1''")
("—" "---")
("–" "--")
("½" "$1/2$")
("¼" "$1/4$")
(fill-individual-paragraphs (point-min) (point-max))

;; ("\([^\\]\)%" "\\1\%")

(defun empty-region (nlines)
"Convert filled paragraphs to unfilled paragraphs in region. With prefix arg,
insert that many blank lines between paragraphs (default 0)."
(interactive "p")
(replace-all-in-region '(("\\\n\\\n+" "@@@@")
("\\s-*\\\n\\s-*" " ")
("@@@@" "\n"))))

(defun copy-region-as-empty ()
"Convert region to empty paragraphs and place it on the kill ring without
deleting it."
(empty-region 0)
(copy-region-as-kill (point) (mark))

(defun word-outline-to-latex ()
"Convert multilevel (numbered) Outline text pasted from Word into section,
subsection, etc. structure of laTeX."
(replace-all-in-region '(("^\\s-*[0-9]+\\s-+\\(.*\\)\\s-*$"
("^\\\\" "\n\\\\")

(defun replace-all-in-region (lst)
(narrow-to-region (point) (mark))
(let ((case-replace-search nil))
(dolist (pair lst)
(goto-char (point-min))
(while (re-search-forward (first pair) nil t)
(replace-match (second pair) nil nil))))))