1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
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
if line:match("%}") 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("<body(.-)>", 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 "<body" ..body ..">"
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 <style> tags
contents = contents:gsub('<style [^>]+>(.-)</style>', function(style)
-- replace only the style for mathjax
if style:match "mjx%-container" then
css = parse_css(style, file_class)
return '<link rel="stylesheet" type="text/css" href="'..filename ..'" />'
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
|