mtx-server-ctx-fonttest.lua /size: 31 Kb    last modification: 2020-07-01 14:35
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                        if s == "*"  then s       = "all" end
545                        if ss  ["*"] then ss["*"] = nil ss.all = true end
546                        if done then
547                            f = ""
548                        else
549                            done = true
550                        end
551                        local title = fonts.handlers.otf.tables.features[f] or ""
552                        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,f,s,concat(table.sortedkeys(ss)," "))
553                    end
554                end
555                result[#result+1] = "</table>"
556            end
557        end
558    else
559        result[#result+1] = "<br/><br/>This font has no features."
560    end
561    return concat(result,"\n")
562end
563
564local info_template = formatters [ [[
565<pre><tt>
566version   : %s
567comment   : %s
568author    : %s
569copyright : %s
570
571maillist  : ntg-context at ntg.nl
572webpage   : www.pragma-ade.nl
573wiki      : contextgarden.net
574</tt></pre>
575]] ]
576
577local function info_about()
578    local m = modules ['mtx-server-ctx-fonttest']
579    return info_template(m.version,m.comment,m.author,m.copyright)
580end
581
582local save_template = formatters [ [[
583    the current setup has been saved:
584    <br/> <br/>
585    <table>
586    <tr><td class='tc'>name&nbsp;      </td><td>%s</td></tr>
587    <tr><td class='tc'>title&nbsp;     </td><td>%s</td></tr>
588    <tr><td class='tc'>font&nbsp;      </td><td>%s</td></tr>
589    <tr><td class='tc'>script&nbsp;    </td><td>%s</td></tr>
590    <tr><td class='tc'>language&nbsp;  </td><td>%s</td></tr>
591    <tr><td class='tc'>features&nbsp;  </td><td>%s</td></tr>
592    <tr><td class='tc'>options&nbsp;   </td><td>%s</td></tr>
593    <tr><td class='tc'>sampletext&nbsp;</td><td>%s</td></tr>
594    </table>
595]] ]
596
597local function loadbase()
598    local datafile = file.join(basepath,basename)
599    local storage = io.loaddata(datafile) or ""
600    if storage == "" then
601        storage = { }
602    else
603        report("loading '%s'",datafile)
604        storage = loadstring(storage)
605        storage = (storage and storage()) or { }
606    end
607    return storage
608end
609
610local function loadstored(detail,currentfont,name)
611    local storage = loadbase()
612    storage = storage and storage[name]
613    if storage then
614        currentfont = storage.font
615        detail.script = storage.script or detail.script
616        detail.language = storage.language or detail.language
617        detail.title = storage.title or detail.title
618        detail.sampletext = storage.text or detail.sampletext
619        detail.name = name or "no name"
620        for k,v in next, storage.features do
621            detail["f-"..k] = v
622        end
623        for k,v in next, storage.options do
624            detail["o-"..k] = v
625        end
626    end
627    detail.loadname = nil
628    return detail, currentfont
629end
630
631local function savebase(storage,name)
632    local datafile = file.join(basepath,basename)
633    report("saving '%s' in '%s'",name or "data",datafile)
634    io.savedata(datafile,table.serialize(storage,true))
635end
636
637local function deletestored(detail,currentfont,name)
638    local storage = loadbase()
639    if storage and name and storage[name] then
640        report("deleting '%s' from base",name)
641        storage[name] = nil
642        savebase(storage)
643    end
644    detail.deletename = nil
645    return detail, ""
646end
647
648local function save_font(currentfont,detail)
649    local specification = get_specification(currentfont)
650    local name, title, script, language, features, options, text = currentfont, "", "dflt", "dflt", { }, { }, ""
651    if detail then
652        local htmldata = showfeatures(specification.filename)
653        script = detail.script or script
654        language = detail.language or language
655        text = string.strip(detail.sampletext or text)
656        name = string.strip(detail.name or name)
657        title = string.strip(detail.title or title)
658        for k,v in next, htmldata.features do
659            if detail["f-"..k] then features[k] = true end
660        end
661        for k=1,#what_options do
662            local v = what_options[k]
663            if detail["o-"..v] then options[k] = true end
664        end
665    end
666    if name == "" then
667        name = "no name"
668    end
669    local storage = loadbase()
670    storage[name] = {
671        font = currentfont, title = title, script = script, language = language, features = features, options = options, text = text,
672    }
673    savebase(storage,name)
674    return save_template(name,title,currentfont,script,language,concat(table.sortedkeys(features)," "),concat(table.sortedkeys(options)," "),text)
675end
676
677local function load_font(currentfont)
678    local datafile = file.join(basepath,basename)
679    local storage = loadbase(datafile)
680    local result = {}
681    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>"
682    for k,v in table.sortedhash(storage) do
683        local fontname, fontfile = get_specification(v.font)
684        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>"](
685            k,k,k,v.font,fontname,v.script,v.language,concat(table.sortedkeys(v.features)," "),v.title or "no title",v.text or "")
686    end
687    if #result == 1 then
688        return "nothing saved yet"
689    else
690        return formatters["<table>%s</table>"](concat(result,"\n"))
691    end
692end
693
694local function reset_font(currentfont)
695    return edit_font(currentfont)
696end
697
698local extras_template = formatters [ [[
699    <a href='mtx-server-ctx-fonttest.lua?extra=reload'>remake font database</a> (take some time)<br/><br/>
700]] ]
701
702local function do_extras(detail,currentfont,extra)
703    return extras_template()
704end
705
706local extras = { }
707
708local function do_extra(detail,currentfont,extra)
709    local e = extras[extra]
710    if e then e(detail,currentfont,extra) end
711    return do_extras(detail,currentfont,extra)
712end
713
714function extras.reload()
715    identifyfonts()
716    return do_extras()
717end
718
719local status_template = formatters [ [[
720    <input type="hidden" name="currentfont" value="%s" />
721]] ]
722
723local variables = {
724    ['color-background-one'] = lmx.get('color-background-green'),
725    ['color-background-two'] = lmx.get('color-background-blue'),
726    ['title']                = 'ConTeXt Font Tester',
727    ['formaction']           = "mtx-server-ctx-fonttest.lua",
728}
729
730-- todo: use lmx file
731
732function doit(configuration,filename,hashed)
733
734    local start = os.clock()
735
736    local detail = url.query(hashed.query or "")
737
738    local currentfont = detail.currentfont
739    local action      = detail.action
740    local selection   = detail.selection
741
742    if action == "luajittex" then
743        jitmode = true
744    elseif action == "luatex" then
745        jitmode = false
746    end
747
748    local loadname    = detail.loadname
749    local deletename  = detail.deletename
750    local extra       = detail.extra
751
752    if loadname and loadname ~= "" then
753        detail, currentfont = loadstored(detail,currentfont,loadname)
754        action = "process"
755    elseif deletename and deletename ~= "" then
756        detail, currentfont = deletestored(detail,currentfont,deletename)
757        action = "load"
758    elseif selection and selection ~= "" then
759        currentfont = selection
760    elseif extra and extra ~= "" then
761        do_extra(detail,currentfont,extra)
762        action = "extras"
763    end
764
765    local fontname, fontfile = get_specification(currentfont)
766
767    if fontfile then
768        variables.title = formatters['ConTeXt Font Tester: %s (%s)'](fontname,fontfile)
769    else
770        variables.title = 'ConTeXt Font Tester'
771    end
772
773    if jitmode then
774        variables.title = variables.title .. " (using LuaJit vm)"
775    end
776
777    -- lua table and adapt
778
779    report("action: %s",action or "no action")
780
781    local buttons = { 'process', 'select', 'save', 'load', 'edit', 'reset', 'features', 'source', 'log', 'info', 'extras', jitmode and "luatex" or "luajittex" }
782    local menu    = { }
783
784    for i=1,#buttons do
785        local button = buttons[i]
786        menu[#menu+1] = formatters["<button name='action' value='%s' type='submit'>%s</button>"](button,button)
787    end
788
789    variables.menu           = concat(menu,"&nbsp;")
790    variables.status         = status_template(currentfont or "")
791    variables.maintext       = ""
792    variables.javascriptdata = ""
793    variables.javascripts    = ""
794    variables.javascriptinit = ""
795
796    local result
797
798    if action == "select" then
799        variables.maintext = select_font()
800    elseif action == "info" then
801        variables.maintext = info_about()
802    elseif action == "extras" then
803        variables.maintext = do_extras()
804    elseif currentfont and currentfont ~= "" then
805        if action == "save" then
806            variables.maintext = save_font(currentfont,detail)
807        elseif action == "load" then
808            variables.maintext = load_font(currentfont,detail)
809        elseif action == "source" then
810            variables.maintext = show_source(currentfont,detail)
811        elseif action == "log" then
812            variables.maintext = show_log(currentfont,detail)
813        elseif action == "features" then
814            variables.maintext = show_font(currentfont,detail)
815        else
816            local e, s
817            if action == "process" then
818                e, s = process_font(currentfont,detail)
819            elseif action == "reset" then
820                e, s = reset_font(currentfont)
821            elseif action == "edit" then
822                e, s = edit_font(currentfont,detail)
823            else
824                e, s = process_font(currentfont,detail)
825            end
826            variables.maintext       = e
827            variables.javascriptdata = s
828            variables.javascripts    = javascripts
829            variables.javascriptinit = "check_form()"
830        end
831    else
832        variables.maintext = select_font()
833    end
834
835    result = { content = lmx.convert('context-fonttest.lmx',false,variables) }
836
837    report("time spent on page: %0.03f seconds",os.clock()-start)
838
839    return result
840
841end
842
843return doit, true
844
845--~ make_lmx_page("test")
846