I think you might be looking for something like this:
(defvar se-wrap-executable "C:/Program Files/Wraparound/Wrap/wrap.exe"
"Wrap is an open source command line tool that is able to
convert Fountain and Wrap files into a correctly formatted
screen- or stageplay as an HTML or a PDF. See URL
(defvar se-wrap-out-dir default-directory
"Output directory for converted source.
Must end in slash. Errors may occur if path contains spaces.
See explanation at URL `https://emacs.stackexchange.com/a/59198/15177'.")
(defun se-wrap-src-block ()
"Call wrap on an Org source block at point.
Output to pdf in `se-wrap-out-dir' using the name of
the subtree containing the source block."
(let* ((element (org-element-at-point))
(type (org-element-type element))
(src (if (string= type "src-block")
(org-element-property :value element)))
(tempfile (concat (temporary-file-directory) "tempfile"))
(headline (org-entry-get nil "ITEM"))
(outfile (concat se-wrap-out-dir "\"" headline "\".pdf"))
(command (concat "\"" se-wrap-executable "\" pdf " tempfile " -o " outfile)))
(message "%s" (shell-command-to-string command))
(message "Output: %s" outfile))
(t (message "Not a src-block")))))
Let's break it down.
We need the content of the source block at point. Org has two ways we might get it, the element parser or entry properties. The former parses an Org buffer into an abstract syntax tree composed of elements. We get the element at point with
org-element-at-point, check that it is a source block with
org-element-type, and obtain the contents using
org-element-property. According to the Org Element API, the
:value property contains the source code in a block as a string1.
We also need the headline containing the source block. Entry properties are more useful here since the point is at the source block. It's not entirely clear to me what an "entry" is, but it seems to be a subtree. Executing
org-entry-properties while in a source block shows that
"ITEM" corresponds to the headline. This can be obtained with
org-entry-get (n.b. not
So far so good.
Now we need to get this information to the
wrap program. There are several ways this could be done. I chose
shell-command-to-string which allows us to send a string to a shell process and who returns the result as a string.
The major challenge is how to present information to
wrap. Does it accept files or can it work with streams? It turns out both. However, sending a string directly to
wrap is a giant pain because of shell quoting. It's much simpler to dump the source content into a temp file and pass that to
wrap. Even still, some fiddling has to happen with quotes. A headline or file path may contain a space. As written, don't include spaces in the
Otherwise, there's some mundane grunt work done to set up the right environment, construct the
wrap shell command, and clean up. Hopefully this is all self-explanatory (via
C-h f and
There are various style details to consider. You could refactor to pass arguments rather than use globals. You also might want to save the source before converting it. This would help ensure the source and output are in sync, but it's not strictly necessary. I have included that option as a comment. Of course, you can also bind things to a single key:
(global-set-key (kbd "<f5>") 'se-wrap-src-block)
This was written using Org 9.3.1 and Emacs 26.3.
I had fun writing this and I hope it helps! I wouldn't want you to break anonymity, but maybe we'll see something of yours on the silver screen soon, if not in a community theater nearby! Cheers!
1 The way this works is that
:value is a keyword (constant variable) of the element symbol. See https://www.gnu.org/software/emacs/manual/html_node/elisp/Symbol-Type.html
2 The code for the
outfile is not safe against spaces. The
headline is quoted but then it's not easy to "quote the quotes" to protect against spaces in
se-wrap-out-dir. You might be able to use something like
convert-standard-filename here. I have little patience for these kinds of details, so I "leave this as an exercise to the reader".