x-math-svg.lua /size: 5366 b    last modification: 2021-10-28 13:51
1if not modules then modules = { } end modules ['x-math-svg'] = {
2    version   = 1.001,
3    comment   = "companion to x-math-svg.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9local tostring, type, next = tostring, type, next
10local lpegmatch, P, Cs = lpeg.match, lpeg.P, lpeg.Cs
11
12local xmlfirst      = xml.first
13local xmlconvert    = xml.convert
14local xmlload       = xml.load
15local xmlsave       = xml.save
16local xmlcollected  = xml.collected
17local xmldelete     = xml.delete
18
19local loadtable     = table.load
20local savetable     = table.save
21
22local replacesuffix = file.replacesuffix
23local addsuffix     = file.addsuffix
24local removefile    = os.remove
25local isfile        = lfs.isfile
26
27local formatters    = string.formatters
28
29moduledata          = moduledata or table.setmetatableindex("table")
30local svgmath       = moduledata.svgmath -- autodefined
31
32local namedata      = { }
33local pagedata      = { }
34
35local statusname    = "x-math-svg-status.lua"
36local pdfname       = "x-math-svg.pdf"
37
38local pdftosvg      = os.which("mutool")
39
40local f_make_tex    = formatters[ [[context --global kpse:x-math-svg.mkvi --inputfile="%s" --svgstyle="%s" --batch --noconsole --once --purgeall]] ]
41local f_make_svg    = formatters[ [[mutool draw -o "math-%%d.svg" "%s" 1-9999]] ]
42
43----- f_inline      = formatters[ [[<div class='math-inline' style='vertical-align:%p'></div>]] ]
44local f_inline      = formatters[ [[<div class='math-inline'></div>]] ]
45local f_display     = formatters[ [[<div class='math-display'></div>]] ]
46local f_style       = formatters[ [[vertical-align:%p]] ]
47
48local f_math_tmp    = formatters[ [[math-%i]] ]
49
50function svgmath.process(filename)
51    if not filename then
52        -- no filename given
53        return
54    elseif not isfile(filename) then
55        -- invalid filename
56        return
57    end
58    local index = 0
59    local page  = 0
60    local blobs = { }
61    local root  = xmlload(filename)
62    for mth in xmlcollected(root,"math") do
63        index = index + 1
64        local blob = tostring(mth)
65        if blobs[blob] then
66            context.ReuseSVGMath(index,blobs[blob])
67        else
68            page = page + 1
69            buffers.assign(f_math_tmp(page),blob)
70            context.MakeSVGMath(index,page,mth.at.display)
71            blobs[blob] = page
72        end
73    end
74    context(function()
75        -- for tracing purposes:
76        for mathdata, pagenumber in next, blobs do
77            local p  = pagedata[pagenumber]
78            p.mathml = mathdata
79            p.number = pagenumber
80        end
81        --
82        savetable(statusname, {
83            pagedata = pagedata,
84            namedata = namedata,
85        })
86    end)
87end
88
89function svgmath.register(index,page,specification)
90    if specification then
91        pagedata[page] = specification
92    end
93    namedata[index] = page
94end
95
96function svgmath.convert(filename,svgstyle)
97    if not filename then
98        -- no filename given
99        return false, "no filename"
100    elseif not isfile(filename) then
101        -- invalid filename
102        return false, "invalid filename"
103    elseif not pdftosvg then
104        return false, "mutool is not installed"
105    end
106
107    os.execute(f_make_tex(filename,svgstyle))
108
109    local data = loadtable(statusname)
110    if not data then
111        -- invalid tex run
112        return false, "invalid tex run"
113    elseif not next(data) then
114        return false, "no converson needed"
115    end
116
117    local pagedata = data.pagedata
118    local namedata = data.namedata
119
120    os.execute(f_make_svg(pdfname))
121
122    local root   = xmlload(filename)
123    local index  = 0
124    local done   = { }
125    local unique = 0
126
127    local between = (1-P("<"))^1/""
128    local strip = Cs((
129        (P("<text") * ((1-P("</text>"))^1) * P("</text>")) * between^0 / "" +
130        P(">") * between +
131        P(1)
132    )^1)
133
134    for mth in xmlcollected(root,"m:math") do
135        index = index + 1
136        local page = namedata[index]
137        if done[page] then
138            mth.__p__.dt[mth.ni] = done[page]
139        else
140            local info    = pagedata[page]
141            local depth   = info.depth
142            local mode    = info.mode
143            local svgname = addsuffix(f_math_tmp(page),"svg")
144            local action  = mode == "inline" and f_inline or f_display
145         -- local x_div   = xmlfirst(xmlconvert(action(-depth)),"/div")
146            local x_div   = xmlfirst(xmlconvert(action()),"/div")
147            local svgdata = io.loaddata(svgname)
148            if not svgdata or svgdata == "" then
149                print("error in:",svgname,tostring(mth))
150            else
151             -- svgdata = string.gsub(svgdata,">%s<","")
152                svgdata = lpegmatch(strip,svgdata)
153                local x_svg = xmlfirst(xmlconvert(svgdata),"/svg")
154             -- xmldelete(x_svg,"text")
155if mode == "inline" then
156    x_svg.at.style = f_style(-depth)
157end
158
159                x_div.dt = { x_svg }
160                mth.__p__.dt[mth.ni] = x_div -- use helper
161            end
162            done[page] = x_div
163            unique = unique + 1
164        end
165    end
166
167--     for k, v in next, data do
168--         removefile(addsuffix(k,"svg"))
169--     end
170--     removefile(statusname)
171--     removefile(pdfname)
172
173    xmlsave(root,filename)
174
175    return true, index, unique
176end
177