This script converts the current OmniOutliner document to markdown, tex, and then PDF via pandoc.
If you apply an appropriately named named style to a row – e.g. “Heading 1,” “Heading 2,” “Blockquote,” “Ordered List” – then the script will automatically add the relevant markdown tag e.g. “#” or “>”. Special thanks to @SGIII for the assist with this.
Unfortunately, the script does not currently convert styled text within a row to its markdown equivalent, so you’ll need to use markdown tags for bold and italic text (for example). I’m still trying to figure out how to convert a rich text object to markdown using javascript. I gather it will involve iterating over the attribute runs in the object.
The markdown, tex, and pdf files – as well as all the ancillary LaTeX files – are created in a new folder on the desktop called “LaTeX.” Requires pandoc to be installed, creation/installation of a custom LaTeX template for pandoc containing the necessary front- and back-matter to be installed (e.g. “\begin{document}”), and editing the script to reflect the name of that template. Note that OO document filenames with special characters such as parentheses currently break the script.
function run() {
// Name of your pandoc template file (don't include the file extension); you'll need one of these to include necessary LaTeX front- and back-matter e.g. "\begin{document}." (You'll also need to look up where pandoc stores these and put yours there.)
var pandocTemplate = 'articletemplate';
// Setup
var app = Application.currentApplication();
app.includeStandardAdditions = true;
var OmniOutliner = Application('OmniOutliner');
// Get the current document
var doc = OmniOutliner.documents[0];
// Get the name (stripped of spaces) of the Omni Outliner document. The script may fail if your filename includes certain characters, e.g. parentheses. (You could also just replace this with something like "paper.")
var fileName = doc.name().replace(/\s/g, '');
// Create a directory on the desktop to hold our new files
var desktopString = app.pathTo("desktop").toString()
app.doShellScript(`mkdir -p ${desktopString}/LaTeX/`);
// The text of the paper
var paperText = "";
// Loop through rows and append their text to paperText
// TODO: how to convert the rich text styling to markdown instead of ignoring it?
doc.rows().forEach(function(theRow) {
if (Object.keys(theRow.style.namedStyles).length > 0) {
switch(theRow.style.namedStyles[0].name()) {
case "Heading 1":
paperText += "# ";
break;
case "Heading 2":
paperText += "## ";
break;
case "Heading 3":
paperText += "### ";
break;
case "Blockquote":
paperText += "> ";
break;
case "Ordered List":
paperText += "1. ";
break;
case "Unordered List":
paperText += "* ";
break;
}
}
paperText += theRow.cells[1].richText();
paperText += "\r\r";
});
// Convert the text of the paper to UTF8 encoding so pandoc can read it
paperText = $.NSString.alloc.initWithUTF8String(paperText);
// Write paperText to a new markdown file
var file = `${desktopString}/LaTeX/${fileName}.md`
paperText.writeToFileAtomicallyEncodingError(file, true, $.NSUTF8StringEncoding, null);
// Use pandoc to convert that markdown file to a tex file
shellCommand = `/usr/local/bin/pandoc ${desktopString}/LaTeX/${fileName}.md -f markdown -t latex -o ${desktopString}/LaTeX/${fileName}.tex --template=${pandocTemplate}`;
app.doShellScript(shellCommand);
// Compile our new tex file to PDF using xelatex
shellCommand = `/Library/TeX/texbin/xelatex --output-directory=${desktopString}/LaTeX/ ${desktopString}/LaTeX/${fileName}.tex`;
app.doShellScript(shellCommand);
return true;
}
// From apple's documentation for Javascript for Automation
function writeTextToFile(text, file, overwriteExistingContent) {
try {
// Convert the file to a string
var fileString = file.toString()
// Open the file for writing
var openedFile = app.openForAccess(Path(fileString), { writePermission: true })
// Clear the file if content should be overwritten
if (overwriteExistingContent) {
app.setEof(openedFile, { to: 0 })
}
// Write the new content to the file
app.write(text, { to: openedFile, startingAt: app.getEof(openedFile) })
// Close the file
app.closeAccess(openedFile)
// Return a boolean indicating that writing was successful
return true
}
catch(error) {
try {
// Close the file
app.closeAccess(file)
}
catch(error) {
// Report the error is closing failed
console.log(`Couldn't close file: ${error}`)
}
// Return a boolean indicating that writing was successful
return false
}
}