mtx-server-ctx-fonttest.lua /size: 31 Kb    last modification: 2025-02-21 11:03
1if not modules then modules = { } end modules ['mtx-server-ctx-fonttest'] = {
2    version   = 1.001,
3    comment   = "Font Feature Tester",
4    author    = "Hans Hagen",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9-- probably too much but who cares
10
11dofile(resolvers.findfile("trac-lmx.lua","tex"))
12dofile(resolvers.findfile("char-def.lua","tex"))
13
14dofile(resolvers.findfile("font-ini.lua","tex"))
15dofile(resolvers.findfile("font-log.lua","tex"))
16dofile(resolvers.findfile("font-con.lua","tex"))
17dofile(resolvers.findfile("font-cft.lua","tex"))
18dofile(resolvers.findfile("font-enc.lua","tex"))
19dofile(resolvers.findfile("font-agl.lua","tex"))
20dofile(resolvers.findfile("font-cid.lua","tex"))
21dofile(resolvers.findfile("font-map.lua","tex"))
22dofile(resolvers.findfile("font-otr.lua","tex"))
23dofile(resolvers.findfile("font-web.lua","tex"))
24dofile(resolvers.findfile("font-cff.lua","tex"))
25dofile(resolvers.findfile("font-ttf.lua","tex"))
26dofile(resolvers.findfile("font-dsp.lua","tex"))
27dofile(resolvers.findfile("font-hsh.lua","tex"))
28dofile(resolvers.findfile("font-oti.lua","tex"))
29dofile(resolvers.findfile("font-ott.lua","tex"))
30dofile(resolvers.findfile("font-otl.lua","tex"))
31-- dofile(resolvers.findfile("font-oto.lua","tex"))
32-- dofile(resolvers.findfile("font-otj.lua","tex"))
33dofile(resolvers.findfile("font-oup.lua","tex"))
34-- dofile(resolvers.findfile("font-ots.lua","tex"))
35-- dofile(resolvers.findfile("font-otd.lua","tex"))
36-- dofile(resolvers.findfile("font-otc.lua","tex"))
37-- dofile(resolvers.findfile("font-oth.lua","tex"))
38-- dofile(resolvers.findfile("font-osd.lua","tex"))
39-- dofile(resolvers.findfile("font-ocl.lua","tex"))
40dofile(resolvers.findfile("font-onr.lua","tex"))
41
42dofile(resolvers.findfile("font-syn.lua","tex"))
43dofile(resolvers.findfile("font-mis.lua","tex"))
44
45local format, gsub, concat, match, find = string.format, string.gsub, table.concat, string.match, string.find
46
47local formatters      = string.formatters
48
49local report          = logs.reporter("ctx-fonttest")
50
51local sample_line     = "This is a sample line!"
52local tempname        = "mtx-server-ctx-fonttest-temp"
53local temppath        = caches.setfirstwritablefile("temp","mtx-server-ctx-fonttest")
54local basename        = "mtx-server-ctx-fonttest-data.lua"
55local basepath        = temppath
56
57local remove_suffixes = { "tex", "pdf", "log" }
58local what_options    = { "trace", "basemode" }
59
60for i=1,#remove_suffixes do
61    os.remove(file.join(temppath,file.addsuffix(tempname,remove_suffixes[i])))
62end
63
64local foolcache = 0
65
66local function makename(name,new)
67    if new then
68        foolcache = foolcache > 25 and 1 or foolcache + 1
69    end
70    return formatters["%s-%02i"](name,foolcache)
71end
72
73-- nowadays i would use the more advanced template mechanism with named variables
74
75local process_templates = { }
76
77--   %\definedfont[name:%s*sample]
78
79process_templates.default = formatters [ [[
80\starttext
81    \setupdirections[bidi=one]
82    \definefontfeature[sample][analyze=yes,%s]
83    \definedfont[name:%s*none]
84    \startTEXpage[offset=3pt]
85        \detokenize{%s}
86        \blank
87        \definedfont[name:%s*sample]
88        \detokenize{%s}
89    \stopTEXpage
90\stoptext
91]] ]
92
93process_templates.cache = formatters [ [[
94\starttext
95    \definedfont[name:%s]
96    \startTEXpage[offset=3pt]
97        cached: \detokenize{%s}
98    \stopTEXpage
99\stoptext
100]] ]
101
102process_templates.trace = formatters [ [[
103\usemodule[fnt-20]
104
105\definefontfeature[sample][%s]
106
107\setupcolors[state=start]
108
109\setupdirections[bidi=global]
110
111\setvariables
112  [otftracker]
113  [title=Test Run,
114   font=name:%s,
115   direction=0,
116   features=sample,
117   sample={‍\detokenize{%s}}]
118]] ]
119
120local javascripts = formatters [ [[
121function selected_radio(name) {
122    var form = document.forms["main-form"] ;
123    var script = form.elements[name] ;
124    if (script) {
125        var n = script.length ;
126        if (n) {
127            for (var i=0; i<n; i++) {
128                if (script[i].checked) {
129                    return script[i].value ;
130                }
131            }
132        }
133    }
134    return "" ;
135}
136
137function reset_valid() {
138    var fields = document.getElementsByTagName("span") ;
139    for (var i=0; i<fields.length; i++) {
140        var e = fields[i]
141        if (e) {
142            if (e.className == "valid") {
143                e.className = "" ;
144            }
145        }
146    }
147}
148
149function set_valid() {
150    var script = selected_radio("script") ;
151    var language = selected_radio("language") ;
152    if (script && language) {
153        var s = feature_hash[script] ;
154        if (s) {
155            for (l in s) {
156                var e = document.getElementById("t-l-" + l) ;
157                if (e) {
158                    e.className = "valid" ;
159                }
160            }
161            var l = s[language] ;
162            if (l) {
163                for (i in l) {
164                    var e = document.getElementById("t-f-" + i) ;
165                    if (e) {
166                        e.className = "valid" ;
167                    }
168                }
169            }
170            var e = document.getElementById("t-s-" + script) ;
171            if (e) {
172                e.className = "valid" ;
173            }
174        }
175    }
176}
177
178function check_form() {
179    reset_valid() ;
180    set_valid() ;
181}
182
183function check_script() {
184    reset_valid() ;
185    set_valid() ;
186}
187
188function check_language() {
189    reset_valid() ;
190    set_valid() ;
191}
192
193function check_feature() {
194    // not needed
195}
196]] ]
197
198local jitmode = false -- assumes local use (one user as shared)
199
200local run_context_normal = formatters["mtxrun --path=%q --script context --once --batchmode %q"]
201local run_context_jitted = formatters["mtxrun --path=%q --script context --once --batchmode --jit %q"]
202
203local function runcontext(temppath,filename)
204    local start = os.clock()
205    local command = (jitmode and run_context_jitted or run_context_normal)(temppath,filename)
206    report("temppath: %s",temppath)
207    report("filename: %s",filename)
208    report("command: %s",command)
209    os.execute(command)
210    return os.clock() - start
211end
212
213local function identifyfonts()
214    local command = "mtxrun --script font --reload"
215    report("command: %s",command)
216    os.execute(command)
217end
218
219local cache = { }
220
221local function showfeatures(f)
222    if f then
223        report("processing font '%s'",f)
224        local features = cache[f]
225        if features == nil then
226            features = fonts.helpers.getfeatures(resolvers.findfile(f))
227            if not features then
228                report("building cache for '%s'",f)
229                local usedname = file.addsuffix(tempname,"tex")
230                io.savedata(file.join(temppath,usedname),process_templates.cache(f,f))
231                runcontext(temppath,usedname)
232                features = fonts.helpers.getfeatures(f)
233            end
234            cache[f] = features or false
235            report("caching info of '%s'",f)
236        else
237            report("using cached info of '%s'",f)
238        end
239        if features then
240            local scr, lan, fea, rev = { }, { }, { }, { }
241            local function show(what)
242                local data = features[what]
243                if data and next(data) then
244                    for f,ff in next, data do
245                        if find(f,"<") then
246                            -- ignore aat for the moment
247                        else
248                            fea[f] = true
249                            for s, ss in next, ff do
250                                if find(s,"%*") then
251                                    -- ignore *
252                                else
253                                    scr[s] = true
254                                    local rs = rev[s] if not rs then rs = {} rev[s] = rs end
255                                    for k, l in next, ss do
256                                        if find(k,"%*") then
257                                            -- ignore *
258                                        else
259                                            lan[k] = true
260                                            local rsk = rs[k] if not rsk then rsk = { } rs[k] = rsk end
261                                            rsk[f] = true
262                                        end
263                                    end
264                                end
265                            end
266                        end
267                    end
268                end
269            end
270            for what, v in table.sortedhash(features) do
271                show(what)
272            end
273            -- we could json this
274            local stupid = { }
275            stupid[#stupid+1] = "var feature_hash = new Array ;"
276            for s, sr in next, rev do
277                stupid[#stupid+1] = formatters["feature_hash['%s'] = new Array ;"](s)
278                for l, lr in next, sr do
279                    stupid[#stupid+1] = formatters["feature_hash['%s']['%s'] = new Array ;"](s,l)
280                    for f, fr in next, lr do
281                        stupid[#stupid+1] = formatters["feature_hash['%s']['%s']['%s'] = true ;"](s,l,f)
282                    end
283                end
284            end
285            -- gpos feature script languages
286            return {
287                scripts = scr,
288                languages = lan,
289                features = fea,
290                javascript = concat(stupid,"\n")
291            }
292        end
293    end
294end
295
296local template_h = formatters [ [[
297<tr>
298    <th>safe name&nbsp;&nbsp;&nbsp;&nbsp;</th>
299    <th>family name&nbsp;&nbsp;&nbsp;&nbsp;</th>
300    <th>style-variant-weight-width&nbsp;&nbsp;&nbsp;&nbsp;</th>
301    <th>font name&nbsp;&nbsp;&nbsp;&nbsp;</th>
302    <th>weight&nbsp;&nbsp;&nbsp;&nbsp;</th>
303    <th>filename</th>
304</tr>]] ]
305
306local template_d = formatters [ [[
307<tr>
308    <td><a href='mtx-server-ctx-fonttest.lua?selection=%s'>%s</a>&nbsp;&nbsp;&nbsp;&nbsp;</td>
309    <td>%s&nbsp;&nbsp;&nbsp;&nbsp;</td>
310    <td>%s-%s-%s-%s&nbsp;&nbsp;&nbsp;&nbsp;</td>
311    <td>%s&nbsp;&nbsp;&nbsp;&nbsp;</td>
312    <td>%s&nbsp;&nbsp;&nbsp;&nbsp;</td>
313    <td>%s</td>
314</tr>]] ]
315
316local function select_font()
317    local t = fonts.names.list(".*",false,true)
318    if t then
319        local listoffonts = { }
320        listoffonts[#listoffonts+1] = "<h1>Fonts</h1><br/>"
321        listoffonts[#listoffonts+1] = "<table>"
322        listoffonts[#listoffonts+1] = template_h()
323        for k, v in table.sortedhash(t) do
324            local kind = v.format
325            if kind == "otf" or kind == "ttf" or kind == "ttc" then
326                local fontname = v.fontname
327                listoffonts[#listoffonts+1] = template_d(
328                    fontname,
329                    fontname,
330                    v.familyname or "",
331                    t.variant    or "normal",
332                    t.weight     or "normal",
333                    t.width      or "normal",
334                    t.style      or "normal",
335                    v.rawname    or fontname,
336                    v.fontweight or "",
337                    v.filename   or ""
338                )
339            end
340        end
341        listoffonts[#listoffonts+1] = "</table>"
342        return concat(listoffonts,"\n")
343    end
344    return "<b>no fonts</b>"
345end
346
347local edit_template = formatters [ [[
348    <textarea name='sampletext' rows='5' cols='100'>%s</textarea>
349    <br/> <br/>name:&nbsp;<input type='text' name='name' size='20' value=%q/>&nbsp;&nbsp; title:&nbsp;<input type='text' name='title' size='40' value=%q/>
350    <br/> <br/>scripts:&nbsp;%s
351    <br/> <br/>languages:&nbsp;%s
352    <br/> <br/>features:&nbsp;%s
353    <br/> <br/>options:&nbsp;%s
354]] ]
355
356--  <embed src="%s#toolbar=0&amp;navpanes=0&amp;scrollbar=0" width="100%%"/>
357
358local result_template = formatters [ [[
359    <br/> <br/>
360    <embed src="%s#view=Fit&amp;toolbar=0&amp;navpanes=0&amp;scrollbar=0" width="100%%"/>
361    <br/> <br/> results:&nbsp;
362    <a href='%s' target="source">tex file</a>&nbsp;&nbsp;
363    <a href='%s' target="result">pdf file</a>&nbsp;&nbsp;
364    (runtime: %s)
365    <br/> <br/>
366]] ]
367
368local main_template = formatters [ [[
369    <h1>Font: %s</h1><br/>
370    %s
371    %s
372]] ]
373
374scripts.webserver.registerpath(temppath)
375
376local function get_specification(name)
377    return fonts.names.resolvedspecification(name or "")
378end
379
380local function edit_font(currentfont,detail,tempname,runtime)
381    report("entering edit mode for '%s'",currentfont)
382    local specification = get_specification(currentfont)
383    if specification then
384        local htmldata = showfeatures(specification.filename)
385        if htmldata then
386            local features, languages, scripts, options = { }, { }, { }, { }
387            local sorted = table.sortedkeys(htmldata.scripts)
388            for k=1,#sorted do
389                local v = sorted[k]
390                local s = fonts.handlers.otf.tables.scripts[v] or v
391                if detail and v == detail.script then
392                    scripts[#scripts+1] = formatters["<input title='%s' id='s-%s' type='radio' name='script' value='%s' onclick='check_script()' checked='checked'/>&nbsp;<span id='t-s-%s'>%s</span>"](s,v,v,v,v)
393                else
394                    scripts[#scripts+1] = formatters["<input title='%s' id='s-%s' type='radio' name='script' value='%s' onclick='check_script()' />&nbsp;<span id='t-s-%s'>%s</span>"](s,v,v,v,v)
395                end
396            end
397            local sorted = table.sortedkeys(htmldata.languages)
398            for k=1,#sorted do
399                local v = sorted[k]
400                local l = fonts.handlers.otf.tables.languages[v] or v
401                if detail and v == detail.language then
402                    languages[#languages+1] = formatters["<input title='%s' id='l-%s' type='radio' name='language' value='%s' onclick='check_language()' checked='checked'/>&nbsp;<span id='t-l-%s'>%s</span>"](l,v,v,v,v)
403                else
404                    languages[#languages+1] = formatters["<input title='%s' id='l-%s' type='radio' name='language' value='%s' onclick='check_language()' />&nbsp;<span id='t-l-%s'>%s</span>"](l,v,v,v,v)
405                end
406            end
407            local sorted = table.sortedkeys(htmldata.features)
408            for k=1,#sorted do
409                local v = sorted[k]
410                local f = fonts.handlers.otf.tables.features[v] or v
411                if detail and detail["f-"..v] then
412                    features[#features+1] = formatters["<input title='%s' id='f-%s' type='checkbox' name='f-%s' onclick='check_feature()' checked='checked'/>&nbsp;<span id='t-f-%s'>%s</span>"](f,v,v,v,v)
413                else
414                    features[#features+1] = formatters["<input title='%s' id='f-%s' type='checkbox' name='f-%s' onclick='check_feature()' />&nbsp;<span id='t-f-%s'>%s</span>"](f,v,v,v,v)
415                end
416            end
417            for k=1,#what_options do
418                local v = what_options[k]
419                if detail and detail["o-"..v] then
420                    options[#options+1] = formatters["<input id='o-%s' type='checkbox' name='o-%s' checked='checked'/>&nbsp;%s"](v,v,v)
421                else
422                    options[#options+1] = formatters["<input id='o-%s' type='checkbox' name='o-%s'/>&nbsp;%s"](v,v,v)
423                end
424            end
425            local e = edit_template(
426                detail and detail.sampletext or sample_line,
427                detail and detail.name or "no name",
428                detail and detail.title or "",
429                concat(scripts,"  "),
430                concat(languages,"  "),
431                concat(features,"  "),
432                concat(options,"  ")
433            )
434            if tempname then
435                local pdffile, texfile = file.replacesuffix(tempname,"pdf"), file.replacesuffix(tempname,"tex")
436                local r = result_template(pdffile,texfile,pdffile,runtime and formatters["%0.3f"](runtime) or "-")
437                return main_template(currentfont,e,r), htmldata.javascript or ""
438            else
439                return main_template(currentfont,e,""), htmldata.javascript or ""
440            end
441        else
442            return "error, nothing set up yet"
443        end
444    else
445        return "error, no info about font"
446    end
447end
448
449local function process_font(currentfont,detail) -- maybe just fontname
450    local features = {
451        "mode=node",
452        formatters["language=%s"](detail.language or "dflt"),
453        formatters["script=%s"](detail.script or "dflt"),
454    }
455    for k,v in next, detail do
456        local f = match(k,"^f%-(.*)$")
457        if f then
458            features[#features+1] = formatters["%s=yes"](f)
459        end
460    end
461    local variant = process_templates.default
462    if detail["o-trace"] then
463        variant = process_templates.trace
464    end
465    local sample = string.strip(detail.sampletext or "")
466    if sample == "" then sample = sample_line end
467    report("sample text: %s",sample)
468    dir.mkdirs(temppath)
469    local tempname = makename(tempname,true)
470    local usedname = file.addsuffix(tempname,"tex")
471    local fullname = file.join(temppath,usedname)
472    local data = variant(concat(features,","),currentfont,sample,currentfont,sample)
473    io.savedata(fullname,data)
474    io.flush()
475    local runtime = runcontext(temppath,usedname)
476    return edit_font(currentfont,detail,tempname,runtime)
477end
478
479local tex_template = formatters [ [[
480<h1>Font: %s</h1><br/>
481<pre><tt>
482%s
483</tt></pre>
484]] ]
485
486local function show_source(currentfont,detail)
487    local tempname = makename(tempname)
488    if tempname and tempname ~= "" then
489        local data = io.loaddata(file.join(temppath,file.replacesuffix(tempname,"tex"))) or "no source yet"
490        return tex_template(currentfont,data)
491    else
492        return "no source file"
493    end
494end
495
496local function show_log(currentfont,detail)
497    local tempname = makename(tempname)
498    if tempname and tempname ~= "" then
499        local data = io.loaddata(file.join(temppath,file.replacesuffix(tempname,'log'))) or "no log file yet"
500        data = gsub(data,"[%s%%]*begin of optionfile.-end of optionfile[%s%%]*","\n")
501        return tex_template(currentfont,data)
502    else
503        return "no log file"
504    end
505end
506
507-- much nicer would be an lmx template
508
509local function show_font(currentfont,detail)
510    local specification = get_specification(currentfont)
511    local features = fonts.helpers.getfeatures(specification.filename)
512    local result = { }
513    result[#result+1] = formatters["<h1>Font: %s</h1><br/>"](currentfont)
514    result[#result+1] = "<table>"
515    result[#result+1] = formatters["<tr><td class='tc' style='width:10em'>fontname   </td><td style='width:20em'>%s</td>"]     (specification.fontname   or "-")
516    result[#result+1] = formatters["    <td class='tc' style='width:10em'>designsize </td><td style='width:20em'>%s</td></tr>"](specification.designsize or "-")
517    result[#result+1] = formatters["<tr><td class='tc' style='width:10em'>fullname   </td><td style='width:20em'>%s</td>"]     (specification.fullname   or "-")
518    result[#result+1] = formatters["    <td class='tc' style='width:10em'>minimumsize</td><td style='width:20em'>%s</td></tr>"](specification.minsize    or "-")
519    result[#result+1] = formatters["<tr><td class='tc' style='width:10em'>filename   </td><td style='width:20em'>%s</td>"]     (specification.fontfile   or "-")
520    result[#result+1] = formatters["    <td class='tc' style='width:10em'>maximumsize</td><td style='width:20em'>%s</td></tr>"](specification.maxsize    or "-")
521    result[#result+1] = formatters["<tr><td class='tc' style='width:10em'>rawname    </td><td style='width:20em'>%s</td>"]     (specification.rawname    or "-")
522    result[#result+1] = formatters["    <td class='tc' style='width:10em'>fontweight </td><td style='width:20em'>%s</td></tr>"](specification.fontweight or "-")
523    result[#result+1] = formatters["<tr><td class='tc' style='width:10em'>familyname </td><td style='width:20em'>%s</td>"]     (specification.familyname or "-")
524    result[#result+1] = formatters["    <td class='tc' style='width:10em'>angle      </td><td style='width:20em'>%s</td></tr>"](specification.angle      or "-")
525    result[#result+1] = formatters["<tr><td class='tc' style='width:10em'>subfamily  </td><td style='width:20em'>%s</td>"]     (specification.subfamily  or "-")
526    result[#result+1] = formatters["    <td class='tc' style='width:10em'>variant    </td><td style='width:20em'>%s</td></tr>"](specification.variant    ~= "" and specification.variant   or "normal")
527    result[#result+1] = formatters["<tr><td class='tc' style='width:10em'>format     </td><td style='width:20em'>%s</td>"]     (specification.format     or "-")
528    result[#result+1] = formatters["    <td class='tc' style='width:10em'>style      </td><td style='width:20em'>%s</td></tr>"](specification.style      ~= "" and specification.style     or "normal")
529    result[#result+1] = formatters["<tr><td class='tc' style='width:10em'>pfmwidth   </td><td style='width:20em'>%s</td>"]     (specification.pfmwidth   ~= "" and specification.pfmwidth  or "-")
530    result[#result+1] = formatters["    <td class='tc' style='width:10em'>weight     </td><td style='width:20em'>%s</td></tr>"](specification.weight     ~= "" and specification.weight    or "normal")
531    result[#result+1] = formatters["<tr><td class='tc' style='width:10em'>pfmheight  </td><td style='width:20em'>%s</td>"]     (specification.pfmweight  ~= "" and specification.pfmweight or "-")
532    result[#result+1] = formatters["    <td class='tc' style='width:10em'>width      </td><td style='width:20em'>%s</td></tr>"](specification.width      ~= "" and specification.width     or "normal")
533    result[#result+1] = "</table>"
534    if features then
535        for what, v in table.sortedhash(features) do
536            local data = features[what]
537            if data and next(data) then
538                result[#result+1] = formatters["<h1>%s features</h1><br/>"](what)
539                result[#result+1] = "<table>"
540                result[#result+1] = "<tr><th>feature</th><th>tag&nbsp;</th><th>script&nbsp;</th><th>languages&nbsp;</th></tr>"
541                for f,ff in table.sortedhash(data) do
542                    local done = false
543                    for s, ss in table.sortedhash(ff) do
544                        local s =  s == "*" and "all" or s
545                        if ss  ["*"] then
546                            ss["*"] = nil
547                            ss.all  = true
548                        end
549                        local name
550                        if done then
551                            name = ""
552                        else
553                            done = true
554                            name = f
555                        end
556                        local title = fonts.handlers.otf.tables.features[name] or ""
557                        result[#result+1] = formatters["<tr><td width='50%%'>%s&nbsp;&nbsp;</td><td><tt>%s&nbsp;&nbsp;</tt></td><td><tt>%s&nbsp;&nbsp;</tt></td><td><tt>%s&nbsp;&nbsp;</tt></td></tr>"](title,name,s,concat(table.sortedkeys(ss)," "))
558                    end
559                end
560                result[#result+1] = "</table>"
561            end
562        end
563    else
564        result[#result+1] = "<br/><br/>This font has no features."
565    end
566    return concat(result,"\n")
567end
568
569local info_template = formatters [ [[
570<pre><tt>
571version   : %s
572comment   : %s
573author    : %s
574copyright : %s
575
576maillist  : ntg-context at ntg.nl
577webpage   : www.pragma-ade.nl
578wiki      : contextgarden.net
579</tt></pre>
580]] ]
581
582local function info_about()
583    local m = modules ['mtx-server-ctx-fonttest']
584    return info_template(m.version,m.comment,m.author,m.copyright)
585end
586
587local save_template = formatters [ [[
588    the current setup has been saved:
589    <br/> <br/>
590    <table>
591    <tr><td class='tc'>name&nbsp;      </td><td>%s</td></tr>
592    <tr><td class='tc'>title&nbsp;     </td><td>%s</td></tr>
593    <tr><td class='tc'>font&nbsp;      </td><td>%s</td></tr>
594    <tr><td class='tc'>script&nbsp;    </td><td>%s</td></tr>
595    <tr><td class='tc'>language&nbsp;  </td><td>%s</td></tr>
596    <tr><td class='tc'>features&nbsp;  </td><td>%s</td></tr>
597    <tr><td class='tc'>options&nbsp;   </td><td>%s</td></tr>
598    <tr><td class='tc'>sampletext&nbsp;</td><td>%s</td></tr>
599    </table>
600]] ]
601
602local function loadbase()
603    local datafile = file.join(basepath,basename)
604    local storage = io.loaddata(datafile) or ""
605    if storage == "" then
606        storage = { }
607    else
608        report("loading '%s'",datafile)
609        storage = loadstring(storage)
610        storage = (storage and storage()) or { }
611    end
612    return storage
613end
614
615local function loadstored(detail,currentfont,name)
616    local storage = loadbase()
617    storage = storage and storage[name]
618    if storage then
619        currentfont = storage.font
620        detail.script = storage.script or detail.script
621        detail.language = storage.language or detail.language
622        detail.title = storage.title or detail.title
623        detail.sampletext = storage.text or detail.sampletext
624        detail.name = name or "no name"
625        for k,v in next, storage.features do
626            detail["f-"..k] = v
627        end
628        for k,v in next, storage.options do
629            detail["o-"..k] = v
630        end
631    end
632    detail.loadname = nil
633    return detail, currentfont
634end
635
636local function savebase(storage,name)
637    local datafile = file.join(basepath,basename)
638    report("saving '%s' in '%s'",name or "data",datafile)
639    io.savedata(datafile,table.serialize(storage,true))
640end
641
642local function deletestored(detail,currentfont,name)
643    local storage = loadbase()
644    if storage and name and storage[name] then
645        report("deleting '%s' from base",name)
646        storage[name] = nil
647        savebase(storage)
648    end
649    detail.deletename = nil
650    return detail, ""
651end
652
653local function save_font(currentfont,detail)
654    local specification = get_specification(currentfont)
655    local name, title, script, language, features, options, text = currentfont, "", "dflt", "dflt", { }, { }, ""
656    if detail then
657        local htmldata = showfeatures(specification.filename)
658        script = detail.script or script
659        language = detail.language or language
660        text = string.strip(detail.sampletext or text)
661        name = string.strip(detail.name or name)
662        title = string.strip(detail.title or title)
663        for k,v in next, htmldata.features do
664            if detail["f-"..k] then features[k] = true end
665        end
666        for k=1,#what_options do
667            local v = what_options[k]
668            if detail["o-"..v] then options[k] = true end
669        end
670    end
671    if name == "" then
672        name = "no name"
673    end
674    local storage = loadbase()
675    storage[name] = {
676        font = currentfont, title = title, script = script, language = language, features = features, options = options, text = text,
677    }
678    savebase(storage,name)
679    return save_template(name,title,currentfont,script,language,concat(table.sortedkeys(features)," "),concat(table.sortedkeys(options)," "),text)
680end
681
682local function load_font(currentfont)
683    local datafile = file.join(basepath,basename)
684    local storage = loadbase(datafile)
685    local result = {}
686    result[#result+1] = "<tr><th>del&nbsp;</th><th>name&nbsp;</th><th>font&nbsp;</th><th>fontname&nbsp;</th><th>script&nbsp;</th><th>language&nbsp;</th><th>features&nbsp;</th><th>title&nbsp;</th><th>sampletext&nbsp;</th></tr>"
687    for k,v in table.sortedhash(storage) do
688        local fontname, fontfile = get_specification(v.font)
689        result[#result+1] = formatters["<tr><td><a href='mtx-server-ctx-fonttest.lua?deletename=%s'>x</a>&nbsp;</td><td><a href='mtx-server-ctx-fonttest.lua?loadname=%s'>%s</a>&nbsp;</td><td>%s&nbsp;</td<td>%s&nbsp;</td><td>%s&nbsp;</td><td>%s&nbsp;</td><td>%s&nbsp;</td><td>%s&nbsp;</td><td>%s&nbsp;</td></tr>"](
690            k,k,k,v.font,fontname,v.script,v.language,concat(table.sortedkeys(v.features)," "),v.title or "no title",v.text or "")
691    end
692    if #result == 1 then
693        return "nothing saved yet"
694    else
695        return formatters["<table>%s</table>"](concat(result,"\n"))
696    end
697end
698
699local function reset_font(currentfont)
700    return edit_font(currentfont)
701end
702
703local extras_template = formatters [ [[
704    <a href='mtx-server-ctx-fonttest.lua?extra=reload'>remake font database</a> (take some time)<br/><br/>
705]] ]
706
707local function do_extras(detail,currentfont,extra)
708    return extras_template()
709end
710
711local extras = { }
712
713local function do_extra(detail,currentfont,extra)
714    local e = extras[extra]
715    if e then e(detail,currentfont,extra) end
716    return do_extras(detail,currentfont,extra)
717end
718
719function extras.reload()
720    identifyfonts()
721    return do_extras()
722end
723
724local status_template = formatters [ [[
725    <input type="hidden" name="currentfont" value="%s" />
726]] ]
727
728local variables = {
729    ['color-background-one'] = lmx.get('color-background-green'),
730    ['color-background-two'] = lmx.get('color-background-blue'),
731    ['title']                = 'ConTeXt Font Tester',
732    ['formaction']           = "mtx-server-ctx-fonttest.lua",
733}
734
735-- todo: use lmx file
736
737function doit(configuration,filename,hashed)
738
739    local start = os.clock()
740
741    local detail = url.query(hashed.query or "")
742
743    local currentfont = detail.currentfont
744    local action      = detail.action
745    local selection   = detail.selection
746
747    if action == "luajittex" then
748        jitmode = true
749    elseif action == "luatex" then
750        jitmode = false
751    end
752
753    local loadname    = detail.loadname
754    local deletename  = detail.deletename
755    local extra       = detail.extra
756
757    if loadname and loadname ~= "" then
758        detail, currentfont = loadstored(detail,currentfont,loadname)
759        action = "process"
760    elseif deletename and deletename ~= "" then
761        detail, currentfont = deletestored(detail,currentfont,deletename)
762        action = "load"
763    elseif selection and selection ~= "" then
764        currentfont = selection
765    elseif extra and extra ~= "" then
766        do_extra(detail,currentfont,extra)
767        action = "extras"
768    end
769
770    local fontname, fontfile = get_specification(currentfont)
771
772    if fontfile then
773        variables.title = formatters['ConTeXt Font Tester: %s (%s)'](fontname,fontfile)
774    else
775        variables.title = 'ConTeXt Font Tester'
776    end
777
778    if jitmode then
779        variables.title = variables.title .. " (using LuaJit vm)"
780    end
781
782    -- lua table and adapt
783
784    report("action: %s",action or "no action")
785
786    local buttons = { 'process', 'select', 'save', 'load', 'edit', 'reset', 'features', 'source', 'log', 'info', 'extras', jitmode and "luatex" or "luajittex" }
787    local menu    = { }
788
789    for i=1,#buttons do
790        local button = buttons[i]
791        menu[#menu+1] = formatters["<button name='action' value='%s' type='submit'>%s</button>"](button,button)
792    end
793
794    variables.menu           = concat(menu,"&nbsp;")
795    variables.status         = status_template(currentfont or "")
796    variables.maintext       = ""
797    variables.javascriptdata = ""
798    variables.javascripts    = ""
799    variables.javascriptinit = ""
800
801    local result
802
803    if action == "select" then
804        variables.maintext = select_font()
805    elseif action == "info" then
806        variables.maintext = info_about()
807    elseif action == "extras" then
808        variables.maintext = do_extras()
809    elseif currentfont and currentfont ~= "" then
810        if action == "save" then
811            variables.maintext = save_font(currentfont,detail)
812        elseif action == "load" then
813            variables.maintext = load_font(currentfont,detail)
814        elseif action == "source" then
815            variables.maintext = show_source(currentfont,detail)
816        elseif action == "log" then
817            variables.maintext = show_log(currentfont,detail)
818        elseif action == "features" then
819            variables.maintext = show_font(currentfont,detail)
820        else
821            local e, s
822            if action == "process" then
823                e, s = process_font(currentfont,detail)
824            elseif action == "reset" then
825                e, s = reset_font(currentfont)
826            elseif action == "edit" then
827                e, s = edit_font(currentfont,detail)
828            else
829                e, s = process_font(currentfont,detail)
830            end
831            variables.maintext       = e
832            variables.javascriptdata = s
833            variables.javascripts    = javascripts
834            variables.javascriptinit = "check_form()"
835        end
836    else
837        variables.maintext = select_font()
838    end
839
840    result = { content = lmx.convert('context-fonttest.lmx',false,variables) }
841
842    report("time spent on page: %0.03f seconds",os.clock()-start)
843
844    return result
845
846end
847
848return doit, true
849
850--~ make_lmx_page("test")
851