Add org-static-mathjax to the contrib directory

Thanks to Jan Bker for this contribution.
This commit is contained in:
Carsten Dominik 2010-08-20 18:12:59 +02:00
parent 190e88cfc9
commit 4a028cc7c0
9 changed files with 564 additions and 0 deletions

View File

@ -0,0 +1,171 @@
;;; org-static-mathjax.el --- Muse-like tags in Org-mode
;;
;; Author: Jan Böker <jan dot boecker at jboecker dot de>
;; This elisp code integrates Static MathJax into the
;; HTML export process of Org-mode.
;;
;; The supporting files for this package are in contrib/scripts/staticmathjax
;; Please read the README.org file in that directory for more information.
;; To use it, evaluate it on startup, add the following to your .emacs:
;; (require 'org-static-mathjax)
;;
;; You will then have to customize the following two variables:
;; - org-static-mathjax-app-ini-path
;; - org-static-mathjax-local-mathjax-path
;;
;; If xulrunner is not in your $PATH, you will also need to customize
;; org-static-mathjax-xulrunner-path.
;;
;; If everything is setup correctly, you can trigger Static MathJax on
;; export to HTML by adding the following line to your Org file:
;; #+StaticMathJax: embed-fonts:nil output-file-name:"embedded-math.html"
;;
;; You can omit either argument.
;; embed-fonts defaults to nil. If you do not specify output-file-name,
;; the exported file is overwritten with the static version.
;;
;; If embed-fonts is non-nil, the fonts are embedded directly into the
;; output file using data: URIs.
;;
;; output-file-name specifies the file name of the static version. You
;; can use any arbitrary lisp form here, for example:
;; output-file-name:(concat (file-name-sans-extension buffer-file-name) "-static.html")
;;
;; The StaticMathJax XULRunner application expects a UTF-8 encoded
;; input file. If the static version displays random characters instead
;; of your math, add the following line at the top of your Org file:
;; -*- coding: utf-8; -*-
;;
;; License: GPL v2 or later
(defcustom org-static-mathjax-app-ini-path
(or (expand-file-name
"../scripts/staticmatchjax/application.ini"
(file-name-directory (or load-file-name buffer-file-name)))
"")
"Path to \"application.ini\" of the Static MathJax XULRunner application.
If you have extracted StaticMathJax to e.g. ~/.local/staticmathjax, set
this to ~/.local/staticmathjax/application.ini"
:type 'string)
(defcustom org-static-mathjax-xulrunner-path
"xulrunner"
"Path to your xulrunner binary"
:type 'string)
(defcustom org-static-mathjax-local-mathjax-path
""
"Extract the MathJax zip file somewhere on your local
hard drive and specify the path here.
The directory has to be writeable, as org-static-mathjax
creates a temporary file there during export."
:type 'string)
(defvar org-static-mathjax-debug
nil
"If non-nil, org-static-mathjax will print some debug messages")
(defun org-static-mathjax-hook-installer ()
"Installs org-static-mathjax-process in after-save-hook.
Sets the following buffer-local variables for org-static-mathjax-process to pick up:
org-static-mathjax-mathjax-path: The path to MathJax.js as used by Org HTML export
org-static-mathjax-options: The string given with #+STATICMATHJAX: in the file"
(let ((static-mathjax-option-string (plist-get opt-plist :static-mathjax)))
(if static-mathjax-option-string
(progn (set (make-local-variable 'org-static-mathjax-options) static-mathjax-option-string)
(set (make-local-variable 'org-static-mathjax-mathjax-path)
(nth 1 (assq 'path org-export-html-mathjax-options)))
(let ((mathjax-options (plist-get opt-plist :mathjax)))
(if mathjax-options
(if (string-match "\\<path:" mathjax-options)
(set 'org-static-mathjax-mathjax-path
(car (read-from-string
(substring mathjax-options (match-end 0))))))))
(add-hook 'after-save-hook
'org-static-mathjax-process
nil t)))))
(defun org-static-mathjax-process ()
(save-excursion
; some sanity checking
(if (or (string= org-static-mathjax-app-ini-path "")
(not (file-exists-p org-static-mathjax-app-ini-path)))
(error "Static MathJax: You must customize org-static-mathjax-app-ini-path!"))
(if (or (string= org-static-mathjax-local-mathjax-path "")
(not (file-exists-p org-static-mathjax-local-mathjax-path)))
(error "Static MathJax: You must customize org-static-mathjax-local-mathjax-path!"))
; define variables
(let* ((options org-static-mathjax-options)
(output-file-name buffer-file-name)
(input-file-name (let ((temporary-file-directory (file-name-directory org-static-mathjax-local-mathjax-path)))
(make-temp-file "org-static-mathjax-" nil ".html")))
(html-code (buffer-string))
(mathjax-oldpath (concat "src=\"" org-static-mathjax-mathjax-path))
(mathjax-newpath (concat "src=\"" org-static-mathjax-local-mathjax-path))
embed-fonts)
; read file-local options
(mapc
(lambda (symbol)
(if (string-match (concat "\\<" (symbol-name symbol) ":") options)
(set symbol (eval (car (read-from-string
(substring options (match-end 0))))))))
'(embed-fonts output-file-name))
; debug
(when org-static-mathjax-debug
(message "output file name, embed-fonts")
(print output-file-name)
(print embed-fonts))
; open (temporary) input file, copy contents there, replace MathJax path with local installation
(with-temp-buffer
(insert html-code)
(goto-char 1)
(replace-regexp mathjax-oldpath mathjax-newpath)
(write-file input-file-name))
; prepare argument list for call-process
(let ((call-process-args (list org-static-mathjax-xulrunner-path
nil nil nil
org-static-mathjax-app-ini-path
input-file-name
output-file-name)))
; if fonts are embedded, just append the --embed-fonts flag
(if embed-fonts
(add-to-list 'call-process-args "--embed-fonts" t))
; if fonts are not embedded, the XULRunner app must replace all references
; to the font files with the real location (Firefox inserts file:// URLs there,
; because we are using a local MathJax installation here)
(if (not embed-fonts)
(progn
(add-to-list 'call-process-args "--final-mathjax-url" t)
(add-to-list 'call-process-args
(file-name-directory org-static-mathjax-mathjax-path)
t)))
; debug
(when org-static-mathjax-debug
(print call-process-args))
; call it
(apply 'call-process call-process-args)
; delete our temporary input file
(kill-buffer)
(delete-file input-file-name)
(let ((backup-file (concat input-file-name "~")))
(if (file-exists-p backup-file)
(delete-file backup-file)))))))
(add-to-list 'org-export-inbuffer-options-extra
'("STATICMATHJAX" :static-mathjax))
(add-hook 'org-export-html-final-hook 'org-static-mathjax-hook-installer)
(provide 'org-static-mathjax)

View File

@ -0,0 +1 @@
*~

View File

@ -0,0 +1,79 @@
Static MathJax v0.1 README
#+AUTHOR: Jan Böcker <jan.boecker@jboecker.de>
Static MathJax is a XULRunner application which loads a HTML input
file that uses MathJax into a browser, waits until MathJax is done
processing, and then writes the formatted result to an output HTML
file.
I have only tested exports from Emacs Org-mode as input files. (As of
2010-08-14, MathJax is used by default with HTML exports in the
current Org development version.)
Optionally, references to the math fonts used will be converted to
"data:" URIs, thus embedding the font data into the HTML file itself.
(see [[http://en.wikipedia.org/wiki/Data_URI_scheme]])
The code is licensed under the GNU General Public License version
2, or, at your option, any later version.
* Usage
To run Static MathJax, an existing XULRunner installation is
required. From the directory to which you unpacked Static MathJax,
run:
xulrunner application.ini <--embed-fonts | --final-mathjax-url <URL>>
<input file> <output file>
If you prefer to call "staticmathjax" instead of "xulrunner
application.ini", link xulrunner-stub into the directory:
ln /usr/lib/xulrunner-1.9.2.8/xulrunner-stub ./staticmathjax
- input file ::
name of the input file (the result of a HTML export
from Org-mode). It is assumed that this file uses the
UTF-8 character encoding.
- output file ::
name of the output file.
- --embed-fonts ::
if specified, the math fonts will be embedded into
the output file using data: URIs
- --final-mathjax-url <URL> ::
if --embed-fonts is not specified, this
must be the URL to a MathJax installation folder (e.g. "MathJax"
if MathJax is installed in a subdirectory, or
"http://orgmode.org/mathjax" to use the version hosted on the Org
website.
All references to math fonts in the output file will point to
this directory.
* Caveats
The input file must not use a MathJax installation on the
web. Otherwise, due to a security feature of Firefox, MathJax will
fallback to image fonts. If you have unpacked MathJax to a
subdirectory "MathJax", specify the following in your Org file:
#+MathJax: path:"MathJax"
The math is rendered in Firefox, so MathJax applies its
Firefox-specific settings. When viewing the output files in other
browsers, it will look slightly different than the result that
running MathJax in that browser would produce.
Internet Explorer does not use the correct font, because it only
supports the EOT font format. For all other browsers (including
Firefox), MathJax uses the OTF font format.
Embedding fonts into the HTML file wastes some space due to the
base64 encoding used in data: URIs.
I have found no way to access stdout or set an exit code in an
XULRunner app, so any code which calls Static MathJax has no idea if
processing was successful and when an error occurs, graphical
message boxes are displayed.

View File

@ -0,0 +1,91 @@
Static MathJax v0.1 README
==========================
Author: Jan Böcker <jan.boecker@jboecker.de>
Date: 2010-08-15 13:53:39 CEST
Static MathJax is a XULRunner application which loads a HTML input
file that uses MathJax into a browser, waits until MathJax is done
processing, and then writes the formatted result to an output HTML
file.
I have only tested exports from Emacs Org-mode as input files. (As of
2010-08-14, MathJax is used by default with HTML exports in the
current Org development version.)
Optionally, references to the math fonts used will be converted to
"data:" URIs, thus embedding the font data into the HTML file itself.
(see [http://en.wikipedia.org/wiki/Data_URI_scheme])
The code is licensed under the GNU General Public License version
2, or, at your option, any later version.
Table of Contents
=================
1 Usage
2 Caveats
1 Usage
~~~~~~~~
To run Static MathJax, an existing XULRunner installation is
required. From the directory to which you unpacked Static MathJax,
run:
xulrunner application.ini <--embed-fonts | --final-mathjax-url <URL>>
<input file> <output file>
If you prefer to call "staticmathjax" instead of "xulrunner
application.ini", link xulrunner-stub into the directory:
ln /usr/lib/xulrunner-1.9.2.8/xulrunner-stub ./staticmathjax
input file:
name of the input file (the result of a HTML export
from Org-mode). It is assumed that this file uses the
UTF-8 character encoding.
output file:
name of the output file.
--embed-fonts:
if specified, the math fonts will be embedded into
the output file using data: URIs
--final-mathjax-url <URL>:
if --embed-fonts is not specified, this
must be the URL to a MathJax installation folder (e.g. "MathJax"
if MathJax is installed in a subdirectory, or
"[http://orgmode.org/mathjax]" to use the version hosted on the Org
website.
All references to math fonts in the output file will point to
this directory.
2 Caveats
~~~~~~~~~~
The input file must not use a MathJax installation on the
web. Otherwise, due to a security feature of Firefox, MathJax will
fallback to image fonts. If you have unpacked MathJax to a
subdirectory "MathJax", specify the following in your Org file:
#+MathJax: path:"MathJax"
The math is rendered in Firefox, so MathJax applies its
Firefox-specific settings. When viewing the output files in other
browsers, it will look slightly different than the result that
running MathJax in that browser would produce.
Internet Explorer does not use the correct font, because it only
supports the EOT font format. For all other browsers (including
Firefox), MathJax uses the OTF font format.
Embedding fonts into the HTML file wastes some space due to the
base64 encoding used in data: URIs.
I have found no way to access stdout or set an exit code in an
XULRunner app, so any code which calls Static MathJax has no idea if
processing was successful and when an error occurs, graphical
message boxes are displayed.

View File

@ -0,0 +1,11 @@
[App]
Vendor=Jan Boecker
Name=StaticMathJax
Version=0.2
BuildID=2
Copyright=Copyright (c) 2010 Jan Boecker
ID=xulapp@jboecker.de
[Gecko]
MinVersion=1.8

View File

@ -0,0 +1 @@
content staticmathjax file:content/

View File

@ -0,0 +1,198 @@
var docFrame;
var logtextbox;
var destFile;
var embedFonts = false;
var finalMathJaxURL = null;
function log(text)
{
logtextbox.setAttribute("value", logtextbox.getAttribute("value") + "\n" + text);
}
function init()
{
try {
docFrame = document.getElementById("docFrame");
logtextbox = document.getElementById("logtextbox");
// parse command line arguments
var cmdLine = window.arguments[0];
cmdLine = cmdLine.QueryInterface(Components.interfaces.nsICommandLine);
embedFonts = cmdLine.handleFlag("embed-fonts", false);
finalMathJaxURL = cmdLine.handleFlagWithParam("final-mathjax-url", false);
if (!embedFonts && !finalMathJaxURL) {
alert("You must eiher specify --embed-fonts or --final-mathjax-url");
window.close();
return;
}
sourceFilePath = cmdLine.getArgument(0);
destFilePath = cmdLine.getArgument(1);
if ( !sourceFilePath || !destFilePath ) {
alert("Not enough parameters, expecting two arguments:\nInput file, output file");
window.close();
return;
}
sourceFile = cmdLine.resolveFile(sourceFilePath);
if (! (sourceFile.exists() && sourceFile.isFile()) ) {
alert("Invalid source file path.");
window.close();
return;
}
sourceURI = cmdLine.resolveURI(sourceFilePath);
// create a nsIFile object for the output file
try{
destFile = cmdLine.resolveURI(destFilePath).QueryInterface(Components.interfaces.nsIFileURL).file;
}catch(e){
alert("Invalid destination file.\n\nException:\n" + e);
window.close();
return;
}
// add iframeLoaded() as an onload event handler, then navigate to the source file
docFrame.addEventListener("DOMContentLoaded", iframeLoaded, true);
docFrame.setAttribute("src", sourceURI.spec);
} catch (e) {
alert("Error in init():\n\n" + e);
window.close();
return;
}
}
function iframeLoaded()
{
/*
// print every MathJax signal to the log
docFrame.contentWindow.MathJax.Hub.Startup.signal.Interest(
function (message) {log("Startup: "+message)}
);
docFrame.contentWindow.MathJax.Hub.signal.Interest(
function (message) {log("Hub: "+message)}
);
*/
// tell MathJax to call serialize() when finished
docFrame.contentWindow.MathJax.Hub.Register.StartupHook("End", function() {serialize();});
}
function fileURLtoDataURI(url)
{
var ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var url_object = ios.newURI(url, "", null);
var file = url_object.QueryInterface(Components.interfaces.nsIFileURL).file;
var data = "";
var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].
createInstance(Components.interfaces.nsIFileInputStream);
fstream.init(file, -1, -1, false);
var bstream = Components.classes["@mozilla.org/binaryinputstream;1"].
createInstance(Components.interfaces.nsIBinaryInputStream);
bstream.setInputStream(fstream);
var bytes = bstream.readBytes(bstream.available());
b64bytes = btoa(bytes);
return "data:;base64," + b64bytes;
}
function serialize()
{
var MathJaxURL = docFrame.contentWindow.MathJax.Hub.config.root;
var searchURIList = new Array();
var replacementURIList = new Array();
log("serialize: preprocessing");
// remove the MathJax status message window
msgdiv = docFrame.contentDocument.getElementById("MathJax_Message");
msgdiv.parentNode.removeChild(msgdiv);
/* Loop through all CSS rules to find all @font-face rules.
At this point, they refer to local absolute paths using file:// URLs.
Replace them either with appropriate URLs relative to finalMathJaxURL
or with data URIs. */
for (var i = 0; i<docFrame.contentDocument.styleSheets.length; i++) {
var stylesheet = docFrame.contentDocument.styleSheets[i];
for (var j=0; j< stylesheet.cssRules.length; j++) {
var rule = stylesheet.cssRules[j];
if (rule.cssText.match("font-face")) {
url = rule.style.getPropertyValue("src");
url = url.match(/url\(\"(.+)\"\)/)[1];
// Since the properties seem read-only here, we populate
// searchURIList and replacementURIList to do text substitution
// after serialization
searchURIList.push(url);
if (embedFonts) {
replacementURIList.push(fileURLtoDataURI(url));
} else {
replacementURIList.push(url.replace(MathJaxURL, finalMathJaxURL));
}
}
}
}
// find and remove the MathJax <script> tag
try{
var scriptTags = docFrame.contentDocument.getElementsByTagName("script");
for (var i=0; i<scriptTags.length; i++) {
if (scriptTags[i].getAttribute("src") && scriptTags[i].getAttribute("src").match(/MathJax.js/i))
scriptTags[i].parentNode.removeChild(scriptTags[i]);
}
}catch(e){alert(e);}
log("serialize: serializing");
var serializer = new XMLSerializer();
var xhtml = serializer.serializeToString(docFrame.contentDocument);
log("serialize: postprocessing");
// make the MathJax URL relative again
// xhtml = xhtml.replace(findMathJaxURL, "MathJax");
try{
r1 = RegExp("&lt;!--/\\*--&gt;&lt;!\\[CDATA\\[/\\*&gt;&lt;!--\\*/", "g");
xhtml = xhtml.replace(r1, "");
r2 = RegExp("/\\*\\]\\]&gt;\\*/--&gt;", "g");
xhtml = xhtml.replace(r2, "");
r3 = RegExp("/\\*\\]\\]&gt;\\*///--&gt;", "g");
xhtml = xhtml.replace(r3, "");
}catch(e){alert(e);}
for (var i=0; i<searchURIList.length; i++)
xhtml = xhtml.replace(searchURIList[i], replacementURIList[i]);
save(xhtml);
window.close();
}
function save(xhtml)
{
try {
var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
foStream.init(destFile, 0x02 | 0x08 | 0x20, 0666, 0);
// write, create, truncate
// write in UTF-8 encoding
var converter = Components.classes["@mozilla.org/intl/converter-output-stream;1"].
createInstance(Components.interfaces.nsIConverterOutputStream);
converter.init(foStream, "UTF-8", 0, 0);
converter.writeString(xhtml);
converter.close(); // this closes foStream
} catch (e) {
alert("Error in save():\n\n" + e);
}
}

View File

@ -0,0 +1,11 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window onload="init();" id="main" title="Static MathJax" width="300" height="300"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script language="JavaScript" src="chrome://staticmathjax/content/main.js"/>
<browser flex="1" id="docFrame" src="" style="background-color:white;"/>
<textbox flex="1" id="logtextbox" multiline="true" style="display:none;"/>
</window>

View File

@ -0,0 +1 @@
pref("toolkit.defaultChromeURI", "chrome://staticmathjax/content/main.xul");