local mkutils = require "mkutils" local log = logging.new("mjcli") -- other possible value is page2svg local mathnodepath = "mjcli" -- options for MathJax command local options = "" -- math fonts position -- don't alter fonts if not set local fontdir = nil -- if we copy fonts local fontdest = nil local fontformat = "woff" local cssfilename = "mathjax-chtml.css" local function compile(filename, options) -- local tmpfile = os.tmpname() log:info("Compile using MathJax") local command = mathnodepath .. " ".. options .. " " .. filename log:info(command) local commandhandle, msg = io.popen(command,"r") if not commandhandle then return nil, msg end local content = commandhandle:read("*all") commandhandle:close() return content end local saved_styles = {} local used_styles = {} local function make_css(saved_styles) -- this buffer contains lines of the new CSS file local buffer = {} -- process table with saved CSS rules and make CSS file again for _, rule in ipairs(saved_styles) do buffer[#buffer+1] = rule.selector .. " {" -- save CSS properties for _, line in ipairs(rule.content) do buffer[#buffer+1] = line end buffer[#buffer+1] = "}" buffer[#buffer+1] = "" -- add blank line end return table.concat(buffer, "\n") end -- MathJax generated CSS contains reusable declarations but also -- declarations of fixes for elements in the current file -- the idea is to reuse the common declarations and to save each -- fix. local function parse_css(css, file_class) local status = "init" local current = {} local current_selector for line in css:gmatch("([^\n]+)") do if status == "init" then local selector, rest = line:match("%s*(.-)%s*{(.-)") if selector then current_selector = selector -- if the current selector contains class, we must prepend the current file class -- as the joined CSS file could contain multiple rules with the same class otherwise if current_selector:match("%.") then current_selector = "." .. file_class .. " " .. current_selector end status = "record" end elseif status == "record" then -- find end of the CSS rule if line:match("}%s*$") then status = "init" if not used_styles[current_selector] then table.insert(saved_styles, {selector = current_selector, content = current}) end current = {} used_styles[current_selector] = true else table.insert(current, line) end end end -- save combined CSS for all files return make_css(saved_styles) end local function make_file_class(name) -- clean the filename to make it safe as a class name return name:gsub("[%s%p%s]", "_") end -- set class attribute in the body element of the current file -- this is necessary for the updated CSS file local function set_body_class(content, file_class) content = content:gsub("", function(body) if body:match("class") then -- add new class if there already is one body = body:gsub("(class.-[\"'])", "%1" .. file_class .. " ") else body = body .. ' class="' .. file_class .. '"' end return "" end) return content end -- save the css code from the html page generated by MathJax local function extract_css(contents, currentfilename) local css local filename = cssfilename local file_class = make_file_class(currentfilename) -- detect all ', function(style) -- replace only the style for mathjax if style:match "mjx%-container" then css = parse_css(style, file_class) return '' end end) contents = set_body_class(contents, file_class) return filename, contents, css end -- Update the paths to fonts to use the local versions local function use_fonts(css) local family_pattern = "font%-family:%s*(.-);.-%/([^%/]+)%.".. fontformat local family_build = "@font-face {font-family: %s; src: url('%s/%s.%s') format('%s')}" local fontdir = fontdir:gsub("/$","") css = css:gsub("(@font%-face%s*{.-})", function(face) if not face:match("url%(") then return face end -- print(face) local family, filename = face:match(family_pattern) log:info("use font: ",family, filename) local newfile = string.format("%s/%s.%s", fontdir, filename, fontformat) Make:add_file(newfile) return family_build:format(family, fontdir, filename, fontformat, fontformat) -- return face end) return css end local function save_css(filename, css) local f = io.open(filename, "w") f:write(css) f:close() end return function(text, arguments) -- if arguments.prg then mathnodepath = arguments.prg end local extoptions = mkutils.get_filter_settings "mjcli" or {} local arguments = arguments or {} mathnodepath = arguments.prg or extoptions.prg or mathnodepath local options = arguments.options or extoptions.options or options fontdir = arguments.fontdir or extoptions.fontdir or fontdir -- the following ne is unused ATM fontdest = arguments.fontdest or extoptions.fontdest or fontdest fontformat = arguments.fontformat or extoptions.fontformat or fontformat cssfilename = arguments.cssfilename or extoptions.cssfilename or arguments.input .. "-mathjax.css" local is_latex = arguments.latex or extoptions.latex or false local filename = arguments.filename -- modify options to use LaTeX syntax by MathJax if is_latex then options = options .. " -l" end -- compile current html file with mathjax local newtext, msg = compile(filename, options) if not newtext then log:error(msg) return text end -- save CSS to a standalone file local cssfile, newtext, css = extract_css(newtext, filename) -- use local font files if fontdir is present if fontdir then css = use_fonts(css) end if css then save_css(cssfile, css) Make:add_file(cssfile) -- print(css) log:info("CSS file: " .. cssfile) end return newtext end