One must imagine Sisyphus happy#
ISMRM is the goto conference for the MRI community. It’s huge, and to go there you have to submit an abstract.
The submission happens online, and the submission must be done through a webpage, and each section of the abstract has to be entered in a form.
They have rudimentary support for formatting, and any mathematics needs to be surrounded in $$
display-style, or $$$
for inline maths.

There is also a word limit, (for 2025 its 750 words, but it used to me more), and a 100 words synopsis to add. Figures need to added separately, and the reconstruction. Also, references are indicated as superscript, and filled in a dedicated region. Frankly, this could be a million time better with just 2 boxes: on for markdown, the other for a BibTex, a inch closer to what JOSS is proposing for instance.
Let’s be honest, it’s a crappy interface to write science. And you probably know where we are going: Let’s use org-mode instead.
Counting word#
The first particularity of ismrm is how they count words, which differs from the M-x count-word
emacs function. In particular, a “word” for ismrm is delimited by whitespace. Looking at the source-code of the abstract upload page and looking for the “count” keyword I foud this javascript function:
function countEditorWords(editor, cntCounter) {
var content, contentcharacter = "";
content = editor.get_text().replace(/ /gi, '');
var words = 0;
if (content) {
var punctRegX = /[!\.?;,:&_\-\-\{\}\[\]\(\)~#'"]/g;
var contentcontent = content.replace(punctRegX, "");
var trimRegX = /(^\s+)|(\s+$)/g;
contentcontent = contentcontent.replace(trimRegX, "");
if (content) {
splitRegX = /\s+/;
var array = contentcontent.split(splitRegX);
words = array.length;
}
contentcharacter = content.replace(/\s/g, "") //Replace all character returns line breaks, and all whitespaces
if (contentcharacter == 0)
words = 0;
}
var counter = $get(cntCounter);
var characters = contentcharacter.length
counter.innerHTML = "Words: " + words + " Characters: " + characters;
counter.setAttribute("CharacterCount", characters);
counter.setAttribute("WordCount", words);
}
With the help of $favorite-language-model
we can translate this into emacs-lisp, and extends it further:
(require 'org)
(defvar count-ismrm-punctregx "[!\.?;,:&_\-\-\{\}\[\]\(\)~#'\"/]"
"Regular expression for punctuation.")
(defvar count-ismrm-trimregx "(^\s+)|(\s+$)"
"Regular expression for trimming.")
(defun my-org-concatenate-entry-bodies ()
"Concatenate the bodies of all Org entries, skipping those tagged with 'nocount'."
(interactive)
(let ((result ""))
(org-map-entries
(lambda ()
(unless (member "nocount" (org-get-tags)) ;; Check if 'nocount' tag is present
(let ((entry-body (org-get-entry))) ;; Get the body of the entry
(setq result (concat result "\n" entry-body)))))
nil 'file) ;; Process all entries in the current file
result))
(defun ismrm-count-word-string (content)
"Count word in the string CONTENT."
(let ((words 0) (contentcharacter ""))
(setq content (replace-regexp-in-string " " "" content))
(let ((contentcontent (replace-regexp-in-string count-ismrm-punctregx "" content)))
(setq contentcontent (replace-regexp-in-string count-ismrm-trimregx "" contentcontent))
(if content
(progn
(let ((array (split-string contentcontent)))
(setq words (length array)))))
(setq contentcharacter (replace-regexp-in-string "\s" "" content))
(if (= (length contentcharacter) 0)
(setq words 0)))
(list words (length contentcharacter))))
(defun ismrm-count-word ()
"Count the number of words in the current buffer or the active region.
If PREFIX is non-nil, count the number of words in the current org subtree.
"
(interactive "P")
(let ((word-length (cond ((region-active-p) (ismrm-count-word-string (buffer-substring-no-properties (region-beginning) (region-end))))
(current-prefix-arg (ismrm-count-word-string (substring-no-properties (org-get-entry))))
;; concatenate all the entries in the buffers, ignoring headings, and heading with :nocount: tag
(t (ismrm-count-word-string (my-org-concatenate-entry-bodies)))))
(math-space-count (count-whitespace-math)))
(message "Words: %d, Characters: %d, Math spaces: %d"
(car word-length) (cadr word-length) math-space-count)))
Now at the top of the org-mode file, I have:
#+begin_src emacs-lisp
(load "ismrm.el")
(ismrm-count-word)
#+end_src
#+RESULTS:
Words: 750, Characters: 5589, Math spaces: 0
But what is “Math spaces” ? Well remember how the words are counted by counting spaces in the body of text ? turns out that you can write equation in \LaTeX / MathJax without any space, and get extra word counts for free. Let’s write a nice function for that as well.
(defun count-whitespace-math ()
"Count the number of spaces used in math environment"
(interactive "P")
(save-excursion
(goto-char (point-min))
(let ((count 0))
(while (re-search-forward " +" nil t)
(when (let ((cur-face (get-char-property (point) 'face)))
;; use existing syntax highlighting instead of fiddling with regex.
(cond ((listp cur-face) (member 'font-latex-math-face cur-face))
((eq cur-face 'font-latex-math-face) t)))
(setq count (1+ count))))
count
)))
Nice citation formatting in HTML and LaTeX#
Org-mode has prime support citations, and thanks to my colleague Chaithya GR we have a nice CSL format to comply with the guideline of ISMRM. Simply adding #+cite_export: csl imsmrm.csl
does the trick.
Since this is org-mode the export to PDF or any other format is possible.
Copy to the ISMRM website#
It’s nice to have an ISMRM abstract written in org-mode, but how do we get it to the upload website ? Turns-out the upload form proposes a “paste from Microsoft Word” option which in reality is more a “paste formatted HTNL” field. So the plan is the following:
- export section by section the org-mode content as HTML, ensure we have the correct MathJax formatting, place the results in the clipboard.
(defun ismrm-formatted-copy ()
"Export region to HTML using pandoc and copy it to the clipboard in rich text format."
(interactive)
(save-window-excursion
(let* ((buf (org-export-to-buffer 'html "*Formatted Copy*" nil nil t t))
(html (with-current-buffer buf (buffer-string))))
(with-current-buffer buf
;; replace the inline maths \( \) by triple dollar signs
(goto-char (point-min))
(while (re-search-forward "\\(\\\\\(\\)\\|\\(\\\\\)\\)" nil t)
(replace-match "$$$" nil nil))
(goto-char (point-min))
;; replace the display maths \[ \] by double dollar signs
(while (re-search-forward "\\(\\\\\\[\\)\\|\\(\\\\\\]\\)" nil t)
(replace-match "$$" nil nil))
(shell-command-on-region
(point-min)
(point-max)
"pandoc -f html -t html | xclip -t text/html -selection clipboard"))
(kill-buffer buf))))
The idea came from the https://kitchingroup.cheme.cmu.edu/blog/category/rtf/ (initially I thought that I had to use rtf format, but no, html works just fine).
Conclusion#
Now, this how I wrote abstracts

Well now I have no excuses to write those abstracts I guess. Its also nice to have everything in org-mode, I can then export it to pdf independently, and send it for review to the other co-authors before submissions.